资讯详情

资讯详情

AI编程助手实战:从零开发H5射击游戏的全流程记录

前阵子接了个临时需求要在短时间内做一个能在手机浏览器里直接玩的H5射击游戏。本来这种小项目我都是自己闷头写结果正好想试试AI编程助手在游戏开发里的真实表现索性全程带着它一起做。整个过程下来AI确实帮了大忙但也埋了不少坑。这篇就把这次“人类定方案、AI写代码、人类救火”的完整协作过程记录下来包括需求拆解、提示词写法、代码审查方法、性能优化还有跨端适配这些真刀真枪的环节希望能给想用AI辅助写游戏的朋友一点参考。1. 项目定位与整体设计思路1.1 为什么选H5射击游戏来练手我选这个方向是有意为之。H5射击游戏麻雀虽小五脏俱全它既有实时渲染循环又有大量对象频繁创建销毁还涉及碰撞检测、输入处理、音效触发、UI更新这些典型游戏模块。拿它来测试AI编程助手可以覆盖很多常见开发场景。更重要的是射击游戏的逻辑边界非常清晰。玩家移动、子弹飞行、敌人生成、碰撞判定每个模块都能独立描述。这种特征正好匹配当前AI编程助手的能力曲线——它擅长处理定义明确的单一功能而不是模糊不清的全局架构。你把一个复杂游戏拆成十几个小任务每个任务都用自然语言清晰描述AI给出可用代码的概率会高很多。另外一个考虑是项目周期。这个游戏我计划是一周内完成从零到可上线。如果纯手写光调碰撞检测和性能优化就能磨掉两三天。用AI先铺出骨架和基础逻辑我集中精力处理架构、边界情况和性能瓶颈整体节奏会舒服很多。1.2 技术选型Canvas还是DOM动手之前先定渲染方案。H5游戏的渲染路线无非三条纯DOM/CSS、Canvas 2D、WebGL或Three.js。我的判断标准很简单——项目规模、目标平台、开发效率。这次做的是一款纵版太空射击游戏敌机从上方出现玩家在底部移动射击。需要绘制的元素包括背景星空、玩家战机、敌方单位、子弹、爆炸特效、得分UI。如果走DOM路线节点数量一多就会出现明显卡顿尤其手机浏览器对DOM重排的优化有限几百个弹体同时存在时掉帧会很严重。WebGL当然画质最好但为了一个小项目引入着色器和矩阵变换开发成本明显不划算。所以最终选了Canvas 2D。它做2D游戏渲染是够用的API直观学习曲线平缓而且对AI编程助手来说也是训练数据最丰富的图像接口之一。AI生成的Canvas绘制代码通常比WebGL代码靠谱得多。后来又补了一个决策所有UI层用DOM叠加在Canvas上方。分数、血量、游戏结束面板这些不需要高频更新的元素放在DOM层里写起来轻松也不影响Canvas的渲染性能。这个“Canvas渲染世界、DOM渲染界面”的混合架构在很多商业H5游戏中也是常见做法。1.3 架构规划先定文件再谈编码在让AI写任何代码之前我先把项目骨架搭了出来。这不是走形式而是给AI划定工作边界。如果一上来就让它写一个2000行的单文件游戏输出基本没法看。正确的做法是拆分成职责单一的模块每个模块对应一个文件。最终的项目结构是这样的game-root/ ├── index.html ├── css/ │ └── style.css ├── js/ │ ├── main.js // 入口初始化游戏实例 │ ├── config.js // 全局配置参数 │ ├── player.js // 玩家机 │ ├── enemy.js // 敌方单位 │ ├── bullet.js // 子弹逻辑 │ ├── effects.js // 爆炸特效 │ ├── collision.js // 碰撞检测 │ ├── audio.js // 音效管理 │ ├── ui.js // 界面渲染 │ └── utils.js // 工具函数 └── assets/ └── (音效和素材文件)这种拆法有几个好处。第一每个文件的代码量控制在100-250行AI生成时不容易跑偏。第二模块之间通过接口通信我可以先定义好接口签名再让AI填充实现。第三出了问题排查快哪个文件报错就盯哪个文件不用在一堆面条式代码里翻找。2. 与AI编程助手的协作方法论2.1 提示词设计的几条实战原则用AI编程助手写代码最核心的技术不是会写代码而是会描述需求。这几条原则是我这次实践下来最有效的原则一给出场景不要只给指令。直接说“写一个敌人移动函数”AI只会给你一个通用函数。但如果你说“在Canvas游戏中敌人从屏幕上方以正弦函数轨迹向下移动移动速度受关卡难度影响”AI写出来的东西就完全不同。场景描述了变量关系和边界条件AI才能生成贴合需求的代码。原则二明确输入输出和函数签名。我通常会这样描述“写一个函数输入是敌机对象和当前时间戳输出是更新后的坐标使用Math.sin控制水平偏移”。这样AI生成的代码基本可以直接用不需要大幅改动。原则三给负面约束。告诉AI“不要做什么”和“要做什么”同样重要。比如我生成子弹逻辑时会专门强调“子弹飞出屏幕边界后必须从数组中移除避免内存泄漏”。这类约束AI不太容易主动想到你不提它就漏。原则四多轮对话优于一次性长文本。一次性把整个游戏需求都丢给AI它给出的往往是看似完整实则松散的大杂烩。我采用的是“一模块一轮对话”的模式先让它生成子弹模块我审查、测试、通过再生成敌人模块。每次对话只聚焦一个点。2.2 任务拆分与迭代节奏小步快跑这次项目我把全流程拆成了十几个独立任务按依赖顺序排列创建Canvas画布和游戏主循环骨架实现玩家机的绘制与键盘/触摸移动实现子弹的发射、移动与回收实现敌人的生成与自动移动实现碰撞检测与爆炸特效实现得分、血量UI增加音效和背景音乐性能优化与设备适配拆完任务每个任务里我又细分出明确的验收标准。比如“子弹回收”的验收标准是“连续发射5分钟内存不持续增长”。有了验收标准AI生成的代码我才能快速验证是否合格。节奏上我坚持“一次只推进一个任务”。刚开始协作时我急着让AI一次性生成多个模块结果模块之间互相调用对方不存在的函数报错连着来。后来改成小步迭代每次改完立即在浏览器里跑一遍确认无误再继续下一个。表面上看效率低了实际上总时间反而缩短了。2.3 代码审查AI输出的把关方法AI生成的代码不能照单全收这个道理谁都懂但真正落到实践里需要一套具体的审查流程。我采用的是三层检查法。第一层是语法和结构检查。看AI生成的代码是否能直接运行。这一步最基础但最容易出问题AI偶尔会生成与项目现有代码风格不一致或者引用了未定义变量的代码。第二层是逻辑一致性检查。重点看AI是否遵守了我在提示词里给的约束和接口定义。这个环节要特别留意AI生成代码中“自作主张”的部分。比如我明确说了子弹回收用对象池AI可能会简化成直接创建新对象代码看起来更简洁但性能池的意义就没了。第三层是边界情况检查。测试输入极端值看程序是否还能正常工作。AI生成的代码通常只处理“正常情况”玩家一直按着射击键不松手怎么办敌人在屏幕边缘生成一半怎么办高速移动时检测不到碰撞怎么办——这些问题AI不会主动考虑必须人来补充。3. 核心玩法实现实录3.1 玩家机与移动逻辑玩家的控制是整个游戏的基础手感来源。我最初的方案是使用方向键控制移动和空格键射击。但考虑到手机端的操作需求后来又补充了触摸拖拽控制。AI生成的方向键控制代码本身没问题但移动速度没有做归一化处理导致不同帧率下移动速度不一样。这里涉及一个很关键的概念deltaTime时间增量。无论你的循环跑60帧还是30帧每次移动的距离都应该基于“上一帧到现在经过的时间”来计算而不是固定的每帧像素数。我让AI重构了这部分逻辑。// 基于deltaTime的移动逻辑 update(deltaTime) { const speed 300 * deltaTime; // 每秒300像素与帧率无关 if (keys.left) this.x - speed; if (keys.right) this.x speed; if (keys.up) this.y - speed; if (keys.down) this.y speed; // 边界限制 this.x Math.max(0, Math.min(canvas.width - this.width, this.x)); this.y Math.max(0, Math.min(canvas.height - this.height, this.y)); }这段代码看起来简单但有几个细节值得注意。边界限制用的是Math.max和Math.min的组合把坐标限制在有效范围内而不是用if else判断代码更简洁也不容易出错。速度用300像素每秒这是经过手感调试的结果——太慢打起来着急太快又容易失控。3.2 对象池提高子弹性能的关键射击游戏最核心的性能挑战是频繁创建和销毁对象。如果玩家每秒发射10发子弹每发子弹都是一个JavaScript对象游戏运行10分钟后就会有成千上万个对象被创建又回收。如果处理不当垃圾回收器会频繁触发导致游戏卡顿。我的方案是对象池Object Pool。原理非常简单创建一批子弹对象放着发射时从池里取一个空闲的用完后还回池里而不是直接销毁。class BulletPool { constructor(size, factory) { this.pool []; this.active []; for (let i 0; i size; i) { this.pool.push(factory()); } } spawn(x, y, speed) { const bullet this.pool.pop(); if (!bullet) return null; // 池空了本次发射失败 bullet.x x; bullet.y y; bullet.speed speed; bullet.active true; this.active.push(bullet); return bullet; } recycle(bullet) { bullet.active false; this.pool.push(bullet); this.active this.active.filter(b b ! bullet); } }实际测试下来使用对象池后游戏中同时存在的子弹对象不会超过池子容量内存曲线非常平稳。AI在我第一次要求时生成的代码没用对象池是我在审查环节指出并让它重构的。这件事说明了一个道理AI会默认选择最简单的实现方式而性能优化类的需求必须主动提。3.3 敌人生成机制难度曲线的设计敌人的生成不能随机乱来。如果所有敌人都在同一时间出现玩家会疲于应付如果间隔太久游戏又会显得无聊。这里需要一条合理的难度曲线。我的方案是按“波次”wave组织敌人的出现。每波敌人之间有一个间隔游戏越往后间隔越短敌人种类越多。这个逻辑用AI的提示词描述起来很清晰“每2秒生成一波敌人每波3-5个波次间隔随游戏时间从2000毫秒递减到500毫秒敌人类型随机从当前解锁的类型中选取。”AI生成的敌人移动逻辑中我加入了一些变化——不同敌机使用不同的运动模式。一种从上直飞一种是蛇形运动还有一种是朝玩家位置追踪。这类AI逻辑生成时容易出问题的点在于“蛇形运动的数学公式写错”。AI生成的Math.sin调用里有时候周期参数和幅度参数含义会混需要人肉眼审查。3.4 碰撞检测与分数结算碰撞检测是整个游戏里最容易出现“看似正确但实际有bug”的部分。我用了AABB轴对齐边界盒检测原理是检查两个矩形是否有交集这是2D游戏中最常用的碰撞方案。AABB检测的关键是判定条件。两个矩形相交的条件是一个矩形的右边界大于另一个的左边界并且一个矩形的下边界大于另一个的上边界反之亦然。AI生成的碰撞代码通常长这样function checkCollision(a, b) { return a.x b.x b.width a.x a.width b.x a.y b.y b.height a.y a.height b.y; }这个逻辑本身没问题但实际应用中我发现了一个性能隐患如果直接对每颗子弹和每个敌人都做碰撞检测复杂度是O(n²)。当游戏中有30发子弹和20个敌人时就是600次检测还能接受。但当对象数量上到几百的时候每帧都要做上万次检测手机就会卡。我最终采用的是粗略的空间分桶方案把画布划分成若干格子每帧只检测同一格子里的对象是否碰撞。这个优化让检测次数从O(n²)降到了接近O(n)性能提升明显。3.5 效果反馈与UI层射击游戏做得“爽不爽”很大程度上看效果反馈。子弹击中敌人要有爆炸特效特效要让玩家明确感知到“打中了”。AI生成的粒子特效代码很适合这个场景几行循环就能绘制出漫天飞散的光点。// 爆炸粒子系统简化版 class ParticleSystem { emit(x, y, count, color) { for (let i 0; i count; i) { const angle Math.random() * Math.PI * 2; const speed 100 Math.random() * 200; this.particles.push({ x: x, y: y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: 0.5 Math.random() * 0.5, color: color }); } } }UI层包括分数、血量、关卡进度和游戏结束面板。这块用DOM实现相对直接但需要注意一个原则不要让DOM更新频率过高。分数显示是每击中新敌人才触发一次更新不需要在requestAnimationFrame循环里反复重绘。4. 性能优化与跨端适配实战4.1 Canvas渲染优化从卡顿到丝滑游戏跑起来之后第一个被程序员用户测出来的问题就是卡顿。打开浏览器开发者工具的性能面板我发现每帧耗时明显超标。主要瓶颈有两个一是在Canvas上重复绘制静态背景二是透明度和阴影效果使用过度每次绘制都有大量的填充计算。针对背景问题我把星空背景预渲染成一个离屏Canvas。具体做法是在初始化时把整幅星空背景画在一个不显示的Canvas上游戏运行时直接drawImage把整幅背景贴上去而不是每帧重新绘制几百颗星星。这一项优化就把每帧的绘制时间降低了一半以上。针对阴影和透明度问题我直接移除了背景星星的GlobalAlpha设置统一为不透明色。这些小细节在PC上感知不明显但在手机GPU上影响很显著。如果想把Canvas性能压榨到极致可以考虑使用WebGL渲染背景层。但这种做法复杂度飙升本次项目没有采用。如果素材量大、场景层级多WebGL才是终极解。4.2 帧率控制与requestAnimationFrame游戏主循环的实现方式有很多种我选的是requestAnimationFrame而不是setInterval或setTimeout。原因很简单requestAnimationFrame由浏览器同步屏幕刷新率调度不会出现setInterval常见的方向偏差也不会在标签页切后台时继续运行浪费资源。但requestAnimationFrame本身有一个特点需要注意它会在浏览器标签页不可见时自动暂停。如果用户把游戏切到后台再回来游戏逻辑就会跳过一段“暂停时间”。如果代码没有处理好这个时间差玩家会发现切回来之后主角瞬间被敌人消灭了。典型的修复方案是记录上一帧的时间戳计算暂停时间并合理处理。这也是很多H5游戏的常见坑。let lastTime 0; function gameLoop(timestamp) { // 计算deltaTime限制最小值防止卡顿后跳帧过多 let deltaTime (timestamp - lastTime) / 1000; if (deltaTime 0.1) deltaTime 0.1; lastTime timestamp; update(deltaTime); render(); requestAnimationFrame(gameLoop); }4.3 不同环境的适配经验H5游戏的适配问题主要集中在三个方面屏幕尺寸、触摸事件、声音播放。屏幕尺寸适配是第一个绕不开的问题。现在的手机屏幕五花八门刘海屏、挖孔屏、全面屏安全区域都不一样。我的做法是让Canvas按固定设计分辨率如750x1334工作然后通过CSS缩放适配到实际屏幕同时用viewport-fitcover配合环境变量safe-area-inset规避刘海区域。meta nameviewport contentwidthdevice-width, initial-scale1.0, viewport-fitcover触摸事件这块主要问题是移动端特有的touch事件与PC端mouse事件的差异。如果直接把mousedown/mousemove做成玩家控制手机上一摸就出问题。我的做法是做一个输入抽象层同时监听鼠标和触摸事件统一转成内部的移动指令。这个抽象层AI生成得很顺利因为我把它描述成了一个事件转发器。声音播放的坑更隐蔽。iOS Safari要求用户交互后才能播放音频否则audio.play()会被拒绝。最简单的处理方案是在用户第一次点击屏幕时调一次audio.resume()把音频上下文激活。如果不处理这个细节iOS用户打开游戏会完全没有声音体验大打折扣。5. 常见问题与排查技巧实录5.1 AI生成代码的典型“坑”用AI编程助手的人早晚会遇到几类典型问题。这次项目中我遇到的AI代码问题大致可以归为四类。第一类是“接口幻觉”。AI生成模块A时假设了模块B中有一个函数叫getEnemyList()但实际上模块B中根本没有这个函数。这是多模块开发中最常见的AI错误。解决方案是在提示词中明确约定“你只能使用我提供的接口不能自行添加外部接口”或者代码审查时仔细检查所有函数调用。第二类是“边界条件缺失”。很多AI生成的代码在正常输入下工作良好但玩家偏离常规操作路径后就会出错。比如玩家连续快速点击开始按钮可能生成两个游戏实例玩家在游戏结束后立即重新开始旧的对象池没有清理干净。第三类是“性能感知不足”。AI知道对象池是什么但你不提醒它就不会主动用。在子弹这种高频创建销毁的场景里AI默认直接new对象代码简洁但对GC很不友好。第四类是“浏览器兼容性想当然”。AI经常假设浏览器支持最新的JavaScript语法和API但用户的手机浏览器版本可能很旧。这需要开发者在构建产物时做好语法降级或者在适配阶段手动补充Polyfill。5.2 手感调优肉眼看不出来的参数差距AI很难帮你做的一件事是“手感调优”。移动速度、子弹发射速率、敌人碰撞体大小、爆炸粒子数量这些参数每改一点游戏的体验就会有一些微妙变化而这些变化用代码审查根本看不出来必须实际玩。我调这类参数的方法是“数据驱动”先把所有关键参数抽离到config.js文件里然后集中调整。例如const config { playerSpeed: 300, bulletSpeed: 600, fireRate: 8, // 每秒射击次数 enemySpawnInterval: 2000, bulletCollisionPadding: 0.8, // 碰撞体缩小比例 particleCount: 12, };把所有参数集中管理后就可以像调音台一样逐项调整。我给玩家测试的快捷方式是浏览器控制台直接改config对象的值实时刷新效果调到满意再写回文件里。5.3 调试工具与日志技巧H5游戏调试有一个很实用的工具组合。第一个是浏览器的开发者工具重点看Performance面板里的帧率曲线和Memory面板里的内存曲线。帧率曲线能直观看出掉帧的位置内存曲线如果呈阶梯状持续上升说明有对象泄漏。第二个是“可视化调试开关”。我在游戏里加了一个debug模式用键盘快捷键F2开启。开启后画布上会显示出所有对象的碰撞边界框用半透明红色的矩形覆盖在每个碰撞体上以及当前对象数量、帧率等实时数据。这个开关在定位碰撞检测问题时非常有用能直接看到检测用的矩形与实际画面是否对得上。第三个是“结构化日志”。给关键事件游戏启动、波次生成、玩家死亡、性能告警加上日志输出后缀带时间戳和对象数量。问题爆发的时候翻日志比断点调试更快。5.4 跨端兼容性一套代码到处跑的辛酸写H5游戏最大的吸引力是一套代码可以在浏览器、微信、App内WebView等多个环境运行。但“到处都能跑”和“到处都跑得好”是两回事。这次适配实测下来主流的Android微信内置浏览器和iPhone Safari是最常被用户使用的两个环境。Safari的Canvas渲染效率通常不错但AudioContext的自动播放政策严格。Android自带浏览器的Canvas性能参差不齐低端机上粒子特效一开就掉帧。针对这种碎片化我采用的经验是分级配置检测到设备性能一般通过帧率监控判断就自动降低粒子数量和特效层内容。这个“动态画质缩放”机制保证了低端机也能玩虽然画面朴素一点但至少流程不卡。6. 关于AI协作开发的长期思考这次项目做下来个人最大的感受是AI编程助手不是“会写代码的实习生”更像是“能力很强的结对编程搭子”它不会从零帮你做产品决策但能把你的思路快速变成代码。我的工作方式也随之改变了。以前是“先想清楚再写”现在变成“先想清楚再让AI写写完再仔细审”。整个流程中值钱的不是敲代码那部分而是思考判断的部分——判断需求怎么拆、模块接口怎么定义、AI给的代码质量怎么样、性能瓶颈在哪里。这些判断力才是一个成熟开发者的核心价值。如果你也想用AI辅助开发游戏我的建议是不要一开始就让它写整个项目从最小可运行的原型开始一次只推进一个模块。模块之间明确接口每个模块写完立即测试验证。把AI当成“加速器”而不是“驾驶员”。它在语法、API调用、常见算法实现上非常可靠但在架构设计、用户体验、性能优化、边界情况处理上还很需要人的把关。最后再分享一个实际使用的小技巧。AI编程助手在处理“带上下文的局部修改”时往往表现不佳。比如你在游戏已经跑通之后让AI增加一个子弹贯穿功能如果不给它看当前子弹模块的完整代码它可能会生成一个风格完全不同、与其他模块不兼容的版本。正确用法是把相关代码片段直接粘贴进提示词告诉它“保持现有代码风格和结构在xxx位置增加yyy功能”。给足了上下文AI在局部修改上的表现会好很多。这次H5游戏从零到能上线前后花了一周时间。AI编程助手大概帮我省去了40%的编码时间但它也带来了一个隐性成本——审查它生成的每一行代码需要额外花费时间。好在总体上省的时间远大于审查的时间这笔账还是划算的。后续如果再精进我可能会尝试加入关卡编辑器和无尽模式用AI跑一遍流程看看它在更复杂的场景下又能发挥到什么程度。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →