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

张开发
2026/6/10 22:21:17 15 分钟阅读

分享文章

用原生JS手搓一个Flappy Bird小游戏(完整代码+逐行讲解)
用原生JS手搓一个Flappy Bird小游戏完整代码逐行讲解最近有学员问我学JavaScript到底有什么用我的回答是——它能让你亲手创造世界。今天我们就用不到200行代码还原那个曾经风靡全球的Flappy Bird。不需要任何框架只用最原生的Canvas API和基础JS语法你就能理解游戏开发的核心逻辑。1. 搭建游戏骨架我们先从最基础的HTML结构开始。创建一个480x270像素的画布这将是我们的主战场!DOCTYPE html html head titleFlappy Bird JS版/title style body { margin: 0; display: flex; justify-content: center; } canvas { background: skyblue; margin-top: 20px; } /style /head body canvas idgame width480 height270/canvas script srcgame.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); }扩展功能建议添加开始菜单和难度选择实现粒子特效碰撞时的爆炸效果加入音效系统本地存储最高分记录移动端适配viewport设置完整代码已经包含了游戏所有核心机制。现在你应该能理解Canvas绘图的基本原理游戏循环的实现方式物理模拟重力加速度碰撞检测算法事件驱动的交互设计试着修改参数看看效果把gravity改为0.3会让游戏变简单调整pipeInterval可以改变管道生成频率。我在教学时发现很多初学者都是在调整这些参数时突然开窍理解了变量如何影响程序行为。

更多文章