news 2026/6/10 22:21:12

用原生JS手搓一个Flappy Bird小游戏(完整代码+逐行讲解)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用原生JS手搓一个Flappy Bird小游戏(完整代码+逐行讲解)

用原生JS手搓一个Flappy Bird小游戏(完整代码+逐行讲解)

最近有学员问我:"学JavaScript到底有什么用?"我的回答是——它能让你亲手创造世界。今天我们就用不到200行代码,还原那个曾经风靡全球的Flappy Bird。不需要任何框架,只用最原生的Canvas API和基础JS语法,你就能理解游戏开发的核心逻辑。

1. 搭建游戏骨架

我们先从最基础的HTML结构开始。创建一个480x270像素的画布,这将是我们的主战场:

<!DOCTYPE html> <html> <head> <title>Flappy Bird JS版</title> <style> body { margin: 0; display: flex; justify-content: center; } canvas { background: skyblue; margin-top: 20px; } </style> </head> <body> <canvas id="game" width="480" height="270"></canvas> <script src="game.js"></script> </body> </html>

接下来在game.js中初始化游戏环境:

const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); // 游戏状态控制 let gameRunning = true; let score = 0;

2. 创建游戏主角

小鸟对象需要包含位置、速度等属性,以及更新和绘制方法:

class Bird { constructor() { this.x = 50; this.y = canvas.height / 2; this.width = 30; this.height = 30; this.velocity = 0; this.gravity = 0.5; this.lift = -10; } draw() { ctx.fillStyle = 'yellow'; ctx.beginPath(); ctx.arc(this.x, this.y, this.width/2, 0, Math.PI * 2); ctx.fill(); } update() { this.velocity += this.gravity; this.y += this.velocity; // 边界检测 if (this.y > canvas.height - this.height/2) { this.y = canvas.height - this.height/2; this.velocity = 0; gameOver(); } if (this.y < this.height/2) { this.y = this.height/2; this.velocity = 0; } } flap() { this.velocity = this.lift; } }

关键点解析

  • gravity常量控制下落加速度
  • lift常量决定每次点击的上升力度
  • 使用arc()方法绘制圆形小鸟
  • 边界检测防止飞出画布

3. 障碍物系统设计

管道需要成对出现(上方和下方),并持续向左移动:

class Pipe { constructor() { this.x = canvas.width; this.width = 60; this.gap = 120; this.topHeight = Math.random() * (canvas.height - this.gap - 100) + 50; this.bottomHeight = canvas.height - this.topHeight - this.gap; this.speed = 2; this.passed = false; } draw() { ctx.fillStyle = 'green'; // 上方管道 ctx.fillRect(this.x, 0, this.width, this.topHeight); // 下方管道 ctx.fillRect(this.x, canvas.height - this.bottomHeight, this.width, this.bottomHeight); } update() { this.x -= this.speed; // 计分检测 if (!this.passed && this.x + this.width < bird.x) { score++; this.passed = true; } } collide(bird) { return ( bird.x + bird.width/2 > this.x && bird.x - bird.width/2 < this.x + this.width && (bird.y - bird.height/2 < this.topHeight || bird.y + bird.height/2 > canvas.height - this.bottomHeight) ); } }

管道生成逻辑需要控制频率:

let pipes = []; let pipeTimer = 0; const pipeInterval = 120; // 帧数 function generatePipes() { if (pipeTimer % pipeInterval === 0) { pipes.push(new Pipe()); } pipeTimer++; }

4. 游戏循环与碰撞检测

核心游戏循环负责更新所有对象状态并重绘画面:

function gameLoop() { if (!gameRunning) return; // 清空画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 更新状态 bird.update(); generatePipes(); pipes.forEach((pipe, index) => { pipe.update(); pipe.draw(); // 碰撞检测 if (pipe.collide(bird)) { gameOver(); } // 移除屏幕外的管道 if (pipe.x + pipe.width < 0) { pipes.splice(index, 1); } }); // 绘制小鸟和分数 bird.draw(); drawScore(); requestAnimationFrame(gameLoop); } function drawScore() { ctx.fillStyle = 'black'; ctx.font = '24px Arial'; ctx.fillText(`得分: ${score}`, 20, 30); } function gameOver() { gameRunning = false; ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'white'; ctx.font = '36px Arial'; ctx.textAlign = 'center'; ctx.fillText('游戏结束', canvas.width/2, canvas.height/2); ctx.fillText(`最终得分: ${score}`, canvas.width/2, canvas.height/2 + 50); }

5. 添加交互控制

为游戏添加键盘和触摸控制:

// 键盘控制 document.addEventListener('keydown', (e) => { if (e.code === 'Space') { bird.flap(); if (!gameRunning) resetGame(); } }); // 触摸控制 canvas.addEventListener('click', () => { bird.flap(); if (!gameRunning) resetGame(); }); function resetGame() { bird = new Bird(); pipes = []; score = 0; gameRunning = true; gameLoop(); }

6. 性能优化与扩展建议

现有基础版本还可以进一步优化:

性能优化点

  • 使用requestAnimationFrame替代setInterval
  • 对象池技术重用管道对象
  • 离屏Canvas预渲染静态元素
// 对象池示例 const pipePool = []; function getPipe() { if (pipePool.length > 0) { return pipePool.pop(); } return new Pipe(); } function recyclePipe(pipe) { pipePool.push(pipe); }

扩展功能建议

  1. 添加开始菜单和难度选择
  2. 实现粒子特效(碰撞时的爆炸效果)
  3. 加入音效系统
  4. 本地存储最高分记录
  5. 移动端适配(viewport设置)

完整代码已经包含了游戏所有核心机制。现在你应该能理解:

  • Canvas绘图的基本原理
  • 游戏循环的实现方式
  • 物理模拟(重力加速度)
  • 碰撞检测算法
  • 事件驱动的交互设计

试着修改参数看看效果:把gravity改为0.3会让游戏变简单,调整pipeInterval可以改变管道生成频率。我在教学时发现,很多初学者都是在调整这些参数时突然开窍,理解了变量如何影响程序行为。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 22:20:05

IDEA新手避坑指南:从Gitee拉取团队项目到成功运行Tomcat的完整流程

IDEA团队协作实战&#xff1a;从Gitee拉取项目到Tomcat部署的避坑手册刚接触团队协作开发的开发者&#xff0c;往往会在版本控制和环境配置上踩不少坑。记得我第一次用IDEA从Gitee拉取团队项目时&#xff0c;光是解决Tomcat配置问题就花了整整一个下午。本文将分享一套经过实战…

作者头像 李华
网站建设 2026/6/10 22:19:01

从‘地板’到‘灵能’:用Rimworld游戏机制拆解Mod开发底层逻辑

从‘地板’到‘灵能’&#xff1a;用Rimworld游戏机制拆解Mod开发底层逻辑在Rimworld的Mod开发中&#xff0c;许多开发者最初会陷入一个误区&#xff1a;认为只要实现功能即可&#xff0c;却忽略了与游戏本体机制的深度契合。这种割裂感往往导致Mod内容显得"格格不入"…

作者头像 李华
网站建设 2026/6/10 22:19:00

M5234BCC评估板深度解析:从ColdFire微控制器到IEEE 1588精密时间同步实战

1. 项目概述与核心价值 在嵌入式开发领域&#xff0c;尤其是涉及工业控制、网络通信和精密时间同步的项目中&#xff0c;从芯片选型到产品落地之间&#xff0c;往往横亘着一道名为“硬件原型验证”的鸿沟。直接设计并打板一块全新的电路板&#xff0c;不仅成本高、周期长&#…

作者头像 李华
网站建设 2026/6/10 22:16:05

别再只盯着TVS了!手把手教你为RS485选型GDT、TBU和TISP(附IEC标准解读)

RS485防护器件选型实战&#xff1a;从GDT到TISP的精准搭配指南在工业自动化现场&#xff0c;一台价值数百万的PLC设备因为RS485端口防护不足而烧毁&#xff0c;这样的场景并不罕见。许多工程师习惯性依赖TVS二极管作为万能解决方案&#xff0c;却忽视了不同瞬态干扰特性的本质差…

作者头像 李华
网站建设 2026/6/10 22:15:05

Excel定位条件全解析:从‘常量/公式’到‘差异单元格’,搞定数据核对与清理

Excel定位条件实战指南&#xff1a;数据清洗与核对的终极武器财务总监Lisa盯着屏幕上密密麻麻的销售报表皱起了眉头——季度审计在即&#xff0c;这份来自五个大区的合并数据充斥着格式混乱的数值、隐藏的错误公式和前后不一致的分类标准。传统的手动检查需要至少三天&#xff…

作者头像 李华