Viết game flappy Javascript thuần túy

10 min read

Mục tiêu: Làm game flappy bird với Javascript thuần túy

Chuẩn bị:

Các hình ảnh dùng trong game:

Lấy base64 string từ trang https://www.base64-image.de/

Bắt đầu code

Viết 1 trang index.html thuần túy, trong đó cần chứa:

  • Canvas
  • Include game.js: là source code game chúng ta sẽ code bên trong
<!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>
    <style>
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #70c5ce;
        }

        #game-container {
            position: relative;
            width: 800px;
            height: 600px;
            overflow: hidden;
            border: 2px solid #000;
        }

        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <div id="game-container">
        <canvas id="gameCanvas"></canvas>
    </div>
    <script src="game.js"></script>
</body>
</html>

Game.js file

Javascript sử dụng Canvas để vẽ các hình ảnh lên đó.

Ví dụ: Để vẽ 1 hình chữ nhật, ta sẽ cần code như sau

const canvas = document.getElementById('gameCanvas');
const context = canvas.getContext('2d');

canvas.width = 800;
canvas.height = 600;

// Draw a rectangle
context.fillStyle = 'blue'; // Set the fill color
context.fillRect(100, 100, 200, 150); // Draw the rectangle (x, y, width, height)

Để vẽ nhân vật Chim, chúng ta sẽ load base64 string vào Image và hiển thị lên canvas

const canvas = document.getElementById('gameCanvas');
const context = canvas.getContext('2d');

canvas.width = 800;
canvas.height = 600;

// Base64 image of the Bird
const base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAYBAMAAABtiDI6AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAHlBMVEUAAABTOEZU0f/6+vpLwfjX5sz82ITkYBhApNL///8xUeTGAAAAAXRSTlMAQObYZgAAAAFiS0dECfHZpewAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfjAwwAAhPn+iW4AAAAbElEQVQY043R2w3AIAgFUFa4K7CCK7hCV2AFV2DsIthqfSS9X3qiBJHIAw8NmQVgZqSEs9g+Z4NOs9SS2eQyCFokTv+Qh04ithCv3V6yipEEFA92UltUVZQ+kY2oB++ljRDChsGu0gY//sRHbqCxPNXcDN8UAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTAzLTExVDE4OjAyOjE5KzA2OjAw4So5wwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wMy0xMVQxODowMjoxOSswNjowMJB3gX8AAAAASUVORK5CYII=';

// Create a new image
const image = new Image();
image.onload = function() {
    context.drawImage(image, 0, 0, canvas.width, canvas.height);
};
image.src = base64Image;

Để Chim có thể di chuyển, chúng ta cần Thay đổi tọa độ hiển thị của Chim theo Thời gian.
Đây là khái niệm “game loop”

Hãy viết 1 game loop đơn giản

function updateAndDraw() {    
}

function gameLoop() {
    updateAndDraw();
    requestAnimationFrame(gameLoop); // Call gameLoop again on the next frame
}

gameLoop();

Chúng ta cần Thay đổi tọa độ vẽ Chim trong hàm updateAndDraw

let x = 0;
let speed = 2;
function updateAndDraw() {
    x = x + speed;
    context.drawImage(image, x, 0, canvas.width, canvas.height);
}

Sử dụng document.addEventListener(‘keydown’, (event) => {}) để lắng nghe sự kiện phím “Space” được gõ

Gom tất cả các logic bên trên vào 1 file, gồm:

  • Load canvas
  • Hiển thị ảnh từ base64 string
  • Game loop & di chuyển nhân vật
  • Lắng nghe sự kiện từ bàn phím

Ta sẽ có 1 file html gồm source code như sau <code full để mn tham khảo>

<!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>
    <style>
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #70c5ce;
        }

        #game-container {
            position: relative;
            width: 800px;
            height: 600px;
            overflow: hidden;
            border: 2px solid #000;
        }

        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <div id="game-container">
        <canvas id="gameCanvas"></canvas>
    </div>
    <script>
        const canvas = document.getElementById('gameCanvas');
        const context = canvas.getContext('2d');

        canvas.width = 800;
        canvas.height = 600;

        // Base64 images
        const birdImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAYBAMAAABtiDI6AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAHlBMVEUAAABTOEZU0f/6+vpLwfjX5sz82ITkYBhApNL///8xUeTGAAAAAXRSTlMAQObYZgAAAAFiS0dECfHZpewAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfjAwwAAhPn+iW4AAAAbElEQVQY043R2w3AIAgFUFa4K7CCK7hCV2AFV2DsIthqfSS9X3qiBJHIAw8NmQVgZqSEs9g+Z4NOs9SS2eQyCFokTv+Qh04ithCv3V6yipEEFA92UltUVZQ+kY2oB++ljRDChsGu0gY//sRHbqCxPNXcDN8UAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTAzLTExVDE4OjAyOjE5KzA2OjAw4So5wwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wMy0xMVQxODowMjoxOSswNjowMJB3gX8AAAAASUVORK5CYII='; // Replace with actual base64 string
        const pipeImage = 'data:image/png;base64,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'; // Replace with actual base64 string
        const backgroundImage = 'data:image/png;base64,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'; // Replace with actual base64 string

        const bird = {
            x: 50,
            y: 150,
            width: 34, // Set the width of bird image
            height: 24, // Set the height of bird image
            gravity: 0.6,
            lift: -15,
            velocity: 0
        };

        const pipes = [];
        const pipeWidth = 52; // Set the width of pipe image
        const pipeGap = 150;
        const pipeFrequency = 90; // Frames
        let frameCount = 0;
        let score = 0;
        let gameOver = false;

        // Create image objects
        const birdImg = new Image();
        birdImg.src = birdImage;

        const pipeImg = new Image();
        pipeImg.src = pipeImage;

        const bgImg = new Image();
        bgImg.src = backgroundImage;

        function updateAndDraw() {
            // Update bird's position
            bird.velocity += bird.gravity;
            bird.y += bird.velocity;

            // Check for collision with the top and bottom of the canvas
            if (bird.y + bird.height > canvas.height || bird.y < 0) {
                gameOver = true;
            }

            // Update pipes
            if (frameCount % pipeFrequency === 0) {
                const top = Math.floor(Math.random() * (canvas.height - pipeGap));
                pipes.push({ x: canvas.width, top: top });
            }

            pipes.forEach(pipe => {
                pipe.x -= 2;
            });

            // Remove off-screen pipes and update score
            pipes.forEach(pipe => {
                if (pipe.x + pipeWidth < 0) {
                    pipes.shift();
                    score++;
                }
            });

            // Check for collisions with pipes
            pipes.forEach(pipe => {
                if (
                    bird.x < pipe.x + pipeWidth &&
                    bird.x + bird.width > pipe.x &&
                    (bird.y < pipe.top || bird.y + bird.height > pipe.top + pipeGap)
                ) {
                    gameOver = true;
                }
            });

            // Draw everything
            context.clearRect(0, 0, canvas.width, canvas.height);
            context.drawImage(bgImg, 0, 0, canvas.width, canvas.height);
            pipes.forEach(pipe => {
                context.drawImage(pipeImg, pipe.x, pipe.top - pipeImg.height); // Draw top pipe
                context.drawImage(pipeImg, pipe.x, pipe.top + pipeGap); // Draw bottom pipe
            });
            context.drawImage(birdImg, bird.x, bird.y, bird.width, bird.height);
            context.fillStyle = 'black';
            context.font = '20px Arial';
            context.fillText(`Score: ${score}`, 10, 20);

            if (gameOver) {
                context.fillStyle = 'black';
                context.font = '40px Arial';
                context.fillText('Game Over', canvas.width / 2 - 100, canvas.height / 2);
                context.fillText(`Score: ${score}`, canvas.width / 2 - 100, canvas.height / 2 + 50);
                context.fillText('Press Space to Restart', canvas.width / 2 - 150, canvas.height / 2 + 100);
            } else {
                frameCount++;
                requestAnimationFrame(updateAndDraw);
            }
        }

        function resetGame() {
            bird.y = 150;
            bird.velocity = 0;
            pipes.length = 0;
            score = 0;
            frameCount = 0;
            gameOver = false;
        }

        document.addEventListener('keydown', (event) => {
            if (event.code === 'Space') {
                if (gameOver) {
                    resetGame();
                    updateAndDraw();
                } else {
                    bird.velocity = bird.lift;
                }
            }
        });

        // Start the game loop
        bgImg.onload = () => {
            updateAndDraw();
        };
    </script>
</body>
</html>
Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *