AI应用报告
维度案例AI 价值(重写)
Web/Desktop 双端存档WebSaveAdapter deep swap + mirror seeding高——人类踩的坑:写死 res:// 路径,Web 端 0 写入
并发 race conditionBGM session id、page_gift await input 边沿高——人类写协程用全局布尔标志 = 必踩 race
状态机边界管理cutscene_trigger 0~8 全套切换中高——AI 强项:把状态空间画完整再写代码
持久化数据迁移axe_count→tool_count、cell_to_inventory_id、tile 复活高——人类漏一个字段迁移 = 老存档全废
复杂 bug 根因分析floor 模型陷阱、tellable TileSet 绕开高——AI 强项:诊断”模型设计缺陷”
跨子系统重构Breakable 通用抽象替代特例方法中——人类能做但容易漏特例

详细说明:每个维度 AI 怎么做到的

1. Web/Desktop 双端存档 — 高

人类怎么写错的

  • 存档路径写死 res://xxx.tres,Web 端 OS.file system 根本写不进去,console 一片红
  • Resource 子属性(如 BagResource 里的 Inventory 子对象)只换了外层,bag_item.current 引用还指向旧 bag → 玩家 transfer 物品,刷新页面物品回去

AI 的做法(三步走):

  1. 抽象”运行时资源路径”层WebSaveAdapter.runtime_path(original_path)res://xxx.tres 翻译成 user://runtime_state/<slot>/<basename>,上层代码不用关心平台
  2. cold-start 镜像机制SEED_RUNTIME_PATHS 列 31 个核心 .tres,cold-start 拷贝到 user://,只 seed 一次——后续操作全在镜像版上做
  3. deep swapswap_to_runtime_deep() 递归把 Resource 子属性切到镜像版;然后主动补 _relink_inventory_after_swap() 把 BagItem/HoldItem.current 字典引用按 inventory.id 重新指向新 bag

关键洞察:人类写存档时不会主动思考”子对象引用还在原对象”这种二阶问题——AI 会因为见过 deep copy / shallow copy 的坑,主动 grep 所有引用点统一迁移。


2. 并发 race condition — 高

人类怎么写错的

  • is_back_audio_running = true 当协程退出条件 → 旧协程 await timer 30 秒醒来时,新协程已经把 is_back_audio_running 重设 True → 旧协程继续跑 → 两个协程抢同一个 back_audio_player → BGM 一首没播完就硬切
  • await Input.is_action_just_pressed("left_mouse")tween.finished 回调里 → 玩家刚才点的 left_mouse.just_pressed 边沿还没被消费 → 协程第一帧就命中 → 立刻关掉后续 tips

AI 的做法(两个暗坑分别破):

  • session id 替代布尔标志:每次 play_level_audio_by_name / pause_bgm_immediately 自增 _bgm_session_id;协程每次 await 后检查 if _bgm_session_id != session_id: return 退出。关键洞见:Godot 4 的 AudioStreamPlayer.stop() 不 emit finished(不像 play()),所以协程 await finished 永远不会被唤醒——必须靠 session id 兜底
  • await process_frame 吞边沿:在 await Input 之前先 await get_tree().process_frame,让那一帧的 just_pressed 边沿在 _process 阶段被消费掉,下一帧再开始监听

关键洞察:人类写协程时不会预演”两个协程抢同一资源”的 timeline——AI 善于画时序图,先识别状态机边界再写守卫。


3. 状态机边界管理 — 中高

人类怎么写错的

  • sprint 门禁写成 if cutscene_trigger == 7: allow_sprint()——只在 trigger 7 允许跑步,但剧情流是 trigger 0~8 共 9 个状态,trigger 8/未来 9/10 全部被卡死
  • 状态转移条件漏写:trigger 5→6 转换只写了 “pick 按下触发”,没考虑 “sleep area body_entered + Sleep 动画播完 + 日期+1 + 7:00 reset + load_level” 全部都得按顺序完成

AI 的做法

  1. 先画完整状态转移表:trigger 0~8 共 9 个状态,每个转移的 owner(在哪个文件)+ 时机(什么时候触发)+ 副作用(emit 什么、改什么字段)
  2. 用 owner map 表格化:8 个 trigger 转移 → 8 行表,把所有触发点列全。后续任何 trigger 改动先查这张表
  3. 门禁反推:从”哪些 trigger 该允许 sprint”反推出门禁条件是 cutscene_trigger > 6(不是 == 7)——AI 不会把门禁写死成单值

关键洞察:人类写状态机习惯按当前需求写当前条件——AI 强项是把整个状态空间列全,画清楚每个边界。


4. 持久化数据迁移 — 高

人类怎么写错的

  • 改 schema 时直接 rename axe_counttool_count,老存档加载时 push_error → 所有砍树进度归零
  • 加新字段时不写默认值,老存档没字段就崩

AI 的做法(三大套路):

  1. 字段迁移(_ready 守卫)if current.has("axe_count") and not current.has("tool_count"): current["tool_count"] = current["axe_count"]; current.erase("axe_count")——运行时一次性迁移
  2. 反查表(hard-coded const)_INVENTORY_ID_TO_FILENAME 92 项,不靠自动扫目录——硬编码确保 reload 时不会漏。漏登记 → push_warning + skip → 表现是”新放能砍、存档重载砍不动”
  3. 持久化视图与内存视图同构BuildingResource.tellable_broken_cellsLevel.tellable_broken_cells 用同一个 dict 结构,reload 时按 layer_key 反向 erase_cell 还原

关键洞察:人类改 .tres schema 时只想着新代码怎么写——AI 强项是先想”老存档加载时会发生什么”,每个字段都加兼容守卫。


5. 复杂 bug 根因分析 — 高

人类怎么诊断错的

  • 看到”吃东西没反应”会调 console 查报错,但 floor 模型陷阱不报错——玩家视觉看到能量条 12.5% 觉得”快没电”,右键 pumpkin 却被守卫拦掉(物品不扣,电流不动)
  • TileSet farmland=true 标记的 cell 才能锄——但 Afforest 装饰 tile 不是 farmland。人类会去改 TileSet(侵入美术资产),改完整个 outdoor 场景的 tile 都要重新画

AI 的诊断法(两步破):

floor 模型陷阱

  1. 列出所有边界条件组合:(floor=0, current=0) / (floor=0, current=ceiling) / (floor=35, current=5) / (floor=40, current=0)(累瘫)
  2. 逐个跑守卫 if current >= ceiling: return 看哪个失败 → 发现 floor=35/current=5 失败
  3. 改成”预测算法”:future_current = min(future_ceiling, current + delta)if future_current <= current: return——能涨就吃

TileSet 绕开

  1. 不去改 TileSet(侵入美术)
  2. 引入新机制外层绕开:metadata/tellable + BreakableTellable + tellable_broken_cells 持久化
  3. get_current_tile_can_hoe 第一优先遍历 tellable 层,绕过 TileSet 自定义数据直接判定

关键洞察:AI 强项不是”看代码”——是先列 invariant(不变量),再列守卫,最后列边界。人类看 bug 看症状,AI 看 bug 看模型。


6. 跨子系统重构 — 中

人类怎么写错的

  • axe 砍 fence 走 axe_fence(),砍建筑走 can_axe_fence,砍宝箱走 chest 自己的 break()——每个特例要在 Level 加方法、一段 if-else、一份配置表
  • 加新可砍物品要改 4~5 个文件

AI 的做法(三条铁律):

  1. 配置表集中化:3 张 const 全在 Level 顶部 200 行——BREAKABLE_SCRIPT_BY_INVENTORY_ID(4 项特例)、BREAKABLE_HITS_NEEDED_BY_INVENTORY_ID(3 项特例)、_INVENTORY_ID_TO_FILENAME(92 项)。加新特例改表不动资源
  2. 保留旧接口为 deprecated aliasaxe_fence / to_axe / fence_axe_count 都保留只为 “unknown method” 误报兜底——不是为了兼容,是为了 grep 时能找到所有旧用法
  3. 接口名对齐抽象意图to_axe()to_tool() 而非保留 to_axe——因为未来 hammer / pickaxe 不该叫 to_hammer(),应该统一叫 to_tool() 参数化

关键洞察:人类重构时图省事保留旧接口——AI 强项是坚持”接口名必须反映抽象意图”,即使保留 deprecated 也要把主接口名改对。


横向洞察:AI 在这 6 个维度的共同特质

AI 特质在哪个维度表现最明显
画时序图 / 状态图再写代码并发 race、状态机边界
列 invariant 再写守卫持久化迁移、bug 根因
配置表集中化跨子系统重构、双端存档
保留 deprecated + 改主接口名跨子系统重构、接口契约
先想”老代码/老存档会怎样”持久化迁移、双端存档
绕过而非侵入bug 根因(TileSet 绕开)、重构(hold_effect 抽象)

一句话总结:AI 在这个项目里不是”写得快”,是”先想全再动手”——状态机先画完、invariant 先列完、老代码先扫完,才动手写。这样写出来的代码,每个守卫、每个反查表、每个 deprecated alias 都不是事后补丁,是设计意图的直接体现

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇