Files
Genarrative/docs/experience/CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

263 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Codex 实战经验沉淀
日期:`2026-03-24`
## 1. 先判断需求属于哪条链路
这个项目几乎所有需求都不是单点 UI 改动,通常会同时影响几条链路:
- 叙事链路AI 剧情、提示词、选项文案
- 状态链路:`GameState`、NPC 状态、背包、队伍、委托
- 演出链路:屏幕外进场、战斗播放、实体站位、动画与特效
- 工具链路编辑器、校验脚本、存档兼容、smoke
经验:
- 每次动手前先判断“这次主要影响哪几条链路”,不要把需求误判成单纯的 UI 需求。
- 只改表现、不改状态,最终一定会返工。
- 只改状态、不补校验,后面也一定会返工。
## 2. 先补状态模型,再补交互
这类项目里,最稳定的顺序永远是:
1. 先补类型与状态字段
2. 再补规则函数
3. 再补 hook 流程
4. 最后接 UI
已经反复验证有效的例子:
- `quests`
- `playerEquipment`
- `playerCurrency`
- `roster`
- `companions`
- `currentNpcBattleMode`
- `sparReturnEncounter`
经验:
- 如果一个功能需要“长期留存”,就不要只存在于局部组件 state。
- 先建模后接按钮,比先堆按钮后补状态稳定得多。
## 3. AI 负责叙事,本地负责规则
项目里最稳的边界是:
- AI 负责:
- `storyText`
- 对话文本
- 选项的自然语言表达
- 本地规则负责:
- 交易是否合法
- 招募是否成立
- 装备是否生效
- 委托进度是否完成
- 掉落、货币、好感、队伍编成
经验:
- 只要涉及数值、资源、状态迁移,就尽量不要让大模型即兴决定。
- 给模型的提示词应该描述“局面”和“边界”,不要让它代替规则系统。
## 4. 固定选项提示不要写得太死
一个重要经验是:
- 模型需要知道每个 `functionId` 的核心含义
- 但不需要看到“当前默认文案 / 补充信息 / 实际行为”这种过于细碎、强绑定的结构
更好的方式是:
- 保留 `functionId`、数量、顺序
- 只提供“这个 function 的核心语义参考”
- 让模型重写更自然、连续的 `actionText`
经验:
- 提示词越像表单,模型越容易产出僵硬选项。
- 提示词越像“约束 + 语义边界”,剧情越自然。
## 5. 面前实体的提示词必须和主角对称
如果主角有:
- 描述
- 性格
- 状态
- 属性
那么“当前面前实体”也应该尽量有同样结构。
经验:
- 只给一句“某 NPC 在这里活动”太粗,会削弱模型对局面的把握。
- 面前实体和主角描述层级一致后,模型更容易写出有来回感、对称感的叙事。
- 对生命/灵力这类状态,离散分档文本比裸数字更利于模型理解。
## 6. 屏幕外进场一定要抽成统一工具
这类项目很容易在多个地方各写一套“从屏幕外进入”的逻辑,结果出现:
- 同一实体重复进场
- 先进入屏幕,又被拉回屏幕外再进一次
- 多怪时只动第一个,其他直接跳终点
这次稳定下来的方法是:
- 抽统一的过渡工具
- `buildEncounterEntryState`
- `buildEncounterTransitionState`
- `interpolateEncounterTransitionState`
- 所有进场逻辑都复用这一套
- 区分两种场景:
- 真正从屏幕外冲入
- 已经在屏内预览,只是收敛到正式位置
经验:
- “屏幕外进入”本质上不是动画问题,而是状态过渡问题。
- 一旦同时存在 preview / call_out / resolve / replay就必须统一插值逻辑。
## 7. 多实体系统里,不要默认“只处理第一个”
这个坑非常常见:
- 场景配置里有多个怪
- 运行时逻辑却只用 `monsterIds[0]`
- 或者动画只插值 `sceneMonsters[0]`
经验:
- 只要数据结构已经允许数组,就尽量按“整组”处理。
- 即便最后 UI 只重点展示一个,也不要在底层逻辑里偷偷退化成单体。
## 8. 招募系统不要只做“当前队伍”
如果只有 `companions` 而没有 `roster`,后面一定会遇到这些问题:
- 队伍满员时必须强制覆盖旧同伴
- 已招募角色很难转成待命
- 营地/编队功能没法闭环
这次稳定下来的模型是:
- `companions`:当前上阵
- `roster`:已招募但待命
经验:
- 只要项目里有招募,迟早就要有 roster。
- roster 不只是 UI 功能,而是状态层能力。
## 9. 装备系统不要只做展示
装备真正形成闭环,至少要同时做到:
- 背包里可装备 / 卸下
- 装备改变真实属性
- 战斗行为读取装备加成
- 存档兼容旧存档
经验:
- “装备栏能显示”不算完成。
- 只有真正影响 `maxHp / maxMana / damage / incomingDamage`,它才是玩法系统。
## 10. 交易系统最好统一成货币价值模型
一开始按“品质交换”虽然简单,但很快会遇到问题:
- 不同类别物品难比较
- 直接购买不好接
- 后面加入售卖、任务赏金、宝藏货币时会冲突
更稳定的做法是:
- 所有物品都有统一价值
- NPC 商品有购买价
- 玩家物品有回收价
- 货币作为通用交换媒介
经验:
- 一旦出现货币,就尽量让交易系统全面切成“价值模型”。
- 不要同时长期维护“品质交换”和“货币购买”两套完全不同的判定逻辑。
## 11. 编辑器要先做保存前校验
编辑器进入“可持续生产内容”阶段后,最优先的不是视觉,而是:
- 保存前校验
- 非法引用提示
- 数值边界检查
经验:
- 编辑器最怕的不是“不够漂亮”,而是“保存成功但运行时报错”。
- 只要内容开始增多,校验脚本和保存前校验就必须尽早接入。
## 12. 每次大改后都要补 smoke
对这个项目来说,`lint + build` 不够。
至少要补 smoke 的场景包括:
- 委托接取 -> 推进 -> 交付
- 多怪遭遇
- 装备加成
- 队伍编成
- 交易价值与直接购买
- 进场插值
经验:
- 只靠人工点点看,很容易漏掉状态分支。
- smoke 不一定要重,但要覆盖关键闭环。
## 13. 兼容旧存档要同步做
每次给 `GameState` 新增字段时,都要同步考虑:
- 默认值
- 存档读取兼容
- 旧字段缺失时如何补全
经验:
- 旧存档兼容不是“最后再说”的工作。
- 新字段一旦进 `GameState`,就应当同一轮把持久化兼容补上。
## 14. 不要在坏文件上无限缝补
这次实际踩到过一个很明显的坑:
- 某些文件本身已经混入大量乱码或结构损坏
- 在原地做小 patch 会越来越难维护
更稳的做法是:
- 保留接口
- 整文件重写成干净版本
- 再接回现有调用
经验:
- 当一个文件已经进入“局部 patch 很难保证正确”的状态时,重写往往比继续缝补更省时间。
## 15. 后续继续迭代时的建议顺序
如果继续推进这个项目,建议优先顺序:
1. 先清理中文乱码高频文件
2. 再继续精英/Boss 层
3. 再补营地关系事件
4. 再做编辑器差异预览 / 导入导出
## 16. 一句话总结
这个项目最重要的经验不是“多写了多少功能”,而是:
**凡是会同时影响叙事、状态、演出和工具链的需求,都要先统一边界,再落实现。**