Files
Genarrative/docs/experience/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md
kdletters a2c71fcb3a
Some checks failed
CI / verify (push) Has been cancelled
chore: remove maincloud configuration
2026-05-02 17:04:11 +08:00

281 lines
10 KiB
Markdown
Raw 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.
# 奇幻酒馆项目开发经验手册
日期:`2026-03-24`
## 1. 项目本质判断
这个项目不是单纯的前端页面项目,也不是单纯的大模型接入项目,而是 4 条链路同时存在的复合型项目:
1. 叙事链路:剧情文本、选项文本、角色对话、聊天上下文。
2. 状态链路角色、怪物、NPC、场景、背包、装备、战斗状态、CD、蓝耗、死亡、掉落。
3. 演出链路:角色进场、近战贴身、受击、死亡、逃跑、镜头跟随、场景切换、前探预览。
4. 工具链路NPC 形象编辑器、行为编辑器、预设数据、校验脚本、本地调试环境。
经验结论:
- 任何需求只要影响两条以上链路,就不能只改 UI。
- 先判断需求落在哪些链路,再决定改哪些文件。
- 这类项目最怕“看起来改好了”,但状态、动画、提示词和运行逻辑其实没对齐。
## 2. 架构拆分经验
已经验证更稳的结构是:
- `App.tsx` 保持尽量薄,只做外层挂载。
- `GameShell.tsx` 负责主流程壳层:开始页、世界选择、角色选择、游戏内主界面切换。
- 各类状态和流程尽量放进 hooks
- `useGameFlow`
- `useCombatFlow`
- `useStoryGeneration`
- `useNpcInteractionFlow`
- `useInventoryFlow`
- `useEquipmentFlow`
- 各类独立 UI 面板拆成独立组件:
- `AdventurePanel`
- `CharacterPanel`
- `InventoryPanel`
- `MapModal`
- `AdventureEntityModal`
经验结论:
- 按职责拆,不按文件长度拆。
- 状态修改尽量集中在 hook 内,不要散落在多个按钮回调里。
- 预览系统、编辑器系统、正式游戏流程要尽量复用同一套业务逻辑,而不是各写一套。
## 3. AI 与本地规则的边界
目前最稳定的边界是:
- AI 负责生成:
- `storyText`
- `actionText`
- 对话文本
- 基于上下文的语气、叙事张力、选项措辞
- 本地规则负责决定:
- 哪些 function 当前可执行
- 技能是否在 CD
- 蓝量是否足够
- 伤害、回复、掉血、死亡、掉落
- 场景切换是否合法
- 背包、装备、属性、队伍变化
经验结论:
- 涉及数值、资源、状态迁移的部分,不要交给模型临场决定。
- 模型应被限制在“叙事表达层”,不要替代规则系统。
- 用户每次选择后只做一次模型推理,因此提示词必须一次性包含足够边界,不能再依赖后处理改写和过滤。
## 4. Function 驱动设计经验
当前更合理的设计方向是:
- 所有可选行为都落到 function。
- 每个 function 只有这些核心字段:
- `id`
- `text`
- `description`
- `category`
- `功能代码`
- 运行时先汇总当前场景的 function再结合角色、状态、敌人、场景实体、资源状态做合法性过滤。
- 模型只在“当前合法 function 集合”内生成选项文本和剧情文本。
经验结论:
- 选项不是自由散文,而是 function 的叙事包装。
- function 必须和角色、状态、场景、实体绑定,不能做成完全漂浮的公共动作池。
- “每次选择只能并且必须命中一个 function” 是保持状态稳定的关键。
## 5. 战斗与动画的经验
战斗部分已经沉淀出的几个重要规则:
### 5.1 结算时机
- 用户点击选项后,后台状态应立即推进到“动画完成后的结果状态”,并把这个新状态放入后续推理上下文。
- 但扣血、死亡表现、消失等视觉结果要在动画播完后再体现在画面上。
这样做的好处:
- 模型推理可以立即开始,不被动画阻塞。
- 画面依然保留正确的演出节奏。
- 状态、上下文、下一轮推理不会落后于动画。
### 5.2 近战位移
- 近战动画不能按固定像素移动。
- 必须按左右锚点、舞台宽度和目标位置计算真实接近距离。
- 宽屏下如果只“往前走一点点”,基本就是位移基准用了屏幕百分比或固定偏移,而不是双方真实锚点差值。
### 5.3 死亡与退出战斗
- 怪物血量归零时先播死亡动画。
- 死亡动画完成后怪物再消失。
- 若敌方死亡,战斗状态应退出,切回空闲状态,并重新切换可用 function 集合。
### 5.4 逃跑与压迫感
- 逃跑镜头要跟着玩家,不是跟怪物。
- 玩家向前跑,怪物被越甩越远,才能形成“甩开追兵”的感觉。
- 相反,如果镜头锚在怪物侧,会让玩家像没动一样。
## 6. 遭遇、场景与地图经验
这部分已经验证有效的设计原则:
- 场景和背景图一一绑定,不切场景就不换背景。
- 场景切换必须由选项命中的 function 驱动,而不是每回合自动变化。
- 每个场景预设自己的怪物、NPC、宝藏池。
- 玩家“继续向前探路”时,不是立刻随机弹结果,而是先预生成前方实体,再通过移动和镜头演出把它带到正式交互位。
经验结论:
- “探索”也应该是一种可演出的 function而不只是一次文字刷新。
- 同一时刻只应遇到一个主实体,避免一回合同时出现多只怪物造成状态混乱。
- 地图系统适合做成场景连接图,由具体 function 触发前往某个场景。
## 7. 移动端优先的 UI 经验
已经验证有效的 UI 方向:
- 先保证移动端一屏成立,再兼容网页宽屏。
- 开始页、世界选择、角色选择都要像游戏流程,而不是产品介绍页。
- 世界选择适合纵向焦点滑动。
- 角色选择适合横向翻卡和中心焦点展示。
- 冒险页中:
- 上方是画面演出
- 中间是自适应剧情文本
- 底部是固定的选项与功能入口
经验结论:
- 剧情区域不能写死高度,应填满画面下方和底部操作条上方之间的剩余空间。
- 队伍、背包更适合作为弹出面板,不应该切走主流程。
- 地图弹窗、队伍面板、背包详情都必须按手机窄屏重新组织,不要沿用桌面弹窗思路。
## 8. 选择页与游戏内界面经验
这轮迭代里比较明确的 UI 结论有:
- 开始页只保留核心按钮,视觉简洁更像正式游戏。
- 世界选择页要突出当前聚焦卡,其他卡随滑动连续改变透明度和大小。
- 角色选择页中:
- 中间卡片不能模糊
- 左右卡片要做正确方向的倾斜
- 中间角色可播放 `run` 动画
- 角色名称显示在动画下方
- 属性和背景信息要紧凑,不能压缩掉上方卡片滑动空间
- 游戏内按钮应尽量图标化,把更多空间留给剧情和选项。
## 9. NPC 形象编辑器经验
NPC 编辑器这部分已经沉淀出一些通用原则:
- 这套 Medieval Fantasy Characters 素材不是传统逐帧全身序列帧,发型、脸型、胡子、武器等部件不能按“每帧都变化”的思路处理。
- 主手武器、副手武器、手、身体、头部、头饰需要明确图层关系。
- 主手武器必须真正握在手里,不能只靠大概位置“看起来差不多”。
- 因为素材高度重叠,编辑器里必须支持:
- 底部组件模块点击选择
- 键盘上下左右微调
- 拖拽微调
- 回滚按钮
经验结论:
- 视觉编辑器如果没有“可控选择 + 微调 + 回滚”,用户会很难调重叠素材。
- 选项命名不能直接暴露英文源文件名,最好转成更贴近视觉理解的中文名称。
- 编辑器产出的相对位置数据必须能直接落回项目运行时,不然就只是一个孤立工具。
## 10. 大模型接入与本地调试经验
这部分踩坑非常集中,结论也比较清晰:
### 10.1 前端不能直连目标模型接口
- 浏览器直连时会遇到 CORS。
- 即使接口本身可用,浏览器环境也可能被跨域限制。
- 更稳的方案是在开发服务器侧做代理,再由前端请求本地 `/api/llm/...`
### 10.2 需要日志,但日志要聚焦
已经证明有价值的日志包括:
- 单次推理耗时
- 原始提示词文本
- 原始返回中的解析后文本
- 失败时的模型名、状态码和错误正文
### 10.3 开发服务器要统一入口
本项目本地正确启动方式应统一走:
```bash
node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0
```
经验结论:
- 只要本地存在旧进程、旧脚本或错误端口映射,就很容易出现“代码改了但界面还是旧的”假象。
- 遇到这类问题,优先检查实际启动脚本、端口占用和返回模块内容,而不是先怀疑 UI 代码没生效。
## 11. 编辑器与正式运行时的关系
已经验证最稳的做法是:
- 编辑器预览直接复用正式运行时函数。
- 预览不自己模拟一套战斗和 function 执行逻辑。
- 运行时怎么结算,编辑器就怎么调用。
经验结论:
- 只要编辑器预览和正式逻辑分成两套,后面一定会越来越不一致。
- 预览系统的价值不是“看起来像”,而是“执行路径就是正式路径”。
## 12. 后续继续开发时建议遵循的顺序
推荐流程:
1. 先补数据结构和类型。
2. 再补 function 规则和过滤条件。
3. 再补 hook 流程与状态迁移。
4. 再补动画和演出。
5. 最后再做 UI 细修。
6. 每轮改动后至少做一次类型检查和本地启动验证。
不推荐的流程:
1. 先堆 UI。
2. 再临时塞状态。
3. 最后补运行逻辑。
这类项目里,后者几乎一定导致返工。
## 13. 后端修改后的重启与测试
后端代码更新后统一执行:
```bash
npm run api-server
```
执行要求:
- 该命令是后端更新后的默认重启入口,不再使用此前的后端重启命令。
- 重启后必须继续执行与本次后端改动对应的自动测试;涉及 Rust workspace 时优先跑 `server-rs` 下的检查或测试脚本。
- 若本次改动涉及 SpacetimeDB 发布、绑定生成或本地联调,按 `spacetimedb-cli` 经验执行,并在验证记录中写清楚实际命令与结果。
## 14. 一句话总结
这个项目最重要的经验不是“做了多少页面和功能”,而是:
**必须把 AI 文本生成、本地规则、动画演出、场景状态、编辑器工具这几套系统严格分层,再通过 function 和统一状态流把它们重新接起来。**
## 15. 相关文档
如需继续细看已有沉淀,可结合以下文档一起阅读:
- `docs/experience/PROJECT_DEVELOPMENT_EXPERIENCE.md`
- `docs/experience/MOBILE_UI_DEV_EXPERIENCE.md`
- `docs/experience/CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md`
- `docs/experience/AGENT_UI_CHANGELOG.md`