用原生JS手搓一个Flappy Bird小游戏(附完整源码和碰撞检测详解)

发布时间:2026/6/10 17:12:17
用原生JS手搓一个Flappy Bird小游戏(附完整源码和碰撞检测详解) 用原生JS手搓一个Flappy Bird小游戏附完整源码和碰撞检测详解在移动互联网时代Flappy Bird以其简单的玩法和极高的难度成为现象级游戏。作为前端开发者用原生JavaScript复刻这款经典游戏不仅能深入理解游戏开发的基本原理还能掌握Canvas绘图、游戏循环、碰撞检测等核心技能。本文将带你从零开始仅用原生JS和Canvas API实现一个完整的Flappy Bird游戏特别聚焦于矩形碰撞检测算法的实现细节。1. 游戏基础架构设计任何游戏的核心都离不开三个基本要素游戏对象、游戏循环和用户输入。对于我们的Flappy Bird来说需要特别关注以下几个关键组件小鸟对象负责位置更新、重力模拟和碰撞检测管道障碍物随机生成上下管道形成可穿越的间隙计分系统记录玩家通过的管道数量游戏区域使用Canvas绘制所有游戏元素// 游戏主对象结构示例 const Game { canvas: null, ctx: null, bird: null, pipes: [], score: 0, frameCount: 0, isGameOver: false };提示使用对象字面量组织游戏状态比全局变量更易于维护也减少了命名冲突的风险。2. 实现游戏核心机制2.1 小鸟的物理模拟Flappy Bird的核心玩法在于控制小鸟飞行高度避开障碍物。我们需要模拟两个关键物理效果重力加速度小鸟会持续下落点击升力玩家点击时给小鸟一个向上的速度class Bird { constructor(x, y) { this.x x; this.y y; this.width 30; this.height 30; this.velocity 0; this.gravity 0.5; this.lift -10; } update() { this.velocity this.gravity; this.y this.velocity; // 防止飞出屏幕顶部 if (this.y 0) { this.y 0; this.velocity 0; } } flap() { this.velocity this.lift; } }2.2 管道生成算法管道需要成对出现上下管道中间留有供小鸟穿过的间隙。关键参数包括参数说明典型值gapHeight上下管道间的间隙150pxpipeWidth管道宽度80pxpipeSpeed管道移动速度2px/帧spawnInterval新管道生成间隔150帧function generatePipe() { const gapStart Math.random() * (canvas.height - gapHeight); const topPipe { x: canvas.width, y: 0, width: pipeWidth, height: gapStart }; const bottomPipe { x: canvas.width, y: gapStart gapHeight, width: pipeWidth, height: canvas.height - gapStart - gapHeight }; Game.pipes.push(topPipe, bottomPipe); }3. 精确碰撞检测实现碰撞检测是游戏开发中的关键算法。对于Flappy Bird这类2D游戏我们采用**轴对齐边界框(AABB)**检测方法这是最简单高效的2D碰撞检测技术。3.1 AABB碰撞原理AABB检测基于一个简单原则如果两个矩形在x轴和y轴上的投影都重叠则它们发生碰撞。具体需要检查四个边界条件物体A的右边界 物体B的左边界物体A的左边界 物体B的右边界物体A的底边界 物体B的顶边界物体A的顶边界 物体B的底边界function checkCollision(rect1, rect2) { return ( rect1.x rect2.x rect2.width rect1.x rect1.width rect2.x rect1.y rect2.y rect2.height rect1.y rect1.height rect2.y ); }3.2 应用到游戏场景在游戏循环中我们需要检测小鸟与每个管道的碰撞情况function updateGame() { // 更新小鸟位置 Game.bird.update(); // 检测碰撞 for (const pipe of Game.pipes) { if (checkCollision(Game.bird, pipe)) { endGame(); return; } } // 检测是否撞到地面 if (Game.bird.y Game.bird.height canvas.height) { endGame(); } }4. 完整游戏循环实现游戏循环是驱动游戏运行的核心机制通常包含以下步骤清除画布擦除上一帧的内容更新游戏状态计算所有对象的新位置渲染对象绘制所有游戏元素到画布请求下一帧继续循环function gameLoop() { if (Game.isGameOver) return; // 1. 清除画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 2. 更新游戏状态 updateGame(); // 3. 渲染所有对象 drawBird(); drawPipes(); drawScore(); // 4. 继续循环 requestAnimationFrame(gameLoop); } // 启动游戏 function startGame() { Game.bird new Bird(100, canvas.height / 2); Game.pipes []; Game.score 0; Game.isGameOver false; gameLoop(); }注意现代浏览器推荐使用requestAnimationFrame而非setInterval来实现游戏循环它能更好地与浏览器刷新率同步。5. 性能优化与进阶技巧当游戏复杂度增加时需要考虑以下优化策略对象池模式复用管道对象而非频繁创建销毁视口剔除不渲染屏幕外的对象碰撞检测优化空间分区算法如四叉树// 对象池示例 const pipePool []; function getPipe() { if (pipePool.length 0) { return pipePool.pop(); } return { x: 0, y: 0, width: 0, height: 0 }; } function recyclePipe(pipe) { pipePool.push(pipe); }6. 完整源码解析以下是游戏的核心代码结构!DOCTYPE html html head titleFlappy Bird with Vanilla JS/title style canvas { background: skyblue; display: block; margin: 0 auto; } /style /head body canvas idgameCanvas width400 height600/canvas script // 完整游戏实现代码... // 包含上述所有功能模块 /script /body /html实现过程中有几个关键点值得注意游戏状态管理清晰区分游戏运行、暂停和结束状态难度曲线随着分数增加可以适当提高管道移动速度或减小间隙用户体验添加游戏开始/结束界面增加视觉反馈效果在调试碰撞检测时一个实用的技巧是临时添加可视化碰撞框function drawDebugCollisionBox() { ctx.strokeStyle red; ctx.strokeRect( Game.bird.x, Game.bird.y, Game.bird.width, Game.bird.height ); }从项目实践来看最大的挑战往往不在于代码实现本身而在于参数调优。比如重力大小、升力强度、管道移动速度等参数的微小变化会极大影响游戏体验。建议将这些参数提取为配置对象方便快速调整const Config { gravity: 0.5, lift: -10, pipeSpeed: 2, gapHeight: 150, spawnRate: 150 };最后要强调的是虽然这个实现只用了不到200行代码但它完整呈现了一个游戏的核心架构。以此为起点你可以进一步扩展功能比如添加背景滚动、粒子特效、音效支持等逐步构建更丰富的游戏体验。