บทนำ (Overview)
จากคราวที่แล้ว เราสามารถบังคับให้ตัวนกสามารถบินโฉบขึ้นได้ (How to create Flappy Bird (Part 3) – Control the bird) มาในบทความนี้เราจะมาสร้างท่อให้นกสามารถข้ามผ่านกันได้ โดยหลักการคือท่อจะเกิดจากขอบจอด้านขวาและเลื่อนไปสิ้นสุดขอบจอด้านซ้าย
ขั้นตอน (Steps)
- โหลดรูป “pipe” (assets/pipe.png)
preload: function() { game.stage.backgroundColor = '#71c5cf'; game.load.image('bird', 'assets/bird.png'); //โหลดรูป pipe.png game.load.image('pipe', 'assets/pipe.png'); }
- สร้าง “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 อัน }
- “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. - ตอนนี้ “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);
- จากนั้นทำให้ “pipe” เลื่อนไปด้านซ้าย ทำให้เปรียบเสมือน “bird” กำลังบินไปด้านขวา
//ทำให้ pipe เคลื่อนที่ไปด้านซ้าย pipe.body.velocity.x = -200;
- จากรูปข้างต้นจะสังเกตุเห็นว่า “pipe” สุดท้ายจะเคลื่อนที่ไปด้านซ้าย แต่ “pipe” แรกกลับไม่เคลื่อนที่ เพราะมาจากสาเหตุที่ เราต้องกำหนด “pipe.body.velocity.x” ให้กับ “object” ที่สร้างออกมาด้วย
- ดังนั้นเราสร้างมาสร้างเป็นฟังก์ชันสำหรับตั้งค่าให้กับ 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);
- เราจะสร้าง 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); }
- จากนั้นลองปรับเปลี่ยนโดยให้มีช่องว่างให้นกผ่าน โดยใช้การสุ่มตำแหน่งและ + ไปอีกหนึ่งช่อง
//สุ่มตำแหน่ง 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); } }
- แต่จากการเรียกใช้งานพบว่าจะสร้างเพียงแถวเดียว แล้วไม่สร้างเพิ่มอีก เพราะฉนั้นจะสร้างให้เป็นฟังก์ชันและเรียกใช้ตาม “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); }
- แต่อย่างไรก็ตามเมื่อทดสอบเราก็พบว่าผ่านไปถึงท่อที่ 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; }
- สามารถดาวน์โหลด source-code ในบทความนี้ได้ที่ https://gist.github.com/thaisingle/b6a1acbd1ba9a400b3a0