How to create Flappy Bird (Part 4) – Create pipes

บทนำ (Overview)

จากคราวที่แล้ว เราสามารถบังคับให้ตัวนกสามารถบินโฉบขึ้นได้ (How to create Flappy Bird (Part 3) – Control the bird) มาในบทความนี้เราจะมาสร้างท่อให้นกสามารถข้ามผ่านกันได้ โดยหลักการคือท่อจะเกิดจากขอบจอด้านขวาและเลื่อนไปสิ้นสุดขอบจอด้านซ้ายflappy18

ขั้นตอน (Steps)

  1. โหลดรูป “pipe” (assets/pipe.png) pipe
        preload: function() { 
    
           game.stage.backgroundColor = '#71c5cf';
           game.load.image('bird', 'assets/bird.png');
           //โหลดรูป pipe.png
           game.load.image('pipe', 'assets/pipe.png');   
    
        }
    
  2. สร้าง “group” เพื่อเก็บ “pipe”
        create: function() { 
    
           this.bird = this.game.add.sprite(100, 245, 'bird');
    
           game.physics.startSystem(Phaser.Physics.ARCADE);
           game.physics.arcade.enable(this.bird);
           this.bird.body.gravity.y = 1000;
    
           var spaceKey = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
           spaceKey.onDown.add(this.jump, this); 
    
           this.pipes = game.add.group(); //สร้าง gorup
           this.pipes.enableBody = true;  //เพิ่ม physics ให้กับ group
    
           //createMultiple(quantity, key, frame, exists)
           this.pipes.createMultiple(20, 'pipe'); //สร้าง pide 20 อัน 
    
        }
    
  3. “createMultiple” ถูกใช้สำหรับสร้าง “Phaser.Sprite” หลาย ๆ “objects” โดยปกติเมื่อสร้าง “object” ต่าง ๆ ตำแหน่ง (0,0)
    พารามิเตอร์ ปรเภทข้อมูล จำเป็น ค่าเริ่มต้น อธิบายเพิ่มเติม
    quantity integer จำนวนของ Sprites ที่สร้าง
    key string ชื่ออ้างอิง
    frame integer | string <optional> If the Sprite image contains multiple frames you can specify which one to use here.
    exists boolean <optional> false The default exists state of the Sprite.
  4. ตอนนี้ “pipe” ที่อยู่ใน “group” ยังไม่ถูกแสดงออกมา ลองทดสอบเรียก “pipe” ออกมา จาก “group” ตาม “code” ข้างล่าง แต่อย่างไรก็ตามพบว่าเราสามารถดึง “pipe” ออกมาได้ไม่เกินตามจำนวนที่เรากำหนดคือ 20 อัน
           // ดึง pipe แรกออกมาจาก group
           var pipe = this.pipes.getFirstDead();
    
           // แสดง pipe ตามตำแหน่ง (x,y)
           pipe.reset(200, 10);
    
           // ดึง pipe ถัดไปออกมาจาก group
           var pipe = this.pipes.getFirstDead();
    
            // แสดง pipe ตามตำแหน่ง (x,y)
           pipe.reset(200, 70);
    

    flappy5

  5. จากนั้นทำให้ “pipe” เลื่อนไปด้านซ้าย ทำให้เปรียบเสมือน “bird” กำลังบินไปด้านขวา
           //ทำให้ pipe เคลื่อนที่ไปด้านซ้าย
           pipe.body.velocity.x = -200;
    

    flappy6

  6. จากรูปข้างต้นจะสังเกตุเห็นว่า “pipe” สุดท้ายจะเคลื่อนที่ไปด้านซ้าย แต่ “pipe” แรกกลับไม่เคลื่อนที่ เพราะมาจากสาเหตุที่ เราต้องกำหนด “pipe.body.velocity.x” ให้กับ “object” ที่สร้างออกมาด้วย
  7. ดังนั้นเราสร้างมาสร้างเป็นฟังก์ชันสำหรับตั้งค่าให้กับ 1 “pipe” แล้วลองทดสอบเรียก 2 ครั้งซึ่งจะได้ 2 “pipe” ดังนี้
        addShotPipe: function(x, y) {  
    
           var pipe = this.pipes.getFirstDead();
           pipe.reset(x, y);
           pipe.body.velocity.x = -200; 
    
        },
    

    ทดลองเรียกใช้

           this.addShotPipe(200,10);
           this.addShotPipe(200,70);
    

    flappy7

  8. เราจะสร้าง pipe ให้มีช่อง เพื่อให้ bird เรารอดไปได้ ดังนี้ใน 1 แท่นเสาจะมี pipe ทั้งหมด 6 อัน และเป็นช่องว่าง 2 อัน เพื่อให้นกรอด เราทดสอบสร้างเสา 8 อันก่อนโดยการสร้าง loop เรียกฟังก์ชัน this.addShotPipe(x,y);  แทนการเขียนเรียกฟังก์ชันซ้ำ ๆ 8 ครั้ง
           for (var i = 0; i < 8; i++){
           	  this.addShotPipe(400, (i * 60) + 10);
           }
    

    flappy8

  9. จากนั้นลองปรับเปลี่ยนโดยให้มีช่องว่างให้นกผ่าน โดยใช้การสุ่มตำแหน่งและ + ไปอีกหนึ่งช่อง
         //สุ่มตำแหน่ง
         var blank = Math.floor(Math.random() * 5) + 1;
           //สร้าง pipe 8 อัน
           for (var i = 0; i < 8; i++){
           	   //ตำแหน่งที่สุ่มและบวกหนึ่งจะไม่แสดง pipe
               if (i != blank && i != blank + 1){
           		//แสดง pipe
                    this.addShotPipe(400, (i * 60) + 10);
           	   }
           }
    

    flappy9

  10. แต่จากการเรียกใช้งานพบว่าจะสร้างเพียงแถวเดียว แล้วไม่สร้างเพิ่มอีก เพราะฉนั้นจะสร้างให้เป็นฟังก์ชันและเรียกใช้ตาม “loop” ที่เราตั้งเวลาสัก 1.6 วินาที เพื่อให้ท่อ ๆ ค่อยปล่อยออกมา
        addLongPipes: function() {        
    
         var blank = Math.floor(Math.random() * 5) + 1;
           for (var i = 0; i < 8; i++){
           	if (i != blank && i != blank + 1){
           		this.addShotPipe(400, (i * 60) + 10);
           	}
           }
        }
    
       create: function() { 
    
           this.bird = this.game.add.sprite(100, 245, 'bird');
    
           game.physics.startSystem(Phaser.Physics.ARCADE);
           game.physics.arcade.enable(this.bird);
           this.bird.body.gravity.y = 1000;
    
           var spaceKey = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
           spaceKey.onDown.add(this.jump, this); 
    
           this.pipes = game.add.group();
           this.pipes.enableBody = true;
           this.pipes.createMultiple(20, 'pipe'); 
    
           //หน่วงเวลาไว้ 1.6 วินาที
           this.timer = game.time.events.loop(1600, this.addLongPipes, this);
        }
    
  11. แต่อย่างไรก็ตามเมื่อทดสอบเราก็พบว่าผ่านไปถึงท่อที่ 3 เกมส์ก็หยุดทันที เหตุเพราะใน group เรามีเพียง 20 pipe เพราะฉะนั้นเราจะทำให้ pipe สามารถตรวจสอบว่าถึงขอบของ game world หรือยังจากนั้นสั่ง kill เมื่อเลยขอบ game world ก็จะสามารถท่อใหม่ได้เรื่อย ๆ ดังนี้
       addShotPipe: function(x, y) {  
    
         var pipe = this.pipes.getFirstDead();
         pipe.reset(x, y);
         pipe.body.velocity.x = -200; 
    
         //ให้ pipe สามารถตรวจสอบว่าถึงขอบ game world หรือยัง
         pipe.checkWorldBounds = true;
         //เมื่อ pipe เลย game world สั่งลบเลย
         pipe.outOfBoundsKill = true;
    
       }
    
  12. สามารถดาวน์โหลด source-code ในบทความนี้ได้ที่ https://gist.github.com/thaisingle/b6a1acbd1ba9a400b3a0

ใส่ความเห็น