AI辅助开发三个月:《镜海》核心玩法跑通与代码审查实战总结
直接开写。既然是一个系列记录的第07篇那就顺着一个独立开发者用AI辅助做游戏的日志感往下走。我把这篇定为“中期总结 单机核心玩法跑通的实战记录”侧重“AI生成代码后的审查、重构与整合成本”这比单纯吹AI多香更有参考价值。1. 项目进展总览从能跑 Demo 到能玩的核心循环先说当前进度。这已经是系列第07篇项目走到现在大约三个月我用 AI 编程的方式完成了《镜海》这个单人开发项目的核心玩法闭环。这是一个俯视角 roguelite 动作游戏美术资产来自 AI 生成加手动修图程序主体用 Godot 4.2 编写开发过程里大概 70% 的代码量是由 AI 辅助生成的——但这里要强调一下“AI 生成”和“AI 生成后能进游戏”是两码事后面会细讲。现阶段完成的内容包括基础移动与冲刺系统含碰撞体处理一套简单的近战攻击框架连击、受击反馈、硬直3 种敌人 AI近战追砍、远程射击、自爆追踪房间刷怪逻辑与波次控制本地存档系统JSON 序列化/反序列化简化的道具掉落与角色属性成长说句掏心窝的话如果按传统方式一个人从零写这个进度至少要翻一倍时间。但有 AI 不等于无脑复制粘贴后面几个章节我会把这个过程中的工具选型、实操链路和踩坑经历全部摊开讲包括一次把整个项目差点搞崩的回滚事故。2. 为什么最终选了 Godot AI 写代码的组合这个决定是在第 02 篇的时候反复权衡过的现在回头看选型基本是正确的。这里把对比逻辑完整梳理一遍给还在犹豫的人一个参考。2.1 引擎选型背后的考量我最初的备选方案有三个Unity、Godot、纯 Web 技术栈。每个方案都有明显的优势和短板。Unity 的生态是最成熟的Asset Store 资源多教程多遇到问题能搜到大量现成答案。但它的编辑器相对臃肿启动慢版本更新频繁而且对于纯程序写代码的人来说Unity 的组件化思维和 C# 的序列化机制需要额外适应。最让我犹豫的一点是Unity 的 UI 系统和资源管理对我这种“代码优先”的开发者来说学习成本并不低。Godot 则相反它整个设计就是围绕“轻量 灵活”来的。场景树结构非常直观GDScript 的语法接近 PythonAI 生成质量很高——这一点至关重要。因为在 AI 辅助开发的模式下语言越贴近主流训练数据AI 生成代码的准确率就越高。GDScript 虽然是小众语言但因为它语法简单、结构清晰大模型理解起来几乎没有障碍。Web 技术栈比如用 Phaser 或 PixiJS TypeScript我也认真考虑过优势是发布路径最短浏览器打开就能玩适合快速验证玩法。但对动作游戏来说性能上限和维护成本都差点意思尤其后期如果要加复杂的粒子效果和物理模拟纯 Web 的方案会让人想骂人。2.2 AI 编程工具在 Godot 工作流里的适配性选 Godot 还有个非常实际的考量它对机器配置的要求低。我的开发机是几年前的笔记本跑 Unity 风扇狂转但跑 Godot 加几个工具面板完全无压力。对一个单人开发者来说开发体验的顺滑度直接决定项目能坚持多久。工具链方面我现在的搭配是用途工具选择理由主编辑器Godot 4.2轻量、场景树直观、GDScript 语法贴近 Python 便于 AI 生成AI 编程助手Cursor Copilot 双修Cursor 擅长多文件重构Copilot 在单文件补全上反应更快代码审查手动 AI 自查关键逻辑必须肉眼审查AI 自查只能当参考美术生成Stable Diffusion 本地部署资源可控、可批量生成配合手动修图音效免费素材库 简单合成独立开发阶段没必要死磕原创音效这里有个经验之谈AI 代码生成工具对 GDScript 的支持程度普遍不如 Python、TypeScript 这些热门语言但即便如此在实际生成质量上GDScript 的表现依然远超预期。原因也很简单——GDScript 简单没有复杂的泛型、没有接口继承的层层嵌套AI 不需要理解太多上下文就能给出合理的函数实现。对 AI 编程来说代码语言的复杂度本身就是一种障碍GDScript 恰好把这道障碍降到了很低。3. 从 AI 生成到进游戏我的标准化接入流程这部分是整个开发模式的核心。AI 不是给你一段代码就完事了你需要一套流程来保证生成的代码能安全地融入现有项目。我在踩了无数次坑之后总结出了下面五步流程现在每一步都严格执行。3.1 需求拆解让 AI 只干一件具体的事很多人让 AI 写代码失败问题出在需求描述太模糊。比如“帮我写一个敌人 AI”这句话给到 AI它大概率会生成一个很泛化的基类然后你的项目结构还得为它重新适配极其痛苦。我的做法是把需求拆到足够小“在 attack 状态下敌人需要先停顿 0.3 秒然后朝玩家当前位置发起直线冲刺冲刺速度为 400持续 0.5 秒结束后进入 cooldown 状态。”这样一段描述AI 返回的代码基本就是可以直接使用的。一段能用的 AI 提示词闭着眼睛也能看出来区别# 推荐写法 在 _attack_state 函数中 1. 先获取玩家全局坐标记录为冲锋目标点 2. 敌人播放攻击前摇动画持续 0.3 秒 3. 前摇结束后敌人向目标点方向以 400 像素/秒的速度移动 4. 若距离目标点小于 10 像素或移动时间超过 0.5 秒切换到 cooldown 状态 5. 冲锋期间碰到玩家调用玩家受伤函数并结束冲锋这样给 AI 描述它返回的是一段可实现的功能函数而不是一个需要你重新设计接口的抽象框架。3.2 代码审查最不能跳过的环节AI 生成的代码我给自己定的规矩是不审查不上线。哪怕是一行简单的数学计算也要过一遍眼睛。这不是信不过 AI而是信不过自己的需求描述——有时候你描述不清AI 理解偏了生成的东西表面上逻辑通顺实际上完全不是你想要的行为。审查的重点有三块一是变量作用域二是有没有直接修改不该动的全局状态三是物理体运动处理是不是放在_physics_process里而不是_process。第三点是 Godot 新人最容易犯的错AI 也经常犯错但只要你审查一遍基本就能揪出来。3.3 隔离测试用最小场景验证行为新代码不会直接放进主场景。我在项目里单独建了一个test_combat.tscn场景里面只放一个玩家、一个敌人、一块平地。每次 AI 写完新的战斗逻辑我就在这里跑一遍观察动画切换、伤害数值、硬直时间是否合理。这个习惯帮我规避了大量“改了 A 导致 B 崩了”的问题。因为主场景里元素太多出了 bug 很难定位但最小场景里只有两三个节点出问题一眼就能看出来。等行为正常了再把这套逻辑挪回主场景用真实环境再做一轮验证。以下是我排查一个实例时的思路后面会讲到具体 bug测试目标近战攻击第一段能否正常命中敌人 测试步骤 1. 玩家靠近敌人至攻击范围内 2. 按下攻击键 3. 观察动画状态机是否切换到 attack_1 4. 观察伤害判定区域是否激活 5. 观察敌人是否收到受伤信号并扣除血条3.4 整合进主场景处理兼容性问题代码在小场景跑通不代表万事大吉。主场景里有摄像机跟随、房间切换、道具生成等复杂逻辑新加的战斗代码很可能和这些系统产生交互最常见的问题是节点生命周期不同步——比如攻击判定的 Area2D 节点在场景切换后被提前释放导致后续攻击失效。这类兼容性问题很难靠 AI 解决因为 AI 看不到你项目里所有系统的交互。它只能基于你贴给它的代码和描述来推断但这种依赖运行时的生命周期问题必须靠开发者在真实环境里跑过才能暴露出来。3.5 持续重构AI 代码的长期维护成本AI 另一个常被忽视的问题是代码风格不一致。它今天给你写的函数叫handle_player_attack明天可能就叫player_attack_execute如果你放任不管很快项目里就会出现大量重复逻辑和命名混乱。我的策略是每天花 30 分钟做代码清理统一函数命名风格、把重复代码抽成公共函数、给关键函数写注释。AI 能写代码但代码结构的演进和维护还是得靠人脑。把这一步当成晨间仪式坚持下来项目结构会越来越清爽AI 后续生成的代码质量也会跟着提高——因为上下文更清晰了。4. 一场差点毁掉存档系统的合并冲突事故这一章专门讲一次严重的踩坑经历。那一天我本来计划完成“拾取道具 属性成长”的联动功能结果折腾到半夜差点把已经做完的存档系统回滚掉。4.1 事故起因两个 AI 会话的并行开发我在 Cursor 里同时开了两个会话一个在写“道具拾取逻辑”另一个在重构“角色属性管理器”。按理说这是两个不相关的模块但问题出在两者都碰了player.gd这个文件——一个在读写属性更新的代码一个在往拾取函数里加入属性变更的调用。当时我犯了个低级错误两个会话都直接保存到了player.gd没有用分支管理。等我把第二个会话的结果确认保存之后第一个会话对文件的修改被直接覆盖了。表面上看着文件内容完整但运行时立刻报错——属性管理器里多了一个null引用。4.2 排查链路从报错到根因报错信息是Attempt to call function add_bonus in base null instance on a null instance.这个报错指向属性管理器调用了一个空引用但实际原因并不是那个属性管理器为空而是我在合并两个版本时把player.gd里初始化属性管理器的代码给弄丢了。合并冲突的典型特征就是——代码结构对不上但很难直接看出哪里丢了。排查过程如下查看报错行号定位到add_bonus调用位置。向上回溯检查该调用所在函数是否从正常路径进入——确实是。检查onready初始化的属性管理器节点是否存在——存在。打印该节点是否为null——是null。检查onready初始化语句是否被意外删除——确实没了。整个过程耗时约 40 分钟期间我已经动了重写存档系统的念头。最后确认是文件覆盖问题后解决办法反而简单从版本控制里把前一次提交的player.gd找出来对比缺失部分然后手动补上。4.3 这次事故带来的流程修正这次事故之后我做了一个硬性规定同一时刻只允许一个 AI 会话修改同一个文件。如果确需并行修改必须先把分支摘出来等各自完成后再手动合并。AI 编程不是人多力量大多个会话同时改同一文件就会出现这种人在多个 AI 之间互相打架的窘境。另一个修正是我把所有 AI 生成的代码都纳入 Git 管理。以前我总觉得自己写的项目不需要那么规范的版本控制但这次事故让我明白AI 生成代码的项目比手写代码更需要版本控制——因为你永远不知道 AI 在哪个环节做了什么微妙的改动。5. 核心玩法系统实现从设计到 AI 落地的战斗模块拆解战斗模块是整个游戏的重心也是 AI 辅助开发收益最明显的部分。这里把近战攻击系统和敌人 AI 状态机的实现过程做一次完整的复盘。5.1 近战攻击判定Area2D 加动画信号近战攻击的第一版实现用的是传统的“检测输入 - 播放动画 - 激活碰撞区域”模式。整体分为三步玩家按下攻击键进入攻击状态锁定移动。播放攻击动画在动画中特定帧通过动画信号激活攻击判定区域。判定区域内的敌人收到伤害进入受击硬直。最大的坑在于攻击判定区域的启用时机和时长控制。如果用固定时间来控制会出现动画还没播到打击帧就命中敌人的违和感。正确的做法是通过AnimationPlayer发出信号在打击帧那一瞬间启用 Area2D 的monitoring然后在下一帧关闭。AI 在这种场景下非常给力。我只需要描述清楚“攻击动画第 5 帧开始激活判定第 8 帧关闭判定”它就能给出精确的时间线控制代码。这里的关键是你需要懂一点动画帧率的概念——编辑器里动画时间轴的默认帧率是 60 FPS你要算好第几帧对应多少秒才能准确告诉 AI 时间参数。5.2 敌人状态机用枚举加 Match 的朴素实现敌人 AI 我碰到的第一版 AI 生成代码用的是类状态模式——每个状态一个类通过继承实现多态。听起来很高级但实际跑起来问题很大敌人逻辑本身不复杂这种架构反而让代码变得绕来绕去不直观还难调参。于是我直接把架构推倒重来用最朴素的enum match实现。每个敌人类里定义一个状态枚举然后在_physics_process里根据当前状态调用不同的处理逻辑enum State { IDLE, CHASE, ATTACK, BACKOFF } var state: State State.IDLE func _physics_process(delta: float) - void: match state: State.IDLE: _handle_idle(delta) State.CHASE: _handle_chase(delta) State.ATTACK: _handle_attack(delta) State.BACKOFF: _handle_backoff(delta)这种实现方式有极其明显的优势状态一目了然单步调试方便给 AI 描述某个状态的具体行为时也非常直接。比如自爆敌人的_handle_chase只需要描述“追踪玩家速度 150距离小于 30 开始爆炸前摇”就行AI 写出来基本不用改。5.3 刷怪波次的调度硬编码脚本与场景生成对比刷怪系统最初用硬编码脚本每个波次写死刷哪些怪。后来发现这个方式在数值调整时非常痛苦每次改敌人数量都要开代码编辑器。我后来让 AI 生成了一套基于“波次模板”的数据驱动刷怪系统。波次模板是一个 JSON 文件里面定义了每一波的敌人构成、生成间隔和总时长。程序读取模板后在对应时间点把对应敌人实例化到场景中。这样做的好处是调平衡不需要碰代码直接改配置文件即可。AI 在处理这种数据驱动逻辑的时候表现尤其出色因为模板本身结构清晰、约定明确生成的解析和调度代码几乎不需要调整。以下是一段简化的宝箱配置{ waves: [ { id: 1, enemies: [ { type: runner, count: 3, spawn_interval: 1.5 }, { type: shooter, count: 1, spawn_interval: 3.0 } ] }, { id: 2, enemies: [ { type: exploder, count: 2, spawn_interval: 2.0 }, { type: runner, count: 4, spawn_interval: 1.0 } ] } ] }6. 美术和动画环节的 AI 工作流无美术基础也能撑住场面做游戏不只有代码美术资源对单人开发者来说往往是比代码更头疼的问题。这一章分享一下我现在的 AI 美术资产生产管线。6.1 角色与怪物建模Stable Diffusion 的本地部署与微调我本地部署了 Stable Diffusion WebUI通过 ControlNet 保证一批素材的风格统一。说实话纯靠 SD 生成的素材直接丢进游戏会觉得“AI 味”很重——边缘发虚、细节不统一、风格偏移明显。后来我用 LoRA 微调了一个自己的画风模型用 20 来张自己精心挑选的参考图做训练出来的素材基本能满足需求但依然需要人工修边、抠图、补细节。做像素风的话会简单很多——像素画的元素更规整AI 出错也不明显手动调整成本低。如果完全没有美术基础我强烈建议先做像素风。6.2 动画状态连接从序列帧到 Godot 动画树我目前的角色动画用的是 Spritesheet 序列帧AI 生成连续动作的序列帧效果很不稳定所以我的做法是分两个环节先生成单个关键姿势再用代码做简单的位移和旋转模拟动画过渡。这不是最好的方案但对单人开发来说足够撑住玩法的验证阶段。等玩法完全定下来再考虑找人补做更精细的动画或者用 Godot 的骨骼动画系统做更加顺畅的动作表现。现阶段的核心目标是让游戏“能玩”而不是“好看”。6.3 程序化生成减少美术依赖的新思路鉴于美术资源的难度我也在尝试程序化生成场景地板砖块纹理用代码生成粒子效果靠 Godot 内置粒子系统调整参数。这部分的收益非常大——我写的瓦片生成脚本可以从一套噪声函数生成几十种不同的地面样式省掉大量手动绘制时间。这一点也是 AI 编程能发挥价值的地方——你不需要懂得复杂的图形学公式只要把需求描述得足够清楚。比如“我想生成一张 32x32 的小地图地面使用 tile_noise 函数基于 Perlin 噪声来决定铺哪种砖”AI 会帮你完成剩下的实现。7. 独立开发中的效率管理AI 帮助不了你的那些事AI 确实拉高了写代码的效率天花板但项目推进还取决于很多 AI 帮不了的事这一章聊几个我用真金白银换来的经验。7.1 任务粒度与心理激励一个人做游戏最大的敌人不是技术是倦怠。我给自己的节奏是每天至少完成一个“看得见”的小目标比如“给敌人加一个受伤闪白效果”或者“修改冲刺冷却时间到 0.8 秒”。这类小目标的成就感维持了我持续上手的动力。而“做完整个关卡”这种大目标拆解成十来个可以当天完成的小任务逐项打勾才能让动力不中断。AI 对拆解任务也有帮助——你告诉它“我想做 BOSS 战但先聚焦它的范围和弹幕”它会帮你把实现步骤列出这样再逐一落地就轻松了。7.2 时间记录与改进哪些环节被 AI 真的压缩了我记了一个多月的开发时间账发现时间占比大概是需求描述和 Prompt 编写15%AI 生成代码后的审查与测试45%Debug 和兼容性修复25%逻辑设计与其他15%有意思的是你花在给 AI 写描述上的时间其实是从代码编写时间里挪过去的。整体来看代码书写时间从原来的 50% 降到了不到 10%但审查和测试的时间大幅增加。这两者一抵消实际节省的时间大概在 30% 到 40% 左右并不是很多人想象的“一晚上做完一个游戏”。从产品角度这 30% 到 40% 的时间节省已经非常可贵。但对单纯想来“用 AI 偷懒”的人我要泼一盆冷水AI 不会降低游戏开发的复杂度和思考门槛它只是加速了你写出正确代码的过程。7.3 关于“AI 真的能独立做一个游戏吗”的回答这个问题我经常被问到。我的回答是能但前提是“你”得先独立做过游戏。AI 是杠杆它放大的是你本身的能力和判断力。如果你不懂游戏设计不懂程序结构不懂动画状态机AI 生成的代码再多也拼不出一个能玩的游戏——因为你无法判断它生成的到底对不对、好不好。所以对想入行的新人我的建议是先手写一个小游戏练手不用 AI 辅助把整个流程走一遍。之后再用 AI 提效你才分得清 AI 给的答案里哪些是精华、哪些是垃圾。没有判断力之前AI 编程带来的可能不是提效而是灾难。8. 当前版本的问题清单与下一阶段计划这个部分按惯例记录当前版本的已知问题和下一步规划。方便后面回看也方便想知道具体踩坑细节的人参考。8.1 已知问题问题描述严重程度原因分析解决方向自爆敌人有时候会在玩家身后突然转向中追踪逻辑只在固定间隔更新方向改为每帧更新方向但加入最大偏转角限制手柄输入在 UI 界面部分按钮无法操作低Godot 内置的焦点系统对手柄支持不完整手动实现焦点控制某些场景切换时有轻微卡顿中地图加载没有预加载完全依赖运行时加载增加预加载资源队列敌人卡在墙角抖动高碰撞体和导航网格碰撞半径设置不合理调整导航半径与 TileMap 碰撞层8.2 下一步规划接下来一个月的主要目标是把 UI 系统完善掉包括主菜单、暂停菜单、背包界面、血量与状态显示。这部分 UI 开发工作很适合用 AI 来做——Godot 的 UI 系统是基于 Control 节点的代码与场景之间的映射关系比较明确描述清楚就能生成大半。再往下是 BOSS 战的设计。我准备用前面 5.2 节提到的状态机逻辑做一个三阶段的 BOSS每个阶段切换时加全屏弹幕过渡。这个功能可能要多花几天做测试与特效调整我会在后续系列里逐步分享。还有一个我在试的新方向使用 AI 生成音效。相比代码生成音效生成的门槛在于可控性差但你只需要“听起来像那么回事”的临时音效完全够用。正式音效等游戏做完之后再决定是自己磨还是找人合作。9. 我个人对 AI 编程式开发的阶段小结写到这里已经很长了最后做一个非总结性的阶段收尾吧——我更愿意把它称为一段亲身经历的推荐。如果你正在犹豫“要不要用 AI 来做自己的游戏”我给的建议分为三种情况第一种你完全没写过代码也没有游戏开发经验。我的建议是暂时不要依赖 AI。先用最简单的方式跟着网上的 Godot 入门教程做一两个小项目哪怕是照抄也好。这个过程不是学语法而是建立一种“程序应该如何被组织”的直觉。有了这种直觉AI 对你来说就是如虎添翼没有这种直觉AI 给的东西你会完全无从下手。第二种你有一定开发经验但对游戏开发不熟。这个状态最适合用 AI 来跨门槛。你懂代码结构但不懂 Godot 的 API 和节点系统AI 能帮你把这类“查文档”的时间压缩到接近零。你的任务只剩下一件事把游戏逻辑拆成清晰的文字描述然后审查 AI 产出的每一段代码。第三种你有游戏开发经验但正在被单人开发的体力活压垮。AI 会帮你极大减轻打字负担让你把精力集中到设计和调试上。AI 编程的定位是一场工作方式层面的工具升级它的使用体验更接近“一艘带自动导航的帆船”——你依然要知道方向在哪但它能省去你划桨的力气。从第 01 篇到现在这个项目的代码量大概在 9000 行左右其中 AI 生成的占比超过七成。在 AI 逐步能自动尝试解决问题的前提下整个项目离我想要的样子依然还有不小的距离但至少我看到了一条可行的路并且还在继续往前推进。如果你也在做类似的事欢迎带着你的问题来交流。下一篇我会具体拆解 BOSS 战的设计文档以及 AI 如何参与到游戏平衡性调参里来。