手把手教你用HTML5 Canvas开发Flappy Bird类游戏

文章目录

手把手教你用HTML5 Canvas开发Flappy Bird类游戏

Flappy Bird是一款风靡全球的休闲小游戏,玩家通过点击屏幕控制小鸟穿越管道,操作简单却极具挑战性。本文将带你从零开始,使用HTML5 Canvas和原生JavaScript实现这款游戏,掌握2D游戏中的重力模拟、碰撞检测等核心技术。
在这里插入图片描述

一、游戏核心原理与准备

在开始编码前,我们先明确Flappy Bird的核心机制:

  • 小鸟(Bird):受重力影响持续下落,点击屏幕时向上跳跃
  • 管道(Pipes):从右侧持续向左移动,管道中间有间隙供小鸟穿过
  • 重力系统:通过加速度模拟重力效果,让小鸟的运动更真实
  • 碰撞检测:检测小鸟是否撞到管道、地面或天花板
  • 计分系统:小鸟成功穿过管道后得分

准备工作:创建一个HTML文件(命名为flappy-bird.html),我们将在这个文件中完成所有开发。

二、步骤1:搭建基础HTML结构

首先创建游戏的基本页面框架,包含Canvas元素和必要的样式:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flappy Bird类游戏</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            background-color: #f0f0f0;
            font-family: Arial, sans-serif;
        }
        #gameCanvas {
            border: 2px solid #333;
            background-color: #4EC0CA; /* 天空蓝色背景 */
        }
        .game-info {
            margin: 10px 0;
            font-size: 1.2em;
            color: #333;
        }
    </style>
</head>
<body>
    <h1>Flappy Bird</h1>
    <div class="game-info">点击屏幕或按空格键让小鸟飞起来</div>
    <!-- 游戏画布 -->
    <canvas id="gameCanvas" width="400" height="600"></canvas>

    <script>
        // 游戏代码将写在这里
    </script>
</body>
</html>

代码说明

  • 创建了一个400x600像素的Canvas画布,高度大于宽度符合竖屏游戏体验
  • 背景色设为天空蓝,营造游戏氛围
  • 添加了简单的说明文字,指导玩家操作
  • 预留<script>标签用于编写游戏逻辑

三、步骤2:初始化游戏对象与参数

接下来定义游戏中所有元素的属性,包括小鸟、管道、地面等核心对象。在<script>标签中添加:

// 获取Canvas元素和绘图上下文
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// 小鸟属性
const bird = {
    x: 100,          // 初始X坐标
    y: 200,          // 初始Y坐标
    width: 34,       // 宽度
    height: 24,      // 高度
    velocity: 0,     // 垂直速度
    gravity: 0.5,    // 重力加速度(控制下落速度)
    jumpStrength: -10 // 跳跃力度(负值表示向上)
};

// 管道属性
const pipe = {
    width: 70,       // 管道宽度
    gap: 150,        // 管道中间的间隙大小
    speed: 2,        // 管道移动速度
    frequency: 150   // 生成新管道的间隔(帧)
};

// 存储所有管道的数组
let pipes = [];

// 地面属性
const ground = {
    y: canvas.height - 50, // 地面Y坐标
    height: 50            // 地面高度
};

// 游戏状态
let score = 0;
let frameCount = 0; // 帧计数器,用于控制管道生成
let gameOver = false;

代码说明

  • bird对象:包含小鸟的位置、大小和运动属性,gravity模拟重力,jumpStrength控制跳跃力度
  • pipe对象:定义管道的尺寸、间隙和移动速度,frequency控制管道生成频率
  • pipes数组:存储所有当前在屏幕上的管道
  • ground对象:定义地面位置和高度,作为碰撞检测的边界
  • 游戏状态变量:记录分数、帧计数和游戏是否结束

四、步骤3:绘制游戏元素

编写函数绘制小鸟、管道、地面和游戏信息(分数、游戏结束画面):

// 绘制小鸟
function drawBird() {
    ctx.fillStyle = '#FFD700'; // 金黄色小鸟
    // 绘制小鸟身体(椭圆)
    ctx.beginPath();
    ctx.ellipse(
        bird.x + bird.width/2, 
        bird.y + bird.height/2, 
        bird.width/2, 
        bird.height/2, 
        0, 0, Math.PI * 2
    );
    ctx.fill();
    
    // 绘制眼睛
    ctx.fillStyle = '#000';
    ctx.beginPath();
    ctx.arc(bird.x + bird.width * 0.7, bird.y + bird.height/3, 3, 0, Math.PI * 2);
    ctx.fill();
    
    // 绘制嘴巴
    ctx.fillStyle = '#FF6B35';
    ctx.beginPath();
    ctx.moveTo(bird.x + bird.width, bird.y + bird.height/2);
    ctx.lineTo(bird.x + bird.width + 6, bird.y + bird.height/2 - 2);
    ctx.lineTo(bird.x + bird.width, bird.y + bird.height/2 - 4);
    ctx.fill();
}

// 绘制管道
function drawPipes() {
    pipes.forEach(pipe => {
        // 上管道(倒置)
        ctx.fillStyle = '#78AB46';
        ctx.fillRect(pipe.x, 0, pipe.width, pipe.topHeight);
        // 上管道顶部装饰
        ctx.fillRect(pipe.x - 5, pipe.topHeight - 10, pipe.width + 10, 10);
        
        // 下管道
        ctx.fillRect(pipe.x, pipe.bottomY, pipe.width, canvas.height - pipe.bottomY);
        // 下管道顶部装饰
        ctx.fillRect(pipe.x - 5, pipe.bottomY, pipe.width + 10, 10);
    });
}

// 绘制地面
function drawGround() {
    ctx.fillStyle = '#78AB46'; // 绿色地面
    ctx.fillRect(0, ground.y, canvas.width, ground.height);
    
    // 绘制地面纹理(简单的线条)
    ctx.fillStyle = '#5A8C35';
    for (let i = 0; i < canvas.width; i += 40) {
        ctx.fillRect(i, ground.y + 10, 20, 5);
    }
}

// 绘制分数
function drawScore() {
    ctx.fillStyle = '#FFF';
    ctx.font = '30px Arial';
    ctx.textAlign = 'center';
    ctx.fillText(score, canvas.width / 2, 50);
}

// 绘制游戏结束画面
function drawGameOver() {
    // 半透明背景
    ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    // 游戏结束文字
    ctx.fillStyle = '#FFF';
    ctx.font = '30px Arial';
    ctx.textAlign = 'center';
    ctx.fillText('游戏结束', canvas.width / 2, canvas.height / 2 - 30);
    
    // 显示分数
    ctx.font = '24px Arial';
    ctx.fillText(`得分: ${score}`, canvas.width / 2, canvas.height / 2 + 10);
    
    // 重启提示
    ctx.font = '18px Arial';
    ctx.fillText('点击屏幕重启', canvas.width / 2, canvas.height / 2 + 50);
}

代码说明

  • drawBird():使用椭圆和基本图形绘制小鸟,添加眼睛和嘴巴增强视觉效果
  • drawPipes():绘制上下管道及顶部装饰,使管道更美观
  • drawGround():绘制绿色地面并添加简单纹理,增强游戏视觉效果
  • 信息绘制:在屏幕顶部显示当前分数,游戏结束时显示结果和重启提示

五、步骤4:处理用户输入(控制小鸟跳跃)

实现通过点击屏幕或按空格键让小鸟跳跃的功能:

// 处理跳跃输入(点击屏幕)
canvas.addEventListener('click', handleJump);

// 处理跳跃输入(空格键)
document.addEventListener('keydown', (e) => {
    if (e.code === 'Space') {
        handleJump();
    }
});

// 跳跃处理函数
function handleJump() {
    if (gameOver) {
        // 游戏结束时点击重启
        resetGame();
        return;
    }
    // 应用跳跃力(向上的速度)
    bird.velocity = bird.jumpStrength;
}

代码说明

  • 监听click事件:点击画布任意位置触发小鸟跳跃
  • 监听keydown事件:按下空格键也能触发跳跃,增加操作方式
  • 游戏结束时点击屏幕会触发重置游戏函数
  • 跳跃原理:给小鸟一个向上的初始速度(负值),对抗重力

六、步骤5:实现管道生成与移动逻辑

编写代码控制管道的生成、移动和移除:

// 生成新管道
function spawnPipe() {
    // 计算管道间隙的随机位置(限制在合理范围内)
    const minGapTop = 50; // 最小顶部管道高度
    const maxGapTop = canvas.height - ground.height - pipe.gap - minGapTop; // 最大顶部管道高度
    const topHeight = minGapTop + Math.random() * maxGapTop;
    
    // 添加新管道到数组
    pipes.push({
        x: canvas.width,          // 从右侧屏幕外进入
        topHeight: topHeight,     // 上管道高度
        bottomY: topHeight + pipe.gap // 下管道起始Y坐标
    });
}

// 更新管道位置
function updatePipes() {
    // 控制管道生成频率(每N帧生成一个)
    frameCount++;
    if (frameCount % pipe.frequency === 0) {
        spawnPipe();
    }
    
    // 移动管道并移除超出屏幕的管道
    for (let i = pipes.length - 1; i >= 0; i--) {
        pipes[i].x -= pipe.speed;
        
        // 检查小鸟是否穿过管道(计分)
        if (!pipes[i].scored && pipes[i].x + pipe.width < bird.x) {
            score++;
            pipes[i].scored = true; // 标记为已计分,避免重复计分
        }
        
        // 移除左侧超出屏幕的管道
        if (pipes[i].x + pipe.width < 0) {
            pipes.splice(i, 1);
        }
    }
}

代码说明

  • spawnPipe():随机生成管道间隙位置,确保间隙在合理范围内(不会太靠近顶部或底部)
  • 管道生成逻辑:通过frameCount控制生成频率,每150帧生成一个新管道
  • 管道移动:每帧向左移动一定距离,模拟小鸟向前飞行的效果
  • 计分逻辑:当管道完全通过小鸟位置时加分,使用scored标记避免重复计分
  • 内存管理:及时移除超出屏幕的管道,避免数组无限增长

七、步骤6:实现碰撞检测

碰撞检测是游戏的核心逻辑,需要检测小鸟是否撞到管道、地面或天花板:

// 检测碰撞
function checkCollisions() {
    // 撞到地面或天花板
    if (bird.y + bird.height > ground.y || bird.y < 0) {
        gameOver = true;
    }
    
    // 撞到管道
    for (const p of pipes) {
        // 检测水平方向是否重叠
        if (bird.x + bird.width > p.x && bird.x < p.x + pipe.width) {
            // 检测垂直方向是否撞到上管道或下管道
            if (bird.y < p.topHeight || bird.y + bird.height > p.bottomY) {
                gameOver = true;
                break;
            }
        }
    }
}

代码说明

  • 地面/天花板碰撞:小鸟底部超过地面Y坐标或顶部小于0时判定为碰撞
  • 管道碰撞:分两步检测
    1. 水平方向:小鸟与管道在X轴上是否重叠
    2. 垂直方向:小鸟是否撞到上管道底部或下管道顶部
  • 碰撞后立即设置gameOvertrue,结束当前游戏

八、步骤7:实现游戏主循环与重置功能

游戏主循环负责更新游戏状态和重绘画面,重置功能用于游戏结束后重新开始:

// 重置游戏
function resetGame() {
    // 重置小鸟状态
    bird.y = 200;
    bird.velocity = 0;
    
    // 清空管道
    pipes = [];
    
    // 重置游戏状态
    score = 0;
    frameCount = 0;
    gameOver = false;
}

// 游戏主循环
function gameLoop() {
    // 清空画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    if (!gameOver) {
        // 更新小鸟位置(应用重力)
        bird.velocity += bird.gravity;
        bird.y += bird.velocity;
        
        // 更新管道
        updatePipes();
        
        // 检测碰撞
        checkCollisions();
    }
    
    // 绘制所有游戏元素
    drawPipes();
    drawGround();
    drawBird();
    drawScore();
    
    // 如果游戏结束,绘制结束画面
    if (gameOver) {
        drawGameOver();
    }
    
    // 继续游戏循环
    requestAnimationFrame(gameLoop);
}

// 启动游戏
gameLoop();

代码说明

  • resetGame():重置小鸟位置、清空管道、重置分数和游戏状态,为新游戏做准备
  • 游戏主循环流程:
    1. 清空画布 → 2. 更新游戏状态(小鸟位置、管道) → 3. 检测碰撞 → 4. 绘制所有元素
    2. 重力模拟:通过bird.velocity += bird.gravity实现加速度效果,让小鸟下落速度越来越快
    3. requestAnimationFrame:浏览器优化的动画API,自动控制帧率(通常60帧/秒)
    4. 游戏结束处理:绘制结束画面,保持当前状态直到用户点击重启

九、完整代码与运行效果

将以上所有代码整合后,保存并在浏览器中打开,你将获得一个功能完整的Flappy Bird类游戏:

  • 点击屏幕或按空格键控制小鸟跳跃
  • 小鸟受重力影响自然下落
  • 管道从右侧不断出现,中间有间隙供小鸟穿过
  • 成功穿过管道得分,撞到管道或地面则游戏结束
  • 游戏结束后点击屏幕可重新开始

十、扩展功能建议(进阶练习)

如果想进一步提升游戏体验,可以尝试添加这些功能:

  1. 难度递增:随着分数增加,管道移动速度逐渐加快,间隙变小
  2. 音效系统:添加跳跃、得分、碰撞的音效
  3. 动画效果:给小鸟添加扇动翅膀的动画,管道添加移动特效
  4. 最高分记录:使用localStorage保存玩家的最高分数
  5. 开始界面:添加游戏标题和"开始游戏"按钮
  6. 粒子效果:碰撞时添加爆炸粒子效果,增强视觉反馈

通过本教程,你已经掌握了Flappy Bird类游戏的核心开发技术,包括重力模拟、物体移动、碰撞检测和游戏循环。这些知识是2D游戏开发的基础,掌握后可以尝试开发更复杂的游戏。祝你编程愉快!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值