Flappy Bird 游戏实现

AI 智能总结

简介

Flappy Bird 是一款极简风格的飞行闯关游戏,玩家控制小鸟躲避管道障碍,穿越一对管道得一分。游戏厅版本使用 Canvas 渲染,实现了完整的物理模拟和碰撞检测。

游戏参数

1var W = 400, H = 500;              // 画布尺寸
2var bird = { x: 80, y: 200, r: 16 }; // 小鸟位置和半径
3var gravity = 0.45;                // 重力加速度
4var jumpForce = -7.5;              // 跳跃力度
5var pipeW = 52;                    // 管道宽度
6var pipeGap = 140;                 // 管道间距
7var pipeSpeed = 2.2;               // 管道移动速度

核心逻辑

重力与跳跃

小鸟每一帧受到重力影响向下加速,按空格键时获得向上的初速度:

 1function update() {
 2  // 重力加速
 3  bird.vy += gravity;
 4  bird.y += bird.vy;
 5
 6  // 顶部/底部边界
 7  if (bird.y + bird.r > H - 45 || bird.y - bird.r < 0) {
 8    gameOver();
 9    return;
10  }
11}

跳跃时重置垂直速度:

1function jump() {
2  if (!playing) {
3    // 首次跳跃 = 开始游戏
4    startGame();
5    return;
6  }
7  bird.vy = jumpForce;
8}

管道系统

管道从右向左匀速移动,离开屏幕后移除。当上一对管道越过一定距离时生成新管道:

 1var pipes = []; // { x, topH, passed }
 2
 3function spawnPipe() {
 4  var minH = 50;
 5  var maxH = H - pipeGap - 50 - 80; // 扣除地面
 6  var topH = minH + Math.random() * maxH;
 7  pipes.push({ x: W, topH: topH, passed: false });
 8}
 9
10function updatePipes() {
11  for (var i = pipes.length - 1; i >= 0; i--) {
12    pipes[i].x -= pipeSpeed;
13
14    // 计分:小鸟飞过管道
15    if (!pipes[i].passed && pipes[i].x + pipeW < bird.x) {
16      pipes[i].passed = true;
17      score++;
18    }
19
20    // 移除离屏管道
21    if (pipes[i].x + pipeW < 0) pipes.splice(i, 1);
22  }
23
24  // 生成新管道
25  if (pipes.length === 0 || pipes[pipes.length - 1].x < W - 200) {
26    spawnPipe();
27  }
28}

碰撞检测

小鸟的圆形碰撞体与管道矩形碰撞体的检测:

 1function checkCollision() {
 2  for (var i = 0; i < pipes.length; i++) {
 3    var p = pipes[i];
 4
 5    // 小鸟是否在管道水平范围内
 6    if (bird.x + bird.r > p.x && bird.x - bird.r < p.x + pipeW) {
 7      // 碰到上管道或下管道
 8      if (bird.y - bird.r < p.topH || bird.y + bird.r > p.topH + pipeGap) {
 9        gameOver();
10        return;
11      }
12    }
13  }
14}

Canvas 渲染

天空渐变背景

 1function drawSky() {
 2  var sky = ctx.createLinearGradient(0, 0, 0, H);
 3  sky.addColorStop(0, '#87CEEB');   // 天蓝
 4  sky.addColorStop(0.4, '#98D8E8');
 5  sky.addColorStop(0.7, '#E8DFC0'); // 渐变到沙色
 6  sky.addColorStop(0.85, '#D4C898');
 7  sky.addColorStop(1, '#C8B878');
 8  ctx.fillStyle = sky;
 9  ctx.fillRect(0, 0, W, H);
10}

地面

1function drawGround() {
2  // 砂土地面
3  ctx.fillStyle = '#8B7355';
4  ctx.fillRect(0, H - 40, W, 40);
5  // 草地边缘
6  ctx.fillStyle = '#6B8E23';
7  ctx.fillRect(0, H - 45, W, 8);
8}

管道

管道本体 + 管口加宽效果:

 1function drawPipes() {
 2  pipes.forEach(function(p) {
 3    // 上管道
 4    ctx.fillStyle = '#2d6a4f';
 5    ctx.fillRect(p.x, 0, pipeW, p.topH);
 6    // 上管口
 7    ctx.fillStyle = '#40916c';
 8    ctx.fillRect(p.x - 4, p.topH - 25, pipeW + 8, 25);
 9
10    // 下管道
11    var bottomY = p.topH + pipeGap;
12    ctx.fillStyle = '#2d6a4f';
13    ctx.fillRect(p.x, bottomY, pipeW, H - bottomY - 40);
14    // 下管口
15    ctx.fillStyle = '#40916c';
16    ctx.fillRect(p.x - 4, bottomY, pipeW + 8, 25);
17  });
18}

小鸟

圆形身体 + 高光 + 眼睛 + 鸟嘴:

 1function drawBird() {
 2  // 身体
 3  ctx.fillStyle = '#fbbf24';
 4  ctx.beginPath();
 5  ctx.arc(bird.x, bird.y, bird.r, 0, Math.PI * 2);
 6  ctx.fill();
 7
 8  // 腹部高光
 9  ctx.fillStyle = '#f59e0b';
10  ctx.beginPath();
11  ctx.arc(bird.x + 2, bird.y - 2, bird.r * 0.7, 0, Math.PI * 2);
12  ctx.fill();
13
14  // 眼睛
15  ctx.fillStyle = '#000';
16  ctx.beginPath();
17  ctx.arc(bird.x + bird.r * 0.5, bird.y - bird.r * 0.3, 3, 0, Math.PI * 2);
18  ctx.fill();
19
20  // 眼睛高光
21  ctx.fillStyle = '#fff';
22  ctx.beginPath();
23  ctx.arc(bird.x + bird.r * 0.5 - 1, bird.y - bird.r * 0.3 - 1, 1, 0, Math.PI * 2);
24  ctx.fill();
25
26  // 鸟嘴
27  ctx.fillStyle = '#f97316';
28  ctx.beginPath();
29  ctx.moveTo(bird.x + bird.r, bird.y);
30  ctx.lineTo(bird.x + bird.r + 10, bird.y + 2);
31  ctx.lineTo(bird.x + bird.r, bird.y + 5);
32  ctx.fill();
33}

游戏循环

 1function gameLoop() {
 2  if (!playing) return;
 3  update();
 4  drawSky();
 5  drawGround();
 6  drawPipes();
 7  drawBird();
 8  drawScore();
 9  frameId = requestAnimationFrame(gameLoop);
10}

计分显示

 1function drawScore() {
 2  ctx.fillStyle = '#fff';
 3  ctx.font = 'bold 32px sans-serif';
 4  ctx.textAlign = 'center';
 5  ctx.fillText(score, W / 2, 50);
 6  // 黑色描边增加可读性
 7  ctx.strokeStyle = '#000';
 8  ctx.lineWidth = 2;
 9  ctx.strokeText(score, W / 2, 50);
10}

游戏结束画面

 1function gameOver() {
 2  playing = false;
 3  cancelAnimationFrame(frameId);
 4
 5  // 半透明遮罩
 6  ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
 7  ctx.fillRect(0, 0, W, H);
 8
 9  // 结束文字
10  ctx.fillStyle = '#fff';
11  ctx.font = 'bold 24px sans-serif';
12  ctx.textAlign = 'center';
13  ctx.fillText('游戏结束', W / 2, H / 2 - 10);
14  ctx.font = '16px sans-serif';
15  ctx.fillText('得分: ' + score + '  点击重新开始', W / 2, H / 2 + 20);
16}

最高分持久化

1function updateBest() {
2  if (score > best) {
3    best = score;
4    localStorage.setItem('arcade-flappy-best', best);
5  }
6}

总结

Flappy Bird 看似简单,但涉及重力模拟、管道生成、碰撞检测等多个物理概念。Canvas 绘制的渐变天空和细节丰富的小鸟形象提升了视觉体验。管道生成使用了随机高度算法,确保每局游戏都不同。

留言评论

期待你的想法

评论加载中