Headlines

สร้างเกม “บินเก็บเหรียญ” ด้วย Phaser จาก Microsoft Copilot

ในยุคปัจจุบัน AI ได้กลายเป็นส่วนสำคัญในการพัฒนาซอฟต์แวร์และเกมต่างๆ การสร้างเกม “บินเก็บเหรียญ” ด้วย Phaser จากการช่วยเหลือจาก AI ไม่เพียงแต่ช่วยลดระยะเวลาการพัฒนา แต่ยังเพิ่มความคิดสร้างสรรค์และนวัตกรรมให้กับเกมอีกด้วย

วันนี้เราลองมาดูฝั่ง Microsoft Copilot กันบ้าง เราจะมาลองใช้เจ้า Copilot สร้างเกม “บินเก็บเหรียญ” กัน

เริ่มแรกให้เราเข้าไปที่ https://copilot.microsoft.com จากนั้นเราก็เริ่ม Prompt กันได้เลย

ขั้นตอนที่ 1 พิมพ์ Prompt “สอน flappy bird ด้วย phaser หน่อยสิ ฉันยังไม่เคยเรียนมาก่อน” กด Enter จากนั้น Copilot จะอธิบายสิ่งต่างๆ ให้เราดังนี้


ขั้นแรก เราต้องเตรียมสิ่งที่จำเป็น:

  1. ติดตั้ง Node.js (https://nodejs.org) และ npm (ถ้ายังไม่มี)
  2. ติดตั้ง Visual Studio Code (หรือโปรแกรมแก้ไขโค้ดที่คุณชื่นชอบ)
  3. สร้างโฟลเดอร์ใหม่สำหรับโปรเจกต์ของคุณ

ต่อไปนี้คือขั้นตอนเบื้องต้นในการสร้าง Flappy Bird ด้วย Phaser:

1. สร้างโครงสร้างโปรเจกต์

ในโฟลเดอร์โปรเจกต์ของคุณ ให้สร้างไฟล์ index.htmlmain.js, และโฟลเดอร์ assets สำหรับเก็บรูปภาพและเสียง

2. เพิ่ม HTML และการตั้งค่า Phaser

ในไฟล์ index.html ของคุณ:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flappy Bird</title>
    <script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.js"></script>
    <script src="main.js" defer></script>
</head>
<body>
    <h1>Flappy Bird with Phaser</h1>
    <div id="game"></div>
</body>
</html>

3. เริ่มต้นโค้ด JavaScript

ในไฟล์ main.js ของคุณ:

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

const game = new Phaser.Game(config);

function preload() {
    this.load.image('bird', 'assets/bird.png');
    this.load.image('pipe', 'assets/pipe.png');
}

function create() {
    this.bird = this.physics.add.sprite(100, 450, 'bird');
    this.bird.setCollideWorldBounds(true);
    this.bird.setGravityY(200);
    this.input.on('pointerdown', flap, this);
}

function update() {
    if (this.bird.y > 600 || this.bird.y < 0) {
        resetBird.call(this);
    }
}

function flap() {
    this.bird.setVelocityY(-350);
}

function resetBird() {
    this.bird.y = 450;
    this.bird.setVelocity(0, 0);
}

4. เพิ่มแอสเซ็ท

ให้ใส่ไฟล์รูปภาพของนก (bird.png) และท่อ (pipe.png) ลงในโฟลเดอร์ assets ที่คุณสร้างขึ้น

นั่นแหละ! เมื่อคุณเปิดไฟล์ index.html ในเบราว์เซอร์ คุณจะเห็นนกที่สามารถกระพือปีกได้เมื่อต้องการ นี่เป็นเพียงขั้นตอนพื้นฐานเท่านั้น คุณสามารถเพิ่มฟีเจอร์เพิ่มเติม เช่น ท่อที่ขยับ, คะแนน, และเสียง เพื่อทำให้เกมสมบูรณ์มากขึ้น


เราลองมาทดสอบกันดูจะเป็นอย่างไรบ้าง และนี่คือผลลัพธ์ที่ได้

ใช่แล้วผลลัพธ์ที่ได้ ท่อไม่มา เราเลยต้องมาเพิ่ม Prompt กันอีกสักหน่อย คราวนี้ลองเพิ่ม Prompt นี้ เข้าไป “ไม่มี pipe แสดง” และโค้ดใหม่ที่ได้คือ

function create() {
    this.bird = this.physics.add.sprite(100, 450, 'bird');
    this.bird.setCollideWorldBounds(true);
    this.bird.setGravityY(200);
    this.input.on('pointerdown', flap, this);

    // เพิ่มท่อ (pipe)
    this.pipe = this.physics.add.sprite(400, 300, 'pipe');
    this.pipe.setImmovable(true);
    this.pipe.setGravityY(0);
}

ลองดูผลลัพธ์ ท่อมาแล้ว แต่ มาแค่ 1 ท่อ ตกแล้วก็หาย ไม่ใช่อย่างที่เราต้องการ

เมื่อผลลัพธ์ที่ได้ยังไม่ตรงที่ต้องการ เลยต้องปรับ Prompt อีกที คราวนี้ใช้ Prompt นี้ “ท่อ ต้อง random ออกมาเรื่อยๆ” และได้โค้ดใหม่มาดังนี้

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

const game = new Phaser.Game(config);

function preload() {
    this.load.image('bird', 'assets/bird.png');
    this.load.image('pipe', 'assets/pipe.png');
}

function create() {
    this.bird = this.physics.add.sprite(100, 300, 'bird');
    this.bird.setCollideWorldBounds(true);
    this.bird.setGravityY(600);

    this.pipes = this.physics.add.group({
        key: 'pipe',
        repeat: 5,
        setXY: { x: 400, y: 0, stepX: 300 }
    });

    this.pipes.children.iterate(function(pipe) {
        pipe.setY(Phaser.Math.Between(50, 550));
        pipe.setVelocityX(-200);
        pipe.setImmovable(true);
    });

    this.time.addEvent({
        delay: 1500,
        callback: addPipe,
        callbackScope: this,
        loop: true
    });

    this.input.on('pointerdown', flap, this);
}

function update() {
    if (this.bird.y > 600 || this.bird.y < 0) {
        resetBird.call(this);
    }

    this.physics.world.collide(this.bird, this.pipes, hitPipe, null, this);
}

function flap() {
    this.bird.setVelocityY(-350);
}

function resetBird() {
    this.bird.y = 300;
    this.bird.setVelocity(0, 0);
}

function addPipe() {
    const pipe = this.pipes.create(800, Phaser.Math.Between(50, 550), 'pipe');
    pipe.setVelocityX(-200);
    pipe.setImmovable(true);

    pipe.checkWorldBounds = true;
    pipe.outOfBoundsKill = true;
}

function hitPipe(bird, pipe) {
    this.scene.restart();
}

ผลลัพธ์ที่ได้ครั้งนี้ โอเคใช่ตามที่ต้องการ

ต่อไปเรามาลองเพิ่มเงื่อนไขต่างๆ เข้าไปอีกสักหน่อย ให้ดูเป็นเกมขึ้น คราวนี้เราใช้ Prompt “
– เพิ่ม background
– เพิ่มเงื่อนไขให้ตกลงพื้นแล้วตาย
– เพิ่มเงื่อนไขชนท่อแล้วตาย
– เพิ่ม Coin เมื่อชน Coin ให้นับคะแนน +1 ไปเรื่อยๆ
– จัดเกม ให้อยู่ตรงกลางหน้าจอ

โค้ดใหม่ที่ได้

ไฟล์ index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flappy Bird</title>
    <script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.js"></script>
    <script src="main.js" defer></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
        #game { position: relative; }
        
        button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
        button:hover { background-color: #ddd; }
    </style>
</head>
<body>
    <div id="game"></div>

  
</body>
</html>

ไฟล์ main.js

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'game', // เพิ่ม parent เพื่อบอก Phaser ว่าให้เกมอยู่ใน div ที่มี id="game"
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

const game = new Phaser.Game(config);

// โค้ดอื่น ๆ 


function preload() {
    this.load.image('background', 'assets/background.png');
    this.load.image('bird', 'assets/bird.png');
    this.load.image('pipe', 'assets/pipe.png');
    this.load.image('coin', 'assets/coin.png'); // เพิ่มการโหลด coin
}

function create() {
    this.add.image(400, 300, 'background');

    this.bird = this.physics.add.sprite(100, 300, 'bird');
    this.bird.setCollideWorldBounds(true);
    this.bird.setGravityY(600);

    this.pipes = this.physics.add.group({
        immovable: true,
        allowGravity: false // ตั้งค่าให้ท่อไม่ตกลงพื้น
    });

    this.coins = this.physics.add.group({
        immovable: true,
        allowGravity: false // ตั้งค่าให้เหรียญไม่ตกลงพื้น
    });

    this.score = 0;
    this.scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });

    this.time.addEvent({
        delay: 1500,
        callback: addObstacle,
        callbackScope: this,
        loop: true
    });

    this.input.on('pointerdown', flap, this);
}

function update() {
    //console.log("this.bird.y: ", this.bird.y);
    if (this.bird.y >= 588 || this.bird.y <= 12) {
        hitGround.call(this);
    }

    this.physics.world.collide(this.bird, this.pipes, hitPipe, null, this);
    this.physics.world.overlap(this.bird, this.coins, collectCoin, null, this); // ตรวจจับการชนกับ coin
}


function addObstacle() {
    const pipeY = Phaser.Math.Between(150, 450); // ปรับความสูงของท่อให้เหมาะสม
    const pipe = this.pipes.create(800, pipeY, 'pipe');
    pipe.setVelocityX(-200);
    pipe.setImmovable(true);

    // เพิ่ม coin เหนือท่อเล็กน้อย
    const coin = this.coins.create(800, pipeY - 100, 'coin');
    coin.setVelocityX(-200);
    coin.setImmovable(true);
}


function collectCoin(bird, coin) {
    coin.disableBody(true, true); // ลบ coin ออกจากหน้าจอ
    this.score += 1; // เพิ่มคะแนน
    this.scoreText.setText('Score: ' + this.score); // อัปเดตข้อความคะแนน
}


function flap() {
    this.bird.setVelocityY(-350);
}

function resetBird() {
    this.bird.y = 300;
    this.bird.setVelocity(0, 0);
}

function addPipe() {
    const pipe = this.pipes.create(800, Phaser.Math.Between(50, 550), 'pipe');
    pipe.setVelocityX(-200);
    pipe.setImmovable(true);

    pipe.checkWorldBounds = true;
    pipe.outOfBoundsKill = true;
}
function hitPipe(bird, pipe) {
    endGame.call(this); // เรียกฟังก์ชันจบเกมเมื่อชนท่อ
}

function hitGround() {
    endGame.call(this); // เรียกฟังก์ชันจบเกมเมื่อชนพื้น
}

function endGame() {
    const score = this.score;
    this.physics.pause(); // หยุดฟิสิกส์ทั้งหมด
    document.getElementById('final-score').textContent = 'Your score: ' + score;
    document.getElementById('overlay').style.display = 'block';
    document.getElementById('popup').style.display = 'block';
}


function restartGame() {
    document.getElementById('overlay').style.display = 'none';
    document.getElementById('popup').style.display = 'none';
    location.reload();
}

ผลลัพธ์ที่ได้

การ Alert มันดูธรรมดาไปหน่อย ลองทำให้สวยขึ้นอีกนิด เลยพิมพ์ Prompt นี้ไป “สามารถปรับ alert ให้มี design ที่สวยงามได้หรือไม่” โค้ดใหม่ที่ได้

ไฟล์ index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flappy Bird</title>
    <script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.js"></script>
    <script src="main.js" defer></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
        #game { position: relative; }
        .popup {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            border: 2px solid #000;
            padding: 20px;
            z-index: 1000;
            text-align: center;
        }
        .popup h2 { margin: 0 0 10px; }
        .popup p { margin: 10px 0; }
        .overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 900;
        }
        button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
        button:hover { background-color: #ddd; }
    </style>
</head>
<body>
    <div id="game"></div>

    <!-- Popup สำหรับการแจ้งเตือน -->
    <div class="overlay" id="overlay"></div>
    <div class="popup" id="popup">
        <h2>Game Over!</h2>
        <p id="final-score">Your score: 0</p>
        <button onclick="restartGame()">Restart</button>
    </div>
</body>
</html>

ไฟล์ main.js

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'game', // เพิ่ม parent เพื่อบอก Phaser ว่าให้เกมอยู่ใน div ที่มี id="game"
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

const game = new Phaser.Game(config);

// โค้ดอื่น ๆ 


function preload() {
    this.load.image('background', 'assets/background.png');
    this.load.image('bird', 'assets/bird.png');
    this.load.image('pipe', 'assets/pipe.png');
    this.load.image('coin', 'assets/coin.png'); // เพิ่มการโหลด coin
}

function create() {
    this.add.image(400, 300, 'background');

    this.bird = this.physics.add.sprite(100, 300, 'bird');
    this.bird.setCollideWorldBounds(true);
    this.bird.setGravityY(600);

    this.pipes = this.physics.add.group({
        immovable: true,
        allowGravity: false // ตั้งค่าให้ท่อไม่ตกลงพื้น
    });

    this.coins = this.physics.add.group({
        immovable: true,
        allowGravity: false // ตั้งค่าให้เหรียญไม่ตกลงพื้น
    });

    this.score = 0;
    this.scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });

    this.time.addEvent({
        delay: 1500,
        callback: addObstacle,
        callbackScope: this,
        loop: true
    });

    this.input.on('pointerdown', flap, this);
}

function update() {
    //console.log("this.bird.y: ", this.bird.y);
    if (this.bird.y >= 588 || this.bird.y <= 12) {
        hitGround.call(this);
    }

    this.physics.world.collide(this.bird, this.pipes, hitPipe, null, this);
    this.physics.world.overlap(this.bird, this.coins, collectCoin, null, this); // ตรวจจับการชนกับ coin
}


function addObstacle() {
    const pipeY = Phaser.Math.Between(150, 450); // ปรับความสูงของท่อให้เหมาะสม
    const pipe = this.pipes.create(800, pipeY, 'pipe');
    pipe.setVelocityX(-200);
    pipe.setImmovable(true);

    // เพิ่ม coin เหนือท่อเล็กน้อย
    const coin = this.coins.create(800, pipeY - 100, 'coin');
    coin.setVelocityX(-200);
    coin.setImmovable(true);
}


function collectCoin(bird, coin) {
    coin.disableBody(true, true); // ลบ coin ออกจากหน้าจอ
    this.score += 1; // เพิ่มคะแนน
    this.scoreText.setText('Score: ' + this.score); // อัปเดตข้อความคะแนน
}


function flap() {
    this.bird.setVelocityY(-350);
}

function resetBird() {
    this.bird.y = 300;
    this.bird.setVelocity(0, 0);
}

function addPipe() {
    const pipe = this.pipes.create(800, Phaser.Math.Between(50, 550), 'pipe');
    pipe.setVelocityX(-200);
    pipe.setImmovable(true);

    pipe.checkWorldBounds = true;
    pipe.outOfBoundsKill = true;
}
function hitPipe(bird, pipe) {
    endGame.call(this); // เรียกฟังก์ชันจบเกมเมื่อชนท่อ
}

function hitGround() {
    endGame.call(this); // เรียกฟังก์ชันจบเกมเมื่อชนพื้น
}

function endGame() {
    const score = this.score;
    this.physics.pause(); // หยุดฟิสิกส์ทั้งหมด
    document.getElementById('final-score').textContent = 'Your score: ' + score;
    document.getElementById('overlay').style.display = 'block';
    document.getElementById('popup').style.display = 'block';
}


function restartGame() {
    document.getElementById('overlay').style.display = 'none';
    document.getElementById('popup').style.display = 'none';
    location.reload();
}

ผลลัพธ์ที่ได้ สวยขึ้นๆ

เพื่อให้เกม “บินเก็บเหรียญ” มีความสนุกสนานสมบูรณ์ขึ้น คุณสามารถเพิ่มองค์ประกอบต่างๆ เช่น เสียงเพลง, การประกาศผล, การเพิ่มด่านต่างๆ ตามจินตนาการของเราได้เลย

สุดท้ายนี้การใช้ AI ในการพัฒนาเกม “บินเก็บเหรียญ” ด้วย Phaser ช่วยให้การพัฒนาเป็นไปอย่างราบรื่นและสร้างสรรค์มากขึ้น AI ไม่เพียงแต่ช่วยในการเขียนโค้ด ยังช่วยเพิ่มความสามารถในการคิดและออกแบบเกมอีกด้วย แต่เราก็ต้องใช้ความสามารถในการคุยมากขึ้นเหมือนกันกว่าจะเข้าใจกัน ทั้งนี้ขอให้สนุกกับการพัฒนาเกมและใช้ AI ในการสร้างสรรค์สิ่งใหม่ๆ กันนะคะ!

Leave a Reply