Rework story engine flow and reorganize project docs
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-06 23:19:00 +08:00
parent d678929064
commit ddcb5d5c8c
241 changed files with 19805 additions and 2478 deletions

View File

@@ -0,0 +1,255 @@
# 冒险运行时开发经验沉淀
日期2026-03-24
## 1. 这轮工作主要覆盖了什么
这轮迭代不是单点 UI 修改,而是同时改了 4 条链路:
1. 战斗演绎链路:近战突进、远程施法、投射物/特效、受击结算时机。
2. 探索链路向前探索、呼喊、NPC 离队、切换场景时的新 encounter 生成与入场动画。
3. NPC function 链路:聊天、招募、观察征兆、交易、离队、上下文注入边界。
4. 面板链路:队伍页、角色详情、背包页、移动端空间分配、入口 icon 语义。
这类项目最重要的判断是:
很多需求表面看是“改个界面”或“改句文案”实际同时影响状态机、动画时序、AI 提示词、实体生成规则和移动端布局。
## 2. 这轮最值得沉淀的结论
### 2.1 function 的职责边界必须先定死
后面事实证明,只有少数 function 应该负责生成新 encounter
- `idle_explore_forward`
- `idle_call_out`
- `npc_leave`
- 进入新场景
其他 function 如果也偷偷新增 NPC、怪物、宝箱后面一定会出现这些问题
- 剧情上下文越来越乱
- 场景上实体数量失控
- 玩家以为“聊天/招募/观察”也会强行推进世界
- AI 生成内容和本地规则互相打架
经验:
先定义“谁能创造世界实体”,再写文案和动画。
### 2.2 动画完成点必须和数值结算点绑定
这轮一个关键修正是:
攻击特效播放完后,受击目标才扣血。
如果不这样做,玩家看到的就是:
- 特效还没打到,血条先掉了
- 投射物还在飞,目标已经结算
- dash 还没到位,攻击已经命中
经验:
战斗系统里不能只写“播放动画”,必须写清楚:
1. 起手阶段是什么
2. 位移阶段是什么
3. 命中阶段是什么
4. 哪个阶段结束后才改数值
### 2.3 视觉素材选择要按“语义 + 小尺寸可读性”来做
这轮做过两类视觉判断:
- 战斗特效素材该怎么挂到角色/技能上
- 冒险页入口 icon 应该换成什么
经验不是“素材越花越好”,而是:
- 先看素材本身表达的动作语义
- 再看缩小到按钮尺寸后是否还能一眼看懂
- 最后才考虑风格统一
例如:
- 队伍入口用“盔甲”不如用“盾牌”更像角色/编队
- 背包入口用真正的包袋图,比木板式 HUD 图更直观
- 远程施法不移动时,特效就必须承担“动作已经发生”的视觉职责
### 2.4 角色型 NPC 和普通 encounter 不能混着处理
这轮踩过的坑说明:
- 可扮演但未入队的 NPC
- 已入队同伴
- 普通场景 NPC
这三者虽然都可能显示为 NPC但数据完整度、立绘来源、详情页能力并不一样。
经验:
- 角色型 NPC 要按角色数据链路渲染
- 普通 NPC 要按 encounter/NPC state 渲染
- 详情弹窗必须对缺失字段做空值保护
否则就会出现点击小人直接报 `map` 相关错误这类问题。
### 2.5 角色和同伴的朝向/位移必须彼此独立
`idle_observe_signs` 的修正说明了一件事:
如果主角和同伴在 transform、朝向、随机停顿上仍然存在隐含父子级关系最后视觉表现一定不自然。
经验:
- 主角随机转向和同伴随机转向要分别计算
- 位置、朝向、停留时长都要独立采样
- 不要让父容器顺手把子角色的朝向也带过去
这条经验同样适用于:
- 招募入队时的新同伴 dash
- 多角色待机观察
- 场景内多实体同步演绎
### 2.6 大模型负责“推理与叙述”,本地规则负责“世界变更”
这轮对 `npc_chat``npc_recruit``idle_observe_signs` 的调整,本质上都指向同一个原则:
- 大模型可以生成观察结果、聊天内容、氛围描述
- 大模型不应该偷偷新增实体、替玩家做决定、绕开本地规则结算
经验:
1. 聊天生成要明确禁止提及其他 function 行为。
2. 招募对话可以引导到成功入队,但最终入队仍应由本地流程触发。
3. `idle_observe_signs` 可以走大模型推理,但写入剧情上下文的内容要可控。
4. `npc_recruit` 这种流程不要顺手给下次推理塞一个“新 NPC”。
一句话总结:
AI 可以解释世界,但不能私自改世界。
### 2.7 移动端面板要优先保信息密度,不要保装饰
这轮背包和队伍页的调整很能说明问题:
- 队伍页只保留成员列表
- 成员详情放到弹窗
- 背包页去掉多余标题、提示文案、厚重背景框
- 装备信息移到角色详情,不和背包抢空间
经验:
- 主面板只放“高频扫读信息”
- 低频详情放二级弹窗
- 任何重复信息都要尽量去重
在小屏上,空间不是拿来“显得完整”的,而是拿来“保证可操作”的。
## 3. 这轮最典型的踩坑记录
### 3.1 encounter 生成距离只改一处是不够的
一开始只是把某个函数里的怪物生成位置往后挪,但后来发现:
- 其他会生成 encounter 的 function 仍然太近
- 新场景进入时也可能直接出现在屏幕里
经验:
“从屏幕外进场、要走 4 秒距离”必须是统一约束,不是某个函数里的特判。
### 3.2 详情文案改源头不一定等于改到了最终显示层
这轮“选项小字太长”的问题最后证明:
真正该改的是渲染层显示的 `detailText`,而不是只改某个上游数据源。
经验:
用户说“没有生效”时,要优先检查最终渲染层,而不是只检查中间数据。
### 3.3 新同伴入队时,尺寸问题本质是缩放基准不统一
招募流程里位置正确但大小不对,说明问题不在路径,而在:
- 新同伴使用的缩放基准
- 主角扮演角色使用的缩放基准
不一致。
经验:
只要角色加入到主队镜头体系里,尺寸基准必须复用主角/同伴那套规则,不能临时另开一套。
### 3.4 背包和角色详情职责不清,会持续挤压移动端布局
这轮背包页一度同时承担:
- 物品网格
- 装备总览
- 装备卸下
- 详情查看
最后结果就是移动端空间不够、信息层级混乱。
经验:
- 背包负责“物品”
- 角色详情负责“装备”
职责一旦分清,后面很多 UI 冲突会自然消失。
## 4. 可复用的开发方法
### 4.1 新增一个 function 前,先问 6 个问题
1. 它会不会生成新 encounter
2. 它会不会写入后续推理上下文?
3. 它的动画分几段?
4. 哪一刻才算真正生效?
5. 哪些内容由 AI 生成,哪些必须本地决定?
6. 它是否需要从屏幕外入场?
这 6 个问题先答清,后面返工会少很多。
### 4.2 配战斗动画时,至少明确 4 个时点
1. 起手
2. 位移/施法
3. 特效命中
4. 扣血/击退/结算
特别是:
- 近战攻击前的 dash
- 远程不移动但要挂特效
- 受击延后到命中特效之后
都属于这套时点设计。
### 4.3 做素材替换时,不要只看资源名
正确顺序更接近:
1. 打开素材看实际形状
2. 判断它在当前语义里是不是“最像这个功能”
3. 缩到真实 UI 尺寸再看一遍
4. 再决定 active/inactive 怎么处理
### 4.4 做聊天流式界面时,反馈一定要插到正在发生的流里
这轮加“好感度 +x”提示后更明确了一件事
- 系统反馈不应该只藏在最终结果
- 要插进聊天进行中的体验里
- 文本流超出剧情框时要自动滚动
否则用户会错过真正重要的状态变化。
## 5. 以后继续做这类需求时,建议坚持的原则
- 先收拢 function 边界,再改剧情文案。
- 先确定动画结算时序,再接特效素材。
- 先做本地规则兜底,再让大模型生成文本。
- 先保证移动端可扫读,再考虑装饰性面板。
- 先复用已有角色/场景坐标体系,再做个别修正。
- 先看最终显示层,再判断“改动是否生效”。
## 6. 这轮最重要的一句话总结
这类 AI 冒险 RPG 的开发,最难的不是“把功能做出来”,而是:
**让 function 边界、世界状态、视觉演绎、移动端面板和大模型文本在同一套规则下稳定协作。**

View File

@@ -0,0 +1,116 @@
# UI 改动记录(供后续 Agent 阅读)
本文档汇总 **像素 RPG UI 皮肤化** 相关实现与决策,便于新会话快速接手。更细的命名与规范见同目录上一级的 **`UI_CODING_STANDARD.md`**。
---
## 1. 相关文件一览
| 路径 | 作用 |
|------|------|
| `UI_CODING_STANDARD.md` | 资源目录约定、9-slice 规则、图标语义、`Icons`/`UI` 命名解读、已知问题(含世界按钮切片) |
| `src/uiAssets.ts` | **唯一推荐** 的 UI 资源映射:`UI_CHROME`9-slice 配置)、`TAB_ICONS``WORLD_SELECT_ICONS``getNineSliceStyle()` |
| `src/components/PixelIcon.tsx` | 小图标 `<img>``image-rendering: pixelated` |
| `src/index.css` | `.pixel-nine-slice``.pixel-root-shell` / `.pixel-app-shell`、tab/按钮布局类、`--ui-scale` |
| `src/App.tsx` | 世界选择、角色卡、底部 tab、剧情/背包面板、地图弹窗、`MudMapRoom` |
| `src/components/GameCanvas.tsx` | 场景名按钮9-slice `Title_frame_m` |
| `vite.config.ts` | `root` / `envDir` 指向 `__dirname`,保证 `.env.local` 从项目根加载 |
| `public/UI/``public/Icons/` | 静态资源(路径以 `/UI/...``/Icons/...` 引用) |
---
## 2. 架构要点
### 2.1 9-slice禁止整图 `background-size: 100% 100%` 拉框体)
- 使用 CSS **`border-image`** + **`fill`**,通过自定义属性注入切片参数。
- 类名:**`pixel-nine-slice`**(定义在 `src/index.css`)。
- 行内样式由 **`getNineSliceStyle(texture, overrides?)`** 生成(`src/uiAssets.ts`),设置 `--frame-src``--slice-*``--frame-pad-*``--frame-repeat`、可选 `--frame-base`(一般仅在图源中心确实透明时使用)。
### 2.2 响应与缩放
- `:root`**`--ui-scale`**`clamp`(桌面)+ 小屏媒体查询内固定约 `0.8`
- 边框宽度 = 切片像素 × `--ui-scale`,与 padding 同逻辑,避免移动端边框比例失控。
### 2.3 交互
- **`pixel-pressable`**hover 用 `translateY` + `brightness`**避免**对像素框体做 `scale()` 以免糊边。
### 2.4 全局背景
- **根布局**`App.tsx` 最外层):`pixel-root-shell` + `Background_fill.png` 平铺 + 深色半透明渐变(替代纯 `#050505` 死底)。
- **下半屏内容区** `pixel-app-shell`:同一纹理 + **更轻**的遮罩,让纹理更明显。
- 开局标题区已去掉单独 `bg-zinc-950`,与根背景一致。
---
## 3. `UI_CHROME` 当前用途(速查)
以下为 `src/uiAssets.ts` 中主要键与界面位置的对应关系(切片数值以文件内为准):
| Key | 资源(示例) | 用途 |
|-----|----------------|------|
| `appBackground` | `Background_fill.png` | 根壳 + 下半屏平铺底 |
| `worldButtonWuxia` / `worldButtonXianxia` | `1_orange_button` / `1_violet_button` | 开局武侠/仙侠(**条高 28px**,切片见下文) |
| `characterCardFrame` | `pick_hero_frame` | 选角卡片 |
| `tabActive` / `tabInactive` | `Shop_tab_picked` / `Shop_tab` | 底部「角色 / 冒险 / 背包」 |
| `panel` | `Frame_bg_big_2` | 装备区等通用面板 |
| `storyPanel` | `Dialogue_frame` | 剧情正文区 |
| `inventoryPanel` | `Inventory_bg` | 背包条目 |
| `statsPanel` | `Stats_bar` | 角色数值面板 |
| `choiceButton` | `Options_bar` | 剧情选项按钮 |
| `modalPanel` | `Popup_window` | 地图弹窗外壳 |
| `infoPanel` | `Dialogue_frame` | 地图弹窗内「当前地点 / 可前往」信息块 |
| `sceneTitle` | `Title_frame_m` | 战斗画布顶部场景名按钮 |
| `mapRoomCell` | `Map_frame` | 地图节点卡片(`MudMapRoom` |
| `mapDiagramPanel` | `Frame_bg_big_2` | 地图关系图整体衬底 |
图标路径:`TAB_ICONS``WORLD_SELECT_ICONS``CHROME_ICONS`;装备槽与背包分类见 `getEquipmentSlotIcon` / `getInventoryCategoryIcon`
---
## 4. 已知坑(必读)
### 4.1 世界选择按钮「中间发空」
- 图源 **`1_orange_button.png` / `1_violet_button.png` 为 125×28**。
-**`slice.top + slice.bottom >= 28`**,中间横带高度为 **0**`border-image``fill` 异常,看起来像透明/没画上。
- **正确做法**:减小上下切片(当前为 **9+9**),并配合 `repeat: 'stretch'` 竖向拉伸条形成品。
- **不推荐**:用大色块 `baseColor` 糊底(与素材内渐变/内框不一致)。详见 `UI_CODING_STANDARD.md`「已知问题」。
### 4.2 新增 9-slice 前
先读 PNG **宽高**,保证 **`top+bottom < height`** 且 **`left+right < width`**。
---
## 5. 环境与运行(与 UI 无关但易踩坑)
- LLM`VITE_LLM_*` 写在项目根 **`.env.local`**;因 `vite.config.ts` 固定 `envDir: __dirname`,从任意 cwd 启动也应能读到。
- 开发:`npm run dev`(默认端口见 `package.json`)。
---
## 6. 尚未统一成 UI 图、仍用 Tailwind 小块面的区域(可选后续)
若产品要求「全盘无纯色板」,可继续替换,当前仍可能为 `bg-black/20``rounded-lg border` 等:
- 角色页装备行、属性格子
- 地图弹窗顶栏分隔、关闭按钮外圈
- `GameCanvas` 内血条、名字条等 HUD
---
## 7. 修改清单摘要(按主题)
1. **资源规范**`UI_CODING_STANDARD.md` + 集中映射 `uiAssets.ts`
2. **9-slice 系统**`index.css` + `getNineSliceStyle`
3. **开局 / 主界面**世界按钮、选角卡、tab、面板、选项、背包、根与内容区背景纹理。
4. **地图弹窗**:外壳、信息板、节点 `Map_frame`、关系图 `Frame_bg_big_2`
5. **画布**:场景名 `Title_frame_m`
6. **世界按钮**:按真实像素尺寸修正切片,去掉错误 `baseColor` 方案。
7. **Vite**`root`/`envDir` 保证环境变量加载稳定。
---
*文档目的:交接给下一个 Agent 时,优先读本文件 + `UI_CODING_STANDARD.md`,再改 `uiAssets.ts` / `App.tsx` / `index.css`。*

View File

@@ -0,0 +1,262 @@
# 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. 一句话总结
这个项目最重要的经验不是“多写了多少功能”,而是:
**凡是会同时影响叙事、状态、演出和工具链的需求,都要先统一边界,再落实现。**

View File

@@ -0,0 +1,277 @@
# Codex Past Work Experience Summary
日期:`2026-03-23`
## 1. 工作范围概览
这几轮工作主要集中在 4 个方向:
1. 编辑器体系整理
2. NPC 视觉编辑与素材接入
3. 玩家角色与怪物动画资源纠偏
4. 选项行为编辑器与预览链路升级
这些改动不是孤立的 UI 修补,而是横跨了:
- 资源定义层
- 编辑器字段层
- 运行时预览层
- 游戏真实播放逻辑层
## 2. 已完成的核心工作
### 2.1 编辑器入口与页签整理
- 保留 `/preset-editor``/npc-editor``/function-editor`
- 新增 `/behavior-editor` 作为“选项行为”编辑页别名
- 将原先单独的 `NPC 视觉` 标签并回 `NPC` 编辑页
-`Function` 页签改名为 `选项行为`
结论:
- 路由层要尽量兼容旧入口,避免历史链接失效
- 页签命名要贴近创作者语言,而不是内部实现命名
### 2.2 NPC 视觉模块并入 NPC 编辑
完成内容:
- 将 [NpcVisualEditor](/E:/Repos/Genarrative/src/components/NpcVisualEditor.tsx) 嵌入 [PresetEditor](/E:/Repos/Genarrative/src/components/PresetEditor.tsx) 的 NPC 编辑页
- 让 NPC 文本字段与视觉字段围绕同一个当前选中 NPC 联动
- 保留视觉覆盖保存与全局布局保存能力
经验:
- NPC 文本编辑和 NPC 视觉编辑不应分裂成两个互不关联的工作流
- “当前选中的 NPC” 必须是两个模块共享的单一数据源
### 2.3 Medieval NPC 素材定义重建
完成内容:
- 在 [medievalNpcVisuals.ts](/E:/Repos/Genarrative/src/data/medievalNpcVisuals.ts) 中重建了 Medieval NPC 的资产定义
- 补齐了 cloth / leather / metal / melee / magic / ranged 六大类真实素材
- 为素材增加了:
- 语义化名称
- 图块尺寸
- 列数
- 帧数
- 姿态选项
- 让编辑器不再直接使用文件名、序号名作为展示项
经验:
- 编辑器下拉项如果来自手写数组,迟早会和真实素材目录脱节
- 素材定义最好具备“资产元数据”,而不是只有路径字符串
- 一旦资产存在大图块,就不能再默认所有图块都是 `32x32`
### 2.4 NPC 动画器支持大图块武器
完成内容:
- 在 [MedievalNpcAnimator.tsx](/E:/Repos/Genarrative/src/components/MedievalNpcAnimator.tsx) 中为 `AtlasSprite` 增加:
- `tileWidth`
- `tileHeight`
- 对齐偏移支持
效果:
- 长柄武器
- 巨剑
- 64x32 远程武器
- 64x64 投石索类武器
都能正确显示,不再被按 `32x32` 裁坏。
经验:
- 视觉编辑器一旦涉及装备 atlas就必须把“资源尺寸”从数据层带到渲染层
### 2.5 玩家角色动画映射纠偏
完成内容:
- 在 [characterPresets.ts](/E:/Repos/Genarrative/src/data/characterPresets.ts) 中重新核对 5 个玩家角色的 Hero 动画目录
- 修正了错误帧数、错误前缀、遗漏动作
- 补齐了真实存在但之前未接入的动作:
- `acquire`
- `climb`
- `dash`
- `die`
- `double jump`
- `hurt`
- `jump`
- `jump attack`
- `wall slide`
- 给角色动画配置增加了 `file` 字段,支持单文件动画
相关文件:
- [types.ts](/E:/Repos/Genarrative/src/types.ts)
- [CharacterAnimator.tsx](/E:/Repos/Genarrative/src/components/CharacterAnimator.tsx)
- [characterCombat.ts](/E:/Repos/Genarrative/src/data/characterCombat.ts)
经验:
- 只要编辑器允许用户切“预览动作”,就不能让未映射动作静默 fallback 到 `idle`
- 正确做法是:
1. 先补齐真实动作映射
2. 再让预览下拉只显示当前角色实际可用动作
### 2.6 怪物动画空白帧修复
完成内容:
- 在 [monsterPresets.ts](/E:/Repos/Genarrative/src/data/monsterPresets.ts) 中把怪物动画从“连续帧猜测”改成“按图集行起点取帧”
- 补上缺失的 `die` 配置
- 清除了所有落进空白格的动画段
经验:
- 像素怪物图集不一定按一个连续区段排完整套动作
- 如果动作配置只写 `start + frames`,但没结合图集行结构,就非常容易踩进空白帧
### 2.7 选项行为编辑器重构
完成内容:
- 将原 “Function 编辑器” 改造成 “选项行为编辑器”
- 页面文案和入口统一为“选项行为”
- 移除“基础场景 / 结果场景”双窗格预览
- 保留并强化:
- 行为列表
- 覆盖保存
- 快速模板套用
经验:
- 创作者并不关心 “function” 这个技术词,更关心“这个选项会发生什么”
- 同类编辑器如果只给字段表单而没有模板起稿能力,复用效率会很低
### 2.8 选项行为预览升级到实机回放
完成内容:
- 在 [StateFunctionEditor.tsx](/E:/Repos/Genarrative/src/components/StateFunctionEditor.tsx) 中新增 `BehaviorExecutionPreview`
- 预览不再是静态推测,而是:
1. 构造本地 `GameState`
2. 调用真实 `resolveFunctionOption`
3. 再调用 [useCombatFlow.ts](/E:/Repos/Genarrative/src/hooks/useCombatFlow.ts) 的
- `buildResolvedChoiceState`
- `playResolvedChoice`
- 从而直接复用游戏真实逻辑
覆盖能力包括:
- 战斗行为
- 恢复行为
- 脱离行为
- 探索前探
- 切场行为
经验:
- 编辑器预览只要和运行时逻辑写成两套,就一定会越来越不一致
- 预览层最稳的做法是“调用真实业务逻辑”,而不是“模拟真实业务逻辑”
## 3. 关键踩坑记录
### 3.1 图标组件名覆盖原生 `Map`
问题:
- `lucide-react``Map` 图标直接命名为 `Map`
- 在 NPC 页签里 `new Map()` 实际调用到了图标组件
- 导致页签内容直接渲染为空
经验:
- 图标组件命名尽量使用 `MapIcon``UserIcon` 这类后缀
- 避免覆盖 JS/TS 原生对象名
### 3.2 预览 effect 依赖不稳定导致回放反复重启
问题:
- `BehaviorExecutionPreview` 里使用了 `useCombatFlow()`
- 但 effect 依赖了返回对象本身
- 每次 `gameState` 更新effect 都会被视为变更
- 导致预览回放速度异常、重复重启、动画像加速
经验:
- 只要预览组件内部要“异步播放状态变化”,就要高度警惕 effect 依赖环
- 解决方式是:
-`ref` 保存稳定方法引用
- 让 effect 只依赖真正的输入配置,不依赖内部播放状态
### 3.3 实时面板与回放阶段不同步
问题:
- `LIVE PLAYER` 用的是实时 `gameState`
- `BATTLE SNAPSHOT` 用的是预计算首回合快照
- 两者不是同一时间点的数据
- 导致面板看起来“都对,但互相对不上”
经验:
- 预览面板要么都显示“实时状态”
- 要么都显示“同一个阶段的快照”
- 混用实时值和预测值会让创作者误判
## 4. 这类项目里沉淀下来的方法论
### 4.1 先校验资源,再改编辑器
顺序建议:
1. 先扫真实目录
2. 再建资产定义
3. 再修编辑器字段
4. 最后修预览
### 4.2 预览必须尽量复用游戏真实链路
优先级:
1. 复用真实函数
2. 复用真实状态结构
3. 复用真实渲染组件
4. 最后才是补充编辑器专用的辅助信息
### 4.3 编辑器要区分“可编辑字段”和“会生效字段”
经验:
- 不是所有字段都应该在所有行为类型下开放
- 如果某类行为最终不会直接读取某个字段,就应该禁用或弱化它
- 否则创作者会错误地以为改动无效是 bug
### 4.4 模板比空白表单更重要
经验:
- 当系统里已经有多种成熟行为时,最快的创作路径不是“从零填写”
- 而是:
- 选一个最像的
- 套结构
- 微调文案和数值
## 5. 推荐的后续方向
如果继续打磨这套编辑器,建议下一步做:
1. 为选项行为预览增加“时间轴 / 阶段日志”
2. 为选项行为编辑器增加“新建行为向导”
3. 把更多系统状态引入预览上下文
- 同伴
- NPC 状态
- 背包
- 当前场景实体池
4. 把“可编辑字段”和“只读推导字段”视觉上再分开
## 6. 一句话总结
过去这几轮最重要的经验不是“写了多少编辑器 UI”而是
**编辑器一旦想可靠,就不能只编辑静态数据,必须逐步接管真实资源定义、真实运行时状态和真实播放逻辑。**

View File

@@ -0,0 +1,191 @@
# 奇幻酒馆 UI 开发经验沉淀
## 1. 总体原则
### 1.1 先保证移动端成立,再兼容网页端
- 入口页、世界选择、角色选择、冒险页、弹窗面板,都要先按手机竖屏去定义信息密度。
- 网页端只做“放宽容器、增加留白、补充 hover”不要反过来让桌面布局压垮手机体验。
- 任何区域如果在移动端需要滚动,必须明确谁滚动,不能让整页和局部同时争夺滚动。
### 1.2 游戏 UI 要优先“状态清晰”,而不是“文案很多”
- 开始页应该像主菜单,不像产品介绍页。
- 角色选择页应该像角色选择器,不像角色说明书。
- 冒险页应该像战斗/剧情操作台,不像文档阅读器。
### 1.3 一屏内的层级顺序要稳定
- 上半部分:画面演出。
- 中间部分:剧情或核心信息。
- 底部:操作按钮。
- 玩家必须能快速判断“我在看什么、下一步点哪里”。
## 2. 入口页经验
### 2.1 开始页
- 极简化是对的。
- 当前实践证明:开始页只保留“开始游戏”和“开发团队”两个主按钮,体验明显更像游戏。
- 游戏名和英文副标题可以保留,但不要再加长段描述。
### 2.2 世界选择
- 纵向焦点轮播比普通列表更适合移动端。
- 当前居中的卡片应该最大、最清晰、最亮;上下卡片缩放和透明度随滚动连续变化。
- 世界确认动作应该绑定当前聚焦卡,而不是每张卡都塞太多按钮。
### 2.3 角色选择
- 横向轮播是正确方向。
- 中间卡片应承担“主视觉 + 主要信息”。
- 左右卡片只做预览,不需要承载完整信息。
- 左右卡片倾斜方向一定要符合透视直觉:
左侧卡片向左外倾,右侧卡片向右外倾。
- 中间卡片不要出现任何模糊,否则会破坏“当前选中”的确认感。
## 3. 角色选择页经验
### 3.1 聚焦卡的表现
- 中间聚焦卡用角色 `run` 动画是有效的,能明显提升“角色活着”的感觉。
- 但动画资源和静态立绘的锚点通常不一致,必须单独做位置与缩放修正。
- 结论:
动画版角色不要直接复用静态图样式,要单独给 `transform``height``transform-origin`
### 3.2 信息区要紧凑
- 角色数值和角色背景应该放在轮播下方,但必须压缩高度。
- 如果下方面板过高,会直接破坏上半部分轮播体验,尤其在手机上。
- 删除背景面板里的额外动画模块是正确的,因为它与主轮播重复抢视觉焦点。
### 3.3 文案层级
- 页面标题只保留一句:
“选择你扮演的角色”
- 不要再强调 `CHARACTER SELECT` 之类的开发向分类标签。
- 聚焦卡下方直接显示角色名,比把名字只放在卡片底部更直观。
### 3.4 按钮位置
- “确认选择”必须放在角色轮播和信息区之后,作为页面最后的主动作。
- 按钮不宜过宽过高,否则会抢轮播的视觉重心。
## 4. 冒险页经验
### 4.1 剧情区必须自适应
- 剧情文本区不应该写死高度。
- 正确做法是:
让它自动填满“上方画面下缘”和“底部按钮区上缘”之间的剩余空间。
- 这样才能同时保证:
1. 上方画面一屏可见
2. 下方三个选项一屏可见
3. 中间剧情尽量大
### 4.2 底部操作区必须锚定到底
- 队伍、背包、换一换、选项列表,都应该属于底部控制区。
- 用户会天然在屏幕底部寻找交互入口,尤其是手机。
- 因此:
不要把这些按钮漂在中间,不要让剧情区把它们挤走。
### 4.3 队伍与背包不应打断主流程
- 在冒险页内,队伍和背包更适合“弹出面板”而不是“切换整页”。
- 原因:
1. 不会打断当前剧情阅读
2. 用户返回成本更低
3. 操作像手游副面板,更符合预期
### 4.4 图标优于文字按钮
- 在底部工具区,队伍/背包改成 icon 后更紧凑。
- 但必须保留 `aria-label`,保证语义清晰、后续也方便测试。
## 5. 队伍面板经验
### 5.1 移动端成员列表不能太“卡片化”
- 如果每个成员都再套一层大边框卡片,手机上会显得很挤。
- 更好的方式是直接在主面板里陈列成员,弱化分隔、强化内容。
### 5.2 队伍列表展示什么
- 头像
- 姓名
- 称号
- 身份标记(领队/同行)
- HP / MP
### 5.3 队伍详情弹窗
- 弹窗必须可滚动。
- 弹窗宽度不宜太大,手机优先单列或偏单列。
- 技能、装备、属性三个区块保持稳定结构,便于扫读。
## 6. 背包页经验
### 6.1 格子数要优先适配手机
- 手机端格子间距要小一号。
- 图标尺寸和分类角标要跟着缩小。
- 目标不是“每个格子信息很多”,而是“单屏能看更多格子”。
### 6.2 背包详情弹窗
- 手机端改成单列/窄宽度布局更合理。
- 详情窗里的信息顺序建议固定:
1. 名称与类别
2. 品质与数量
3. 大图标
4. 说明
5. 标签
## 7. 样式与动画经验
### 7.1 轮播动画要连续,不要离散
- 滚动时应根据“当前位置与当前卡片中心的距离”实时计算:
- `scale`
- `opacity`
- `rotate`
- `translate`
- 不要做成“翻页后才跳变”的效果。
### 7.2 焦点卡与非焦点卡的职责要不同
- 焦点卡负责可读性与确认感。
- 非焦点卡负责预告与空间深度。
- 所以焦点卡不该模糊,非焦点卡可以轻微降透明度、缩放、偏移。
### 7.3 资源锚点要单独校准
- 静态立绘和动画帧经常不是同一锚点。
- 只要切换成动画,就需要重新调:
- 高度
- Y 偏移
- 缩放
- `transform-origin`
## 8. 工程经验
### 8.1 组件要继续拆
- 本轮重构证明,把入口页、冒险页、队伍页、背包页拆成独立组件是正确的。
- 后续新增 UI 需求时,应优先落在:
- `GameShell.tsx`
- `AdventurePanel.tsx`
- `CharacterPanel.tsx`
- `InventoryPanel.tsx`
### 8.2 不要相信“服务已经在跑”
- 这轮出现过“端口上有进程,但浏览器仍然拿到旧模块”的问题。
- 实际经验:
1. 要检查 `src` 文件内容
2. 要检查 dev server 实际返回的模块内容
3. 要确认启动脚本是否是当前项目真正使用的脚本
### 8.3 本项目当前开发服务入口
- 本地正确启动脚本是:
`node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0`
- 历史脚本或旧进程会导致“代码已改但 UI 看起来没变”的假象。
## 9. 后续建议
### 9.1 可以继续统一中英混杂文案
- 当前部分新旧文本仍有历史遗留字符问题。
- 如果后面继续做 UI 精修,建议单独做一轮文案清洗。
### 9.2 可以把轮播抽成通用组件
- 世界纵向焦点轮播
- 角色横向倾斜轮播
- 这两套逻辑已经足够稳定,适合后续抽成通用 hook 或通用组件。
### 9.3 可以补移动端安全区适配
- 当前已经偏移动端优先,但还可以继续加:
- `safe-area-inset-bottom`
- `safe-area-inset-top`
- 更细的竖屏断点处理
## 10. 一句话结论
这一轮最关键的经验是:
**游戏 UI 的移动端优化,本质不是把元素缩小,而是重组视觉重心、固定操作锚点、让焦点内容在一屏内自然成立。**

View File

@@ -0,0 +1,243 @@
# AI Native Visual RPG 开发经验沉淀
## 1. 项目特点判断
这个项目不是单纯的“像素 UI 项目”,而是 4 条链路同时耦合的项目:
1. 叙事链路AI 生成剧情文本与选项
2. 状态链路玩家、怪物、NPC、背包、好感、同伴、场景流转
3. 演出链路:战斗计划、动画播放、投射物、特效、镜头位移
4. 界面链路:选择世界、选择角色、冒险页、背包页、地图弹窗、编辑器页
经验:
- 做功能前先判断它主要影响哪几条链路。
- 如果一个需求同时影响“状态 + 演出 + UI”不要只改一个点。
- 像“初始同伴”这种功能,本质上不是 UI 需求,而是“选角流程 + 初始 encounter + NPC 好感 + 招募状态”的组合需求。
## 2. 先做数据建模,再做 UI
这一类项目最容易犯的错误,是先加按钮、再补状态。
实践下来更稳的顺序是:
1. 先把状态字段补齐
2. 再补工具函数
3. 再接交互入口
4. 最后补展示层
已经验证有效的状态字段包括:
- `playerInventory`
- `npcStates`
- `companions`
- `currentBattleNpcId`
- `currentEncounter`
- `playerActionMode`
- `activeCombatEffects`
经验:
- “能否交易 / 能否招募 / 送礼涨多少好感”都应该由状态和规则函数决定,不能写死在按钮文本里。
- NPC 交互尽量走本地规则,不要依赖 AI 即时决定关键数值。
## 3. 复杂页面一定要拆流程层
`App.tsx` 一旦同时承载:
- 游戏主状态
- 剧情生成
- 战斗播放
- NPC 交互
- 地图
- 选角
就会迅速失控。
当前更合理的分层思路是:
- `useGameFlow`
负责基础游戏状态、世界选择、角色选择、初始进入逻辑
- `useCombatFlow`
负责战斗计划与播放
- `useStoryGeneration`
负责剧情生成、NPC 本地交互分流、选项池管理
- `useNpcInteractionFlow`
负责同伴/NPC 展示态
- `GameShell`
负责主容器与选择流程
- `AdventurePanel`
负责冒险页文本和选项
- `NpcModals`
负责交易 / 送礼 / 放生招募等弹窗
经验:
- 流程层优先按“职责”拆,不按“文件长度”拆。
- 状态修改逻辑尽量集中到 hook 内,不要散落在多个组件按钮回调里。
## 4. AI 只适合生成叙事,不适合决定关键规则
实践中最稳定的策略是:
- AI 负责:
- `storyText`
- 非 NPC 关键规则的普通探索选项文案
- 本地规则负责:
- NPC 交互选项
- 交易合法性
- 礼物好感值
- 招募阈值
- 战斗掉落
- 帮助奖励
经验:
- 凡是会影响数值平衡、背包物品、好感、队伍成员的部分,都不要交给 AI 即时决定。
- AI 生成内容要能被本地规则兜底,否则体验会不稳定。
## 5. NPC 系统要“角色型 NPC”和“普通 NPC”分开处理
项目里 NPC 实际上有两种:
1. 普通场景 NPC可用通用 Medieval NPC 渲染
2. 角色型 NPC应该复用玩家角色对应的立绘和动画
经验:
- 只看 `encounter.kind === 'npc'` 不够,还要看 `encounter.characterId`
-`characterId` 的 NPC应该优先走 `CharacterAnimator`
- 否则就会出现:
- 选了某个同伴,但开场看到的是另一套通用 NPC 外观
- 战斗里角色型 NPC 看起来像普通路人
## 6. 位置与朝向必须统一到一套坐标规则
这是这类项目里最容易反复返工的点。
实践中踩过的坑:
- `sceneMonsters` 用一套坐标逻辑
- `currentEncounter` 用另一套坐标逻辑
- 结果开场 NPC 和遇怪站位不一致
- 角色型 NPC 立绘比怪物更容易出现“脚没落地”“太小”“翻转方向错”
最终经验:
- 对面实体的横向定位必须统一到“怪物那套 world-space 逻辑”
- 也就是:
- 位置统一用怪物侧的 anchor
- 相机平移时统一跟随同一套计算
- 角色型 NPC 的垂直位置不能偷懒固定
- 应该结合角色自身 `groundOffsetY`
- 朝向规则要统一使用 `getFacingTowardPlayer`
一句话总结:
- 角色型 NPC 不应该单独发明一套站位系统,而应该尽量复用怪物对位系统。
## 7. “新增流程”不要破坏原有选择 UI
这次初始同伴功能就是一个典型经验:
- 用户原本对“选择扮演角色”的视觉和交互已经形成预期
- 如果为了加“初始同伴选择”直接把角色选择页改成另一种样式,会造成明显割裂
经验:
- 新流程优先插在旧流程后面,而不是重写旧流程
- “确认角色 -> 选择初始同伴 -> 进入冒险”比“把原选角页改成全新样式”风险小很多
- 如果必须改 UI也要尽量保留旧页面的视觉结构和交互节奏
## 8. 冒险页布局要优先保证画面和选项完整可见
冒险页真正的优先级是:
1. 上方画布要在一屏内正常显示
2. 下方 3 个选项要在一屏内正常显示
3. 剧情文本框剩余空间自适应
经验:
- 文本框不能无限长撑开
- 正确做法是:
- 文本框高度自适应剩余空间
- 文本超长时内部滚动
- 不能让 `storyText` 把战斗画面和选项挤出首屏
## 9. 编辑器页和玩家页要明确隔离
当前项目里存在编辑器页面:
- `PresetEditor`
- `NpcVisualEditor`
- `StateFunctionEditor`
玩家页和编辑器页的需求完全不同。
经验:
- 像字体切换、视觉统一这种全局改动,不要直接打到整个站点
- 应该只挂在非编辑器根容器上
- 比如 `fusion-pixel-app` 这种类,只挂在正式游玩界面,不挂在编辑器根节点
## 10. 构建环境问题要项目内消化
实际踩到的构建问题:
- Node 16 环境下Vite 构建会因为 `crypto.getRandomValues` 缺失报错
沉淀出的解决方式:
- 不强依赖开发机立刻升级 Node
- 在项目内增加 Vite 启动包装脚本
- 统一让 `dev / build / preview` 都走这层 shim
经验:
- 环境兼容问题如果能在项目内吸收,就尽量不要把负担转移给每个协作者
- 文档里要明确记录“为什么这样做”
## 11. 比较稳的开发顺序
后续继续扩展功能时,建议遵守这个顺序:
1. 写状态字段
2. 写规则工具函数
3. 写流程 hook
4. 接 UI
5.`npm run lint`
6.`npm run build`
7. 再做视觉微调
不要反过来做:
- 先做 UI
- 再补状态
- 最后硬修流程
这种顺序在状态复杂的项目里会越改越乱。
## 12. 当前最值得继续坚持的原则
- 保持 AI 生成和本地规则分工清晰
- 保持角色型 NPC 与普通 NPC 的渲染分流
- 保持“怪物 / encounter / 战斗 NPC”统一坐标系
- 保持新增功能不破坏既有核心 UI 体验
- 保持编辑器页与玩家页隔离
- 每次大改后都用 `lint + build` 双重验证
## 13. 后续建议
下一阶段最值得继续沉淀的方向:
1. 把 NPC 交互逻辑继续从 `useStoryGeneration` 中独立成更纯粹的 `useNpcInteractionFlow`
2. 把角色型 NPC 的位置、缩放、贴地参数做成可配置规则,而不是继续散落在画布里微调
3. 针对初始同伴流程补一份单独的状态图 / 时序图
4. 对大 chunk 警告做代码分包
## 14. 一句话总结
这个项目真正的开发经验不是“怎么多写一个按钮”,而是:
- 在 AI 叙事、像素演出、战斗状态、NPC 规则、选择流程和编辑器体系同时存在的情况下,始终让每条链路各归其位。

View File

@@ -0,0 +1,266 @@
# 奇幻酒馆项目开发经验手册
日期:`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. 一句话总结
这个项目最重要的经验不是“做了多少页面和功能”,而是:
**必须把 AI 文本生成、本地规则、动画演出、场景状态、编辑器工具这几套系统严格分层,再通过 function 和统一状态流把它们重新接起来。**
## 14. 相关文档
如需继续细看已有沉淀,可结合以下文档一起阅读:
- `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`

25
docs/experience/README.md Normal file
View File

@@ -0,0 +1,25 @@
# 经验沉淀
这一组文档主要回答两个问题:
- 这个项目开发时有哪些稳定有效的方法论。
- 遇到 UI、运行时、编辑器、AI 边界问题时,优先应该怎么判断。
## 推荐入口
1. [PROJECT_WORK_EXPERIENCE_PLAYBOOK.md](./PROJECT_WORK_EXPERIENCE_PLAYBOOK.md):最完整的项目开发手册,适合先建立全局认识。
2. [PROJECT_DEVELOPMENT_EXPERIENCE.md](./PROJECT_DEVELOPMENT_EXPERIENCE.md):项目级经验浓缩版,适合快速回顾。
3. [ADVENTURE_RUNTIME_DEV_EXPERIENCE.md](./ADVENTURE_RUNTIME_DEV_EXPERIENCE.md)专门看运行时、战斗、演出、NPC 流程时优先读。
4. [MOBILE_UI_DEV_EXPERIENCE.md](./MOBILE_UI_DEV_EXPERIENCE.md):做移动端/游戏 UI 时的布局和交互经验。
5. [AGENT_UI_CHANGELOG.md](./AGENT_UI_CHANGELOG.md):当前 UI 改动脉络、资产约束和已知坑。
## 历史实现经验
- [CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md](./CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md):偏“这类需求怎么拆链路”的实战经验。
- [CODEX_PAST_WORK_EXPERIENCE_SUMMARY.md](./CODEX_PAST_WORK_EXPERIENCE_SUMMARY.md):偏“之前做过什么、怎么做的”的历史记录。
## 使用建议
- 只需要读一份时,优先看 `PROJECT_WORK_EXPERIENCE_PLAYBOOK`
- 做 UI 改动时,把本目录和根目录的 `UI_CODING_STANDARD.md` 对照着看。
- 做运行时流程改动时,把本目录和 `docs/audits/engineering/README.md` 一起看,能更快发现风险边界。