AI 说"写完了",但文件一个字没变—AI 游戏开发中那些离谱的 Bug

精华修改于03/2649 浏览开发心得
horizontal linehorizontal line

前言

最近用嗒啦啦做了一款叫「山海经·异兽录」的竖屏卡牌收集+轻策略放置游戏。25 个山海经异兽角色、50 个技能、16 套羁绊、完整的战斗系统和 UI,前后大概跑了几十轮对话。
整个过程中嗒啦啦帮我写了几千行 Lua 代码,效率确实高。但同时也踩了不少坑——有些是嗒啦啦的问题,有些是我自己的问题,还有些是"人机协作"本身带来的新型 Bug。
这篇文章记录了开发过程中遇到的 9 个真实 Bug,每个都附带诊断思路和修复方法。希望对同样在用嗒啦啦做游戏的朋友有点帮助。
horizontal linehorizontal line

Bug 1:嗒啦啦说"已完成",文件一个字没变

这是整个项目里最离谱的一个 Bug。
背景:我需要把 3 个数据文件(HeroData、BondData、SkillData)从旧 IP 重写为新 IP,每个文件几百到上千行。我让嗒啦啦同时启动了 3 个并行 Agent 来干这事。
现象:3 个 Agent 全部报告"已完成"。我一看文件——内容完全没变,还是旧数据。行数一样,关键词一样,一个字都没改。
根因:Agent 内部用了 Shell 的 heredoc 方式写大文件:
bash
cat > scripts/data/HeroData.lua << 'EOF'
... 几百行 Lua 代码 ...
EOF
这种写法在沙箱环境中处理包含特殊字符的大文件时,会静默失败——命令返回 exit code 0(成功),但文件内容根本没写进去。
怎么修的:换成专用的文件写入工具(类似 IDE 的"保存文件"功能),不走 Shell 管道。
教训
  永远不要信任"已完成"的状态报告。 每次写文件后必须验证:查行数、搜关键词、确认内容确实变了。"执行成功"和"结果正确"是两回事。
horizontal linehorizontal line

Bug 2:全局替换做了 14 个文件,最后 2 个漏了

背景:把项目从"斗罗大陆"IP 切换到"山海经",涉及大量术语替换(魂师→驭兽师、武魂→契兽、魂环→灵印……)。我把十几个文件分成 3 批,让 3 个 Agent 并行处理。
现象:3 个 Agent 都报告替换完成。最后做全量验证时:
bash
grep -rn "魂师\|武魂\|魂环\|魂骨" scripts/
两个文件还有旧术语——Icons.lua
里有"魂骨部位",HeroCard.lua
里有"魂师卡牌"。
根因:任务分配时按目录归类——"screens 目录给 Agent A"、"systems 目录给 Agent B"、"battle 目录给 Agent C"。但 Icons.lua
在 data 目录、HeroCard.lua
在 widgets 目录,三个 Agent 的分工范围都没覆盖到它们。
怎么修的:手动对这两个文件做定点替换,然后再跑一次全量扫描确认零残留。
教训
  批量替换的最后一步,必须对整个项目目录做全量 grep。 遗漏总是出现在你觉得"不可能有"的边缘文件里。分批处理可以加速,但全量验证不能省。
horizontal linehorizontal line

Bug 3:注释里的"彩蛋"

现象:术语全部替换完了,build 也通过了。但仔细一看 Constants.lua,发现注释里还残留着:
lua
SoulRingQualityName = { ... } -- 灵印品质名称(原魂环)
SoulBonePartName    = { ... } -- 神骸部位名称(原魂骨)
那个括号里的 (原魂环)
(原魂骨)
没被替换掉。
根因:替换规则只匹配独立词汇"魂环"→"灵印",但 (原魂环)
这种被括号包裹的形式不在匹配范围内。而且这种括号注释本身就不应该保留——既然已经改了名,就不需要再标注"原来叫什么"。
怎么修的:直接删除这些括号注释。
教训
  同一个术语会以各种变体出现:纯文本、括号包裹、字符串拼接、注释说明。替换时要考虑所有可能的上下文形式,或者干脆用正则表达式做模糊匹配。
horizontal linehorizontal line

Bug 4:羁绊显示全是空白

现象:羁绊(Bond)界面打开后,成员名字全部显示为空。
根因:BondData.lua
中的 members
字段存的是 heroId,UI 拿着这个 id 去 HeroData.lua
里查名字。问题是两个文件里的 id 对不上——BondData 写的是 "tang_san"
,但 HeroData 改版后 id 变成了 "zhu_long"
这本质上是个数据外键不一致的问题。如果是数据库,外键约束会直接报错;但在 Lua 的 table 里,查不到就返回 nil
,UI 拿到 nil 就显示空白,不报错也不崩溃——静默失败。
怎么修的
1. 从 BondData 提取所有 members 引用的 id
2. 从 HeroData 提取所有实际定义的 id
3. 交叉对比,找到不匹配的
4. 统一修正
教训
  多个数据文件之间有引用关系时,改一个就要检查所有引用它的文件。 这跟改数据库表结构一个道理——你改了主表的主键,所有外键引用都得同步更新。
horizontal linehorizontal line

Bug 5:grep 说没问题,其实是 grep 自己出了问题

现象:用这个命令验证是否还有旧术语残留:
bash
grep -c "魂师" file1.lua && grep -c "魂师" file2.lua && echo "检查完成"
命令执行到一半就断了,"检查完成"没打印出来。
根因:grep -c
在找到 0 个匹配时返回 exit code 1。在 bash 里,&&
表示"前一个命令成功才执行下一个"。所以当第一个文件确实没有残留(匹配数=0)时,grep 返回 1,bash 认为"命令失败了",整个链条中断。
讽刺的是——文件没问题,反而导致验证脚本报错
怎么修的
```bash
加  echo 0 兜底,确保命令链不中断
count=$(grep -c "魂师" file.lua  echo 0)
echo "残留数量: $count"
```
教训
  Shell 脚本里的退出码是个大坑。 很多命令在"正常但结果为空"时返回非零退出码。写验证脚本时,要假设每个命令都可能"失败",用  true
或  echo fallback
保底。
horizontal linehorizontal line

Bug 6:临时文件跨会话蒸发

背景:SkillData.lua 有 1200+ 行,一次写不完。嗒啦啦把它拆成两部分,第一部分直接写入目标文件,第二部分存到 /tmp/skilldata_part2.lua
,准备下一轮合并。
现象:下一轮对话开始时,/tmp/skilldata_part2.lua
不存在了。
根因:/tmp/
是临时目录,沙箱环境在会话之间会清理。嗒啦啦不知道(或者说"忘了")这个限制。
怎么修的:不再拆分,直接用专用工具一次写入完整文件。
教训
  永远不要用 /tmp 做跨会话的中间存储。 需要持久化的内容必须写到项目目录里。这个问题不只是嗒啦啦开发会遇到——任何依赖临时文件的工作流都要注意这一点。
horizontal linehorizontal line

Bug 7:LSP 警告满天飞,但代码能跑

现象:编辑器里一大片红色/黄色波浪线,看着很吓人。但代码实际运行没问题。
根因:Lua 是动态类型语言。变量声明时如果没赋值或赋了 nil
,LSP 不知道它是什么类型,后续访问成员时就报 undefined-field
警告。
```lua
-- LSP 不知道 scene 是什么,访问 CreateChild 时报警告
local scene = nil
scene:CreateChild("Node")  -- 黄色波浪线!
-- 加类型标注后 LSP 就认识了
---@type Scene
local scene = nil
scene:CreateChild("Node")  -- 正常,无警告
```
怎么修的:在关键变量声明处加 EmmyLua 类型标注。不需要给每个变量都加——只标注"类型源头"(赋值为 nil 或未赋值的变量),后续的类型推导会自动传递。
教训
  类型标注是给工具看的,也是给未来的自己看的。 花 10 秒加一行 ---@type XXX
,能省下后面反复确认"这个变量到底是什么"的时间。尤其在多轮对话中,类型标注能帮嗒啦啦更准确地理解你的代码意图。
horizontal linehorizontal line

Bug 8:卡牌边框千篇一律

你可能觉得这不算 Bug,但作为游戏体验来说确实是个问题。
现象:v1 版本的卡牌用了统一的金色边框模板。25 张卡看起来都一样,只是中间头像不同,没有辨识度。
根因:嗒啦啦生图时用了批量生成,所有卡牌共用一个 prompt 模板,只替换角色名。结果风格高度统一——太统一了。
怎么修的
改为单张调用生图,每张卡牌的 prompt 里加入:
角色属性特征(火/水/毒/光……)
阵营风格(昆仑仙气、幽冥暗黑、大荒狂野……)
角色性格(威严、妖媚、憨厚、冷酷……)
每次 3 张并行,分 9 轮完成 25 张。虽然慢了点,但每张都有自己的视觉特色。
教训
  嗒啦啦生图时,单独调用比批量调用质量更可控。 批量模式倾向于生成风格统一的结果,但游戏角色需要的恰恰是差异化。宁可多跑几轮,也要保证每张卡牌有辨识度。
horizontal linehorizontal line

Bug 9:鼠标左键判断用了数字 0

这个不是我项目里遇到的,但嗒啦啦写代码时经常犯。放在这里提醒大家。
现象:鼠标点击事件明明触发了,但 if button == 0
的判断就是不进去。
根因:很多开发者(和嗒啦啦)习惯从其他框架带来的经验——SDL 里左键是 0,Web 里 button=0 也是左键。但在 UrhoX 引擎里,鼠标按钮用的是枚举常量,MOUSEB_LEFT
的值不一定是 0。
```lua
-- 错:照搬其他框架的习惯
if button == 0 then ... end
-- 对:用引擎定义的枚举
if button == MOUSEB_LEFT then ... end
```
教训
  嗒啦啦写代码时会把不同框架的经验混在一起。 遇到输入判断、渲染模式等跟引擎强相关的代码,一定要用引擎自己的枚举常量,不要用数字。自己 review 的时候看到硬编码数字就要警觉。
horizontal linehorizontal line

总结:和嗒啦啦一起开发的 Debug 方法论

用嗒啦啦做了这个项目之后,我总结了一套"人机协作"场景下的 Debug 流程:
```
第一步:复现
  └── 截图/录屏 > 文字描述。给嗒啦啦看截图比说"不对"有用 10 倍。
第二步:定位
  └── 让嗒啦啦先读文件再改文件。不要让它凭记忆猜。
第三步:修复
  └── 小改用精确替换,大改用专用写入工具,别走 Shell 管道。
第四步:验证(最重要!)
  ├── grep -rn 全量扫描
  ├── wc -l 行数校验
  ├── build 编译测试
  └── 预览看实际效果
```
最重要的一条经验:
  嗒啦啦是个高效但粗心的队友。它写代码很快,但你得帮它做质检。 每次嗒啦啦说"完成了",你要做的不是说"好的下一个",而是跑一遍验证确认它真的完成了。
这跟带新人其实一个道理——能力不差,就是需要 code review。
项目:山海经·异兽录 | 引擎:UrhoX/星火编辑器 | 语言:Lua 5.4
如果你也在用嗒啦啦做游戏,欢迎评论区交流踩坑经验。
猜你想搜
taptap 制造嗒啦啦开发bug
3
2