init with react+axum+spacetimedb
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-26 18:06:23 +08:00
commit cbc27bad4a
20199 changed files with 883714 additions and 0 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,18 @@
# Agent 空会话草稿可见性修正 2026-04-26
用户从创作中心点进 RPG 或大鱼吃小鱼工作台时,后端会立即创建 Agent session并写入一条助手欢迎消息。但在用户尚未发送任何消息、也没有传入种子文本时这个 session 只是临时工作区,不应进入“我的创作”草稿列表。
本次规则:
1. 只有存在用户消息、非空 seedText、真实草稿数据或已发布状态时Agent session 才算作品草稿。
2. 助手欢迎消息、默认 anchorPack、空 `{}` draftProfile 不算用户创作内容。
3. 过滤必须落在后端 works 聚合层,前端创作中心只消费结果,不负责隐藏空草稿。
4. RPG 仍保留已发布 profile 和孤立持久草稿 profile 的展示;未发布且仍有活跃 Agent session 的编译 profile 继续去重。
涉及入口:
- `server-rs/crates/spacetime-module/src/lib.rs`
- `server-rs/crates/spacetime-module/src/custom_world/mod.rs`
- `server-rs/crates/spacetime-module/src/big_fish/session.rs`
后续如果新增玩法创作 Agent也必须复用同一判断创建会话不等于创建草稿作品列表只展示已经被用户实际开始编辑或已经生成结果的会话。

View File

@@ -0,0 +1,210 @@
# 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` 保证环境变量加载稳定。
---
## 8. 2026-04-18 / 2026-04-20 账号入口补充记录
- 早期方案曾在 `AuthGate` 层提供右上角全局账号信息条,并在 `GameShellRuntime` 中临时隐藏。
- 2026-04-20 起,这个全局悬浮入口已整体下线,不再区分“平台显示 / 冒险隐藏”。
- 原因是右上角高频观察区不适合承载账号入口,且平台内已经有更明确的页面内入口。
- 当前账号相关入口统一保留在平台首页受保护动作、个人页、存档页与账号弹窗,不再占用全局悬浮层。
---
## 9. 2026-04-20 等级 HUD / 冒险布局补充
- 当前运行中的等级 UI 已从 `AdventurePanel` 底部移出,改为放在 `GameShellRuntime` 左上角固定 HUD避免把主对话区挤短。
- 左上角 HUD 复用 `CharacterInfoShared.tsx` 里的 `PlayerLevelProgress`,角色面板、实体详情、游戏 HUD 使用同一套等级进度表现。
- `AdventurePanel` 不再承担等级条展示,底部交互区只保留队伍 / 背包 / 刷新 / 退出聊天 / 选项 / 自定义输入,并压缩了底部留白与面板间距。
- 角色信息不只在总 HUD 里显示:`CharacterPanel` 的队伍成员卡、角色详情面板,以及 `AdventureEntityModal` 的实体详情头部都会展示角色身份与等级信息。
- 队长展示正式 `Lv.`;同行角色展示“参考 Lv.”NPC 优先展示运行时 `levelProfile.level`,这样 UI 只负责表现,不在前端虚构额外成长逻辑。
- 左上角等级 HUD 不使用背景框体,仅保留 `Lv`、等级数字与极细经验线,避免遮挡场景背景与移动端视野。
---
## 10. 2026-04-20 平台亮色主题主 Tab 修正
- `PlatformHomeView.tsx` 的四个主 Tab首页 / 创作 / 存档 / 我的)现在统一挂在 `platform-remap-surface` 下,让亮色主题能接管历史遗留的 `text-zinc-*``bg-black/*``border-white/*` 组合。
- 平台首页卡片覆层不要在组件里继续写死深色 `rgba(8,10,14,...)` 渐变;这次已收口为 `--platform-card-overlay-soft``--platform-card-overlay-strong``--platform-card-overlay-deep`,明暗主题都从 token 走。
- 平台桌面顶栏里的账号头像、移动端底部主 Tab 分隔线,也不要保留暗色主题时留下的固定蓝色渐变和深色边线,应直接使用平台主题变量(如 `--platform-profile-avatar-fill``--platform-line-soft`)。
- 后续如果继续调整平台主 Tab 视觉,优先改 `src/index.css` 的平台主题 token 和 remap 规则;只有 token 无法表达时,再做局部组件样式补丁,避免亮色主题再次出现“页面整体是亮的,但局部卡片仍是暗的”。
- 参考图方向已明确:平台亮色主题应以白色为主底色,粉红只承担背景气氛和重点 CTA不应让整页主壳继续像深粉底板。
- 移动端底部 `platform-bottom-nav` 的 Tab 激活态必须与默认态使用同一套盒模型;边框要预占位,不能在 onPress / active 时临时增加边框导致按钮尺寸和留白跳变。
- 2026-04-20 第二轮细查补色时,继续把 `PlatformWorldDetailView.tsx``PlatformHomeView.tsx``PreGameSelectionFlow.tsx` 里落在白底/浅底面板上的标题、说明、次级标签、搜索栏和加载兜底文本显式切回平台亮色 token避免亮色主题下继续出现浅底白字或过浅灰字。
- 2026-04-20 第三轮修正方向后,平台首页移动端底部 `platform-bottom-nav` 的高度、内边距、按钮圆角、图标尺寸、标签字号统一收口到 `src/index.css` token`PlatformHomeView.tsx` 只保留结构类,避免 `h-14`、容器 padding、按钮内部内容间距和 active 底座各自维护半套尺寸,导致选中态看起来比 Tab 槽位更矮或更高。
- 2026-04-20 第四轮把平台亮色主题顶部过重的红色收轻:`--platform-body-fill``--platform-hero-fill``--platform-shell-glow-*``--platform-surface-glow-*` 改成更接近暖白 + 浅珊瑚的低饱和版本,首页 / 创作页 / 详情页 Hero 覆层统一改走 `--platform-hero-overlay-strong`,避免组件里继续写死高饱和粉红渐变。
---
## 11. 2026-04-20 创作 Agent 聊天工作台亮色主题补色
- `src/components/custom-world-agent/*` 这一条创作 Agent 工作台链路已统一切回 `platform-subpanel``platform-input``platform-button``platform-banner``platform-progress-track`,亮色主题下不再继续裸露 `bg-[#111318]``bg-black/*``bg-white/*``text-white` 这类历史深色残留。
- 聊天线程中的用户气泡、助手气泡、系统消息、推荐回复按钮、流式回复态统一映射到平台 token后续如果继续调整创作 Agent 聊天视觉,优先改平台 token 或平台 class不要在组件里再单独写一套聊天色板。
- 顶栏、操作横幅、进度条、输入框的状态色统一复用平台亮暗主题变量,避免再次出现“页面整体已切亮色,但 Agent 局部还是旧暗色弹层”的割裂感。
---
## 12. 2026-04-20 NPC 聊天退出恢复与文本阅读性修正
- `AdventurePanel.tsx` 的叙事 `storyText` 已取消斜体,改为更大的正文尺寸,避免长段阅读时发飘。
- 冒险面板里的 `actionText` 统一上调到聊天态同级字号;`detailText` 不再默认渲染,保持底部选项区更清爽。
- `npcEncounterActions.ts` 在“退出聊天”后重新续写剧情时,会优先把当前故事里最近一轮已经呈现给玩家的非聊天选项文案并回 `optionCatalog`,避免高好感聊天收束后又退回 NPC 静态 fallback 文案。
---
## 13. 2026-04-21 创作中心失效草稿恢复兜底
- `src/components/rpg-entry/useRpgEntryLibraryDetail.ts` 现在会识别 `custom-world agent session``404 NOT_FOUND` 读取失败,不再把这类错误直接冒泡成未捕获 Promise。
- 当用户在创作中心点击“继续创作”命中失效草稿时,前端会主动清空 `customWorldSessionId` 恢复参数,并刷新一次 works 列表,避免刷新页面后反复尝试恢复同一个坏会话。
- 当前交互已收口成平台内可见提示:用户会停留在创作中心,并看到“这份共创草稿已失效,已为你返回创作中心,请重新开始创作。”,而不是卡在空白工作区或只在控制台看到英文异常。
- 这次兜底只处理失效会话恢复,不改变正常草稿继续创作、结果页恢复和已发布作品进入世界的主链。
---
## 14. 2026-04-24 Agent 工作区恢复指针按用户隔离
- `custom-world agent session` 现在由 `server-rs` Axum 路由接入 SpacetimeDB procedure模块内按 `owner_user_id + session_id` 查询;前端恢复旧工作区前必须确认本地指针属于当前登录用户,否则旧账号残留会先请求一次 `/api/runtime/custom-world/agent/sessions/:sessionId` 并产生 404。
- `src/services/customWorldAgentUiState.ts` 的 URL 仍只保留 `customWorldSessionId` / `customWorldOperationId`,用户归属只写入 `sessionStorage`,避免把 `userId` 暴露到可分享链接里。
- `src/components/rpg-entry/useRpgCreationSessionController.ts` 在恢复初始工作区时会对比 `ownerUserId`,发现不是当前用户就清空恢复指针并停留在创作入口,不再打后端失效 session。
- 后续新增 Agent 类恢复入口时,同样要区分“可分享的 URL 指针”和“仅本机使用的登录用户归属”,不要只凭 sessionId 自动恢复受保护资源。
---
## 15. 2026-04-24 多玩法 Agent 聊天顶部文案统一隐藏
- `CreationAgentWorkspace` 已支持在 `title``assistantSummary` 为空时只展示返回、主操作、进度与锚点区域;各玩法适配层不要再传入“世界共创 / 玩法共创”这类模块标题或引导副文案。
- `custom-world``big-fish``puzzle` 三条 Agent 聊天工作区现在统一隐藏顶部标题与标题下方说明,避免只有 RPG / 自定义世界生效、其他玩法模板仍残留旧文案。
- 后续新增玩法模板时,聊天页顶部模块应保持清爽:必要状态放进进度、操作横幅或聊天消息,不把功能说明类文案默认写入 UI。
---
## 16. 2026-04-24 创作结果页亮色主题细节补色
- RPG / 拼图 / 大鱼结果页根容器统一挂 `platform-remap-surface`,让亮色主题能接管遗留的 `text-white``text-zinc-*``bg-white/*``bg-black/*` 和状态色工具类。
- 拼图与大鱼结果页顶部 Hero 只增加 `platform-result-hero` 语义类,不改变整体布局;亮色主题下由 `src/index.css` 统一换成暖白底、轻粉高光和平台主按钮色。
- 地图弹窗新增 `map-modal-overlay``map-modal-shell``map-modal-backdrop``map-modal-shade``map-info-panel` 语义类;亮色主题通过这些类降低暗色遮罩、提亮地图背景、统一节点卡与连线颜色。
- 结果页中保存、生成、发布等旧的 `bg-amber-600` / `bg-cyan-600` / `bg-cyan-200` 按钮,在 `platform-remap-surface` 内被映射回 `platform-button--primary` 同款渐变,避免亮色主题下按钮体系割裂。
- 后续继续做结果页 UI 细节时,优先补语义 class + `src/index.css` 的 light remap不要在每个结果页组件里复制一套亮色配色也不要调整页面整体布局结构。
---
## 17. 2026-04-26 创作编辑器关闭确认弹窗亮色主题修正
- `RpgCreationEntityEditorShared.tsx` 的未保存关闭确认统一收口为 `CloseConfirmDialog`,弹窗只保留确认信息和两个动作,不新增说明文案。
- `CloseConfirmDialog` 通过 `platform-close-confirm-dialog` 语义类接入平台主题 token提示块使用 `--platform-warm-*`,确认按钮使用 `--platform-button-primary-*`,继续编辑按钮使用 `--platform-neutral-*`
- 后续新增关闭 / 退出确认面板时,不要继续复制 `text-amber-50``text-sky-50``bg-black/*` 这类深色 Tailwind 组合;优先复用语义类,避免亮色主题出现浅底白字和按钮文字不可读。
---
_文档目的交接给下一个 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` 嵌入 `PresetEditor` 的 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,310 @@
# 当前游戏全流程体验报告2026-04-07
## 1. 报告说明
本次报告基于 `2026-04-07` 仓库现状完成,目标不是评审 PRD而是从玩家进入游戏的第一秒开始顺着当前可达链路实际跑一遍记录“能不能玩、玩到哪、哪里出戏、哪里已经有感觉”。
本次模拟采用两段式验证:
- 开发服验证:直接访问本地 `http://127.0.0.1:3000`
- 临时生产包试玩:执行 `node scripts/vite-cli.mjs build --outDir temp_playtest_build` 后,通过静态服务器预览
- 试玩视口:移动端优先,约 `430 x 932`
需要先说明一个前提:
- 当前开发服首页会被 Vite 错误遮罩拦住,玩家无法直接进入游戏
- 为了继续完成全流程体验,我改走了临时生产包试玩
- 临时生产包没有本地 `/api` 代理,因此剧情区会持续出现 `501 Unsupported method ('POST')` 的报错文案AI 文本体验会被明显污染
因此,这份报告同时包含两类结论:
- 一类是“当前版本玩家真实会撞到的入口问题”
- 一类是“绕过入口问题后,主流程骨架本身的可玩性表现”
---
## 2. 本次实际跑通的流程
本次实际走通的路径如下:
1. 启动页
2. 世界选择
3. 角色选择
4. 进入营地开场
5. 首次剧情抉择
6. 任务更新
7. 场景移动到 `宫苑内庭`
8. NPC 首次互动
9. 交易面板
10. 地图弹窗
11. 队伍面板
12. 背包 / 工坊面板
13. 设置面板
14. 保存并退出
15. 继续游戏恢复存档
结论先说:
- 主流程骨架已经成型,已经不是“只有页面没有游戏”
- 进入营地、触发任务、切场景、遇 NPC、开交易、开地图、看队伍、看背包、保存继续这一整圈是能跑下来的
- 但入口稳定性、错误文案兜底、语言一致性、部分中文乱码,已经直接影响玩家的第一轮真实体验
---
## 3. 分阶段体验记录
## 3.1 启动页
玩家第一眼看到的是一个相对简洁的开始页,只有标题、开始按钮、开发团队入口和联系方式,节奏是对的,确实更像游戏入口,不像表单式 Demo。
正向感受:
- 开始动作很聚焦,玩家不会迷路
- 首屏信息量不大,移动端阅读负担低
- “开始游戏 / 新游戏 / 继续游戏”的结构清晰
问题:
- 当前开发服并不能进入这个页面,实际先看到的是 Vite 错误遮罩
- “开发团队 / 联系方式”仍然偏开发样态,正式玩家会有一点出戏
## 3.2 世界选择
世界选择页目前有 `武侠``仙侠``自定义世界` 三个主要入口。对玩家来说,这一页的信息组织已经够直观:世界名、气质、副标题、在线人数氛围标签都能快速帮助判断。
正向感受:
- 选世界成本低,点击欲望明确
- 自定义世界入口放得足够醒目,不会被埋
- 武侠 / 仙侠区分清楚,符合开局决策直觉
问题:
- “在线人数”更像氛围数字,不像真实系统状态,容易被当作假在线
- 当前页面更偏“卡片入口”,还没有形成非常强的世界身份记忆点
## 3.3 角色选择
角色选择页已经具备“选人进入冒险”的基本仪式感。当前可见角色包括 `剑之公主``神箭游侠``双刃旅者``破军拳师``玄甲战锋`。属性、背景故事、性格标签和详情入口都齐了。
正向感受:
- 选角信息够完整,能形成第一轮角色代入
- “背景故事 + 标签 + 属性”三层信息组织合理
- 移动端视口下仍然能完成浏览和确认
问题:
- 角色页里的“自定义 / 详情”会让玩家在开局阶段产生一点分心
- 当前角色差异更多停留在说明层,开局前还没完全转化成“我为什么要选这个人”的强动机
## 3.4 营地开场
选择角色进入营地后,游戏会先给一段开场对话,再给玩家两个非常关键的初始决策:
- 先问问对方为什么会出现在这里
- 直接前往下一场景
这一段是目前全流程里最像“正式游戏开局”的部分之一。
正向感受:
- 营地比直接扔进战斗更稳,给了玩家进入状态的缓冲
- 开场对话能自然把关系、任务感和前路危险感一起立起来
- “先聊聊再走 / 直接上路”是个很好的第一轮分流
问题:
- 在静态试玩环境下,剧情区会同时出现完整 `501` HTML 报错,极度出戏
- 开场后文本会夹杂英文 fallback语言氛围被打断
## 3.5 首次选择后的反馈
当我选择“先问问你为什么会出现在这里”后,系统会立即给出关系变化与互动解锁反馈,例如可继续触发:
- 交易
- 切磋
- 前往下一场景
这一点说明“问一句话不是纯文案,而是会打开玩法分支”,这非常重要。
正向感受:
- 玩家会感觉自己的选择真的改变了接下来能做什么
- NPC 不是摆设,至少已经能作为玩法节点工作
- “营地开场 -> 关系松动 -> 解锁交互”这条节奏是成立的
问题:
- 互动文案和角色名偶尔会出现中英混用
- 某些反馈更像系统摘要,而不是完全沉浸式叙事
## 3.6 场景切换与任务推进
离开营地前往 `宫苑内庭` 后,系统会弹出任务更新提示,随后给出新的场景内可选路线,例如:
-`旧宫侍女` 交谈
- 继续向前探路
- 前往 `铸坊工场`
这是当前版本最能证明“游戏不是单房间对话器”的一段。
正向感受:
- 切场景后有明确任务更新,玩家知道自己没有在空转
- 新场景不是纯背景图替换,而是伴随新的实体和路径选择
- “NPC / 探索 / 场景跳转”三种入口并列,主循环味道出来了
问题:
- 任务弹窗表达比较清楚,但质感仍偏功能通知
- 任务标题、阶段名、剧情节拍这些信息有时偏系统化,缺少一点戏剧包装
## 3.7 NPC 互动与交易
`宫苑内庭``旧宫侍女` 接触后,可以进一步进入交易。交易弹窗中已经具备:
- NPC 名称
- 玩家当前货币
- 对方库存
- 购买数量调整
- 总价计算
- 取消 / 确认购买
这是当前版本完成度相对高的一段玩法。
正向感受:
- 交易不是假按钮,而是完整闭环
- 商品分类、稀有度、价格都能读懂
- 作为玩家,会明显感觉“这个 NPC 是有功能的”
问题:
- 从“与 NPC 交谈”直接跳到“交易 / 战斗 / 切磋”,中间缺少一层更自然的对话承接
- 当前交易更偏功能正确,角色气质和商品叙事关联还不够强
## 3.8 战斗 / 切磋
我实际触发了切磋和场景战斗。进入后,系统会给出带有数值提示的选项,例如:
- 耗蓝
- 伤害
- 正面压制 / 稳扎稳打 / 假动作切入
这说明战斗不是纯文案,而是有明确本地规则参与的。
正向感受:
- 玩家能看见技能选择的直接代价和收益
- 战斗选项语义比较明确,不是模糊散文式描述
- 从营地切磋到场景战斗的承接是通的
问题:
- 在本次静态试玩里,战斗文本会出现英文 fallback
- 战斗推进感还不够强,玩家能感知到“进入战斗了”,但还不够容易感知“这一手到底让局势前进了多少”
## 3.9 地图、队伍、背包
我实际打开了地图、队伍和背包。
地图方面:
- 点击场景名可直接打开地图弹窗
- 当前场景和相邻场景关系可见
- `宫苑内庭` 可看到 `雨夜长街``铸坊工场``地宫通道`
队伍方面:
- 队伍列表能打开
- 可读到主角状态、标签数、适配倍率
- 但面板里已经出现明显乱码,如 `闃熼暱`
背包方面:
- 能看到初始资源、材料和工坊配方
- 锻造 / 合成入口都已经在主流程里
- 玩家会明确感觉到“我不是只有剧情,我还有 build 和资源循环”
整体判断:
- 三个面板都不是空壳
- 地图和背包的功能价值已经足够成立
- 队伍页的信息密度没问题,但乱码已经直接破坏观感
## 3.10 保存并退出 / 继续游戏
设置面板中已经有:
- 音乐音量
- 运行统计
- 保存并退出
我实际触发了“保存并退出”,随后回到开始页,再点击“继续游戏”,能够恢复到先前场景和流程状态。
这是本次试玩里最让我放心的一条链路。
结论:
- 存档与继续不是摆设,是真的通了
- 这让整套流程第一次具备了“可以连续玩,而不是每次重开”的基础感
---
## 4. 当前版本最明显的优点
1. 主循环骨架已经成立。开局、选世界、选角色、营地、任务、切场景、NPC、交易、战斗、背包、地图、存档这些点已经能串起来。
2. 移动端优先思路是对的。至少在窄屏下,核心路径没有因为布局崩掉而不可玩。
3. 功能入口大多不是假入口。交易、地图、背包、保存继续都是真能执行的。
4. “AI 叙事 + 本地规则”的边界能感知到。尤其战斗选项里的耗蓝 / 伤害提示,已经把规则感立起来了。
---
## 5. 当前版本最影响玩家体验的问题
## P0
1. 开发入口直接被错误遮罩拦住。当前 `http://127.0.0.1:3000` 不是“有点瑕疵”,而是玩家根本进不去。
2. 标准构建命令当前不可用。`npm run build` 会因为 `dist` 清理阶段的 `EPERM` 失败,说明发布路径并不稳定。
3. 没有 `/api` 代理时,剧情区会直接显示完整 `501` HTML 错误正文,沉浸感几乎被瞬间打穿。
## P1
1. 多处英文 fallback 直接进入正式体验例如营地、NPC 接触、切磋文本。
2. 队伍面板已经出现玩家可见乱码,如 `闃熼暱``褰撳墠濮旀墭` 一类内容。
3. 冒险主标签栏被隐藏后,玩家主要依赖小按钮进入队伍/背包,主导航层级不够直观。
4. NPC 首次互动到“交易 / 战斗 / 切磋”的切换偏硬,少了一层更自然的剧情过渡。
## P2
1. 世界页和角色页已经能用,但记忆点还不够强,个体世界身份和角色差异还可以再拉开。
2. 任务提示偏功能型,情绪包装和戏剧感还可以继续加强。
---
## 6. 玩家视角总结
如果只从“玩法骨架”看,这个项目已经不是 PPT也不是只有几个页面的壳。它已经有一条能走完整圈的游戏主流程而且最关键的交易、地图、任务、战斗、存档都不是假的。
但如果从“当前玩家第一次打开就会得到什么体验”来看,问题也很直接:
- 入口不稳
- 构建不稳
- 离线 / 无代理时错误文案直接冲到脸上
- 中英混用和部分乱码会快速打断沉浸
一句话总结:
**当前版本已经具备“能玩一圈”的核心骨架,但距离“放心交给玩家体验”还差一次很扎实的入口修复、错误兜底和文本统一收尾。**
---
## 7. 建议的下一步
建议优先顺序如下:
1. 先修入口可玩性:解决开发服错误遮罩、`build` 清理失败、静态环境错误文案泄露。
2. 再修体验一致性:清掉玩家可见英文 fallback 和明显乱码。
3. 然后打磨主循环表达让营地开场、任务更新、NPC 接触这三段更有戏。
4. 最后再扩内容:因为现在真正限制体验的不是“内容太少”,而是“入口和表达不够稳”。

View File

@@ -0,0 +1,218 @@
# 奇幻酒馆 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`,保证语义清晰、后续也方便测试。
### 4.5 冒险主场景不要挂右上角账号悬浮条
- 冒险页右上角属于画面演出和战斗/剧情信息的高频观察区。
- 全局账号信息条挂在这里,会直接压住场景、敌人血条或顶部提示,手机端尤其明显。
- 结论:
账号入口应收回平台首页、个人页或设置面板,不要在实际冒险主场景常驻悬浮显示。
- 当前仓库已进一步收口为:
不再提供右上角全局账号悬浮条,统一只保留页面内入口与独立账号面板。
### 4.6 冒险主场景双方角色必须按画面中线镜像
- 非滚动画面的交谈、预览、单体对峙态,主角和对面角色不能分别用左右边距、世界坐标和角色宽度重复推算。
- 正确做法是先定义“角色容器中心距离画面中线”的统一间距,再让主角中心落在中线左侧、对面角色中心落在中线右侧。
- 角色容器宽度、角色图片缩放和左右朝向必须各自独立处理,不能用额外 left inset 去修正角色图片,否则会破坏左右对称。
- 自定义图片 NPC、模板角色和组合式 NPC 都要进入同一 112px 场景容器,再按各自素材锚点做场景缩放,保证视觉大小不漂移。
## 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 的移动端优化,本质不是把元素缩小,而是重组视觉重心、固定操作锚点、让焦点内容在一屏内自然成立。**
### 10.1 可扮演角色形象预览保持 1:1
- 可扮演角色的形象预览容器统一使用 1:1 方形,入口选择轮播、角色资产工坊和结果页角色卡片都不能用纵向长卡片去拉伸预览图。
- 预览图片本身使用 `object-contain`,保证 AI 生成主形象、模板像素角色和运行时动画都在方形容器内完整显示,不裁切角色主体。
- 卡片可以在方形预览下方放角色名、称号、状态等信息,但这些文本区不能反向影响预览区比例。
- 编辑角色弹窗也遵循同一规则:移动端不能用固定高度压扁预览区,预览容器应随宽度保持 `aspect-square`
### 10.2 运行画面怪物锚点按视觉底边校准
- 对战预览里主角和对手要沿画面中线成对出现,但纵向不能只共用一个 `bottom` 常量。
- 怪物精灵帧的空白、体型和脚底位置差异很大,运行画面应按帧高分档下沉,让怪物视觉底边落在主角同一条地面线上。
- 后续新增怪物资源时,先检查红圈标注的实际落点,再调整锚点分档或单怪物偏移,避免出现“悬在地面上方”的状态。
- 自定义世界里敌对角色已经先作为场景 NPC 存在,即使它同时携带 `characterId``monsterPresetId`,画布也不能直接沿用模板角色的 `groundOffsetY`;只要 encounter 自身有 `imageSrc``visual`,就按场景 NPC 自定义形象锚点处理。
- 幕预览运行时还会构造“无 `characterId`、但有 `visual` 的场景 NPC”这类和平相遇分支同样必须套用场景 NPC 自定义形象锚点,否则会停在画面中上部。

View File

@@ -0,0 +1,31 @@
# PC 端世界生成与草稿页布局优化说明 2026-04-24
## 目标
在移动端现有布局不变的前提下,只优化 PC 端世界生成页与世界草稿页的信息组织,让页面更紧凑、更有层次,并保留全部已有功能入口。
## 范围
- 世界生成页:`src/components/CustomWorldGenerationView.tsx`
- 世界草稿页 / 作品页:`src/components/custom-world-home/CustomWorldCreationHub.tsx`
- 新建作品入口:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`
- 作品卡片:`src/components/custom-world-home/CustomWorldWorkCard.tsx`
- 筛选标签:`src/components/custom-world-home/CustomWorldWorkTabs.tsx`
## PC 端落地规则
1. 移动端默认类名保持原布局语义,只通过 `lg:` / `xl:` 断点追加 PC 布局。
2. 世界生成页在 PC 端改为左右双栏:左侧突出进度与阶段,右侧承载玩家设定 / 结构化锚点,减少纵向滚动。
3. 世界草稿页在 PC 端将“新建作品”和“作品列表”分区强化:顶部入口更紧凑,作品卡片网格密度提升。
4. 不新增规则说明文案,不改变按钮、筛选、删除、体验、进入等功能行为。
5. 中文文本只做必要保留,不因为布局调整改写已有中文内容。
## 视觉策略
- PC 端使用更明确的 `xl:grid`、固定信息侧栏和更小间距,让主内容首屏承载更多信息。
- 卡片在 PC 端降低无效高度,操作按钮与状态信息尽量同行展示。
- 作品卡片底部统计标签必须保留在卡片圆角范围内,不能为了压缩高度让标签贴边或被 `overflow-hidden` 裁掉。
- 卡片正文摘要优先缩短行数来给底部标签留空间;当标题、摘要或标签变长时,允许卡片自然增高。
- RPG 作品卡片点击行为按作品状态分流:草稿统一继续创作,已发布作品进入详情或世界;不要只依赖 `sourceType` 判断草稿可打开性。
- 整张作品卡片需要由卡片根节点承载点击与键盘打开能力,避免透明绝对定位按钮在真实浏览器中被判定不可见,导致自动化和用户点击不稳定。
- 保留现有 `platform-*` 视觉体系,避免引入新的 UI 系统。

View File

@@ -0,0 +1,17 @@
# PC 世界档案草稿编辑页布局修正 2026-04-24
## 背景
用户反馈 PC 端世界草稿页没有明显变化。复核截图后确认实际页面是世界档案草稿编辑页中的实体目录,而不是创作首页作品列表。
## 本次修正范围
- `src/components/CustomWorldEntityCatalog.tsx`
## 落地要求
1. 移动端仍保持原来的单列滚动、顶部标签与搜索结构。
2. PC 端把超宽单列实体列表改成卡片网格,减少横向空白,提高信息密度。
3. PC 端顶部世界标题、标签、搜索和操作按钮更紧凑,避免首屏被空白标题区占用。
4. 功能不变:搜索、切换标签、新增、批量删除、选择、编辑、发布入口均保持原有行为。
5. 不新增说明类文案,不改写已有中文内容。

View File

@@ -0,0 +1,19 @@
# 平台首页 Banner 图尺寸修复记录
更新时间:`2026-04-25`
## 问题
首页 Hero / banner 使用作品封面作为背景图。全局 `.platform-surface > *` 会把所有直接子节点重新设为 `position: relative`,导致带有 Tailwind `absolute` 类的背景图和遮罩被覆盖,图片重新进入普通文档流后可能撑高首页,影响首屏布局。
## 落地
- 修改 `src/index.css`,将 `.platform-surface > *` 收敛为 `.platform-surface > :not(.absolute)`
- 保留普通内容层的 `z-index: 1`,让文字、按钮仍稳定压在背景之上。
- 让 banner 背景图继续使用绝对定位和 `object-cover`,只在固定容器内裁切显示,不参与首页高度计算。
## 经验
- 首页、结果页、详情页的背景图都必须由固定尺寸容器承载,图片本身不要参与布局流。
- 给通用容器写层级规则时,不能无差别覆盖 `.absolute` 节点,否则会破坏所有“背景图 + 遮罩 + 内容”的结构。
- 后续若新增平台 Hero优先沿用 `platform-surface--hero`,并确认背景图节点仍是 `absolute inset-0 h-full w-full object-cover`

View File

@@ -0,0 +1,20 @@
# 可扮演角色外观模板字段删除经验
## 背景
可扮演角色曾通过 `templateCharacterId` 保存“外观模板”选择。当前角色主形象已经由 `visualDescription``imageSrc` 与生成资产链路承接,外观模板不应继续作为可扮演角色档案字段暴露给用户编辑或持久化。
## 落地边界
- 可扮演角色数据结构不再声明 `templateCharacterId`
- 可扮演角色编辑面板删除“外观模板”下拉项,保存时不再补默认模板。
- 草稿规范化与资料库读取时丢弃旧数据中的 `templateCharacterId`,避免旧快照把字段带回新数据。
- 运行时如需要基础动作、默认立绘或战斗标签,只能通过角色文本、参考 profile 或固定 fallback 规则临时推导模板,不再写回角色字段。
- 资产工坊可以继续接收运行时临时模板提示,但该提示不得成为可扮演角色的持久字段。
## 验收要点
- 新建或编辑可扮演角色时界面不出现“外观模板”。
- 保存后的 `playableNpcs` 条目不包含 `templateCharacterId`
- 旧存档带有 `templateCharacterId` 时,进入当前规范化链路后会被丢弃。
- 自定义世界运行角色仍能通过推导模板获得基础动作与默认占位图,不因字段删除而中断。

View File

@@ -0,0 +1,268 @@
# 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 内,不要散落在多个组件按钮回调里。
## 3.1 AI 草稿数据进列表前,要先补本地稳定标识
自定义世界、角色草稿、澄清问题、生成结果卡片这类数据,在草稿态或兼容旧数据时,`id` 可能为空。
经验:
- React 列表的 `key` 不要直接裸用这类可能为空的 `id`
- 当前选中态、草稿缓存、轮播焦点也不要直接绑空 `id`,否则会出现“点了第二张卡,结果还是第一张卡被选中”的错位。
- 更稳的做法是:
- 业务数据层尽量补齐真实 id
- UI 层再补一层本地稳定 `selectionKey` / fallback render key
- fallback 至少带上 `index + 名称种子`,保证当前列表内唯一
## 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. SpacetimeDB 绑定桥接层要做同名去重
`server-rs/crates/spacetime-client` 里有一部分内容是围绕 SpacetimeDB 生成绑定补的手写桥接层。
经验:
- 新增 procedure、input type 或 mapper 时,先全局确认 `module_bindings/mod.rs``mapper.rs`、业务封装文件里是否已经存在同名声明
- `module_bindings/mod.rs` 同一个模块只保留一条 `pub mod` 和一条 `pub use`,不要同时放在 reducer 区和 procedure 区
- `mapper.rs` 的字符串枚举解析函数、API 入参结构只保留一个权威定义,业务侧统一复用
- 业务封装文件里同一个 procedure 只暴露一个客户端方法,避免 Rust 在编译期出现 E0428、E0252、E0119、E0592 这类重复定义错误
- 修复重复绑定时优先删除后追加的重复块,不要重写整文件,避免影响中文注释和生成绑定附近的大段内容
## 15. 一句话总结
这个项目真正的开发经验不是“怎么多写一个按钮”,而是:
- 在 AI 叙事、像素演出、战斗状态、NPC 规则、选择流程和编辑器体系同时存在的情况下,始终让每条链路各归其位。

View File

@@ -0,0 +1,280 @@
# 奇幻酒馆项目开发经验手册
日期:`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:maincloud
```
执行要求:
- 该命令是后端更新后的默认重启入口,不再使用此前的后端重启命令。
- 重启后必须继续执行与本次后端改动对应的自动测试;涉及 Rust workspace 时优先跑 `server-rs` 下的检查或测试脚本。
- 若本次改动涉及 SpacetimeDB 发布、绑定生成或 Maincloud 联调,按 `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`

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

@@ -0,0 +1,32 @@
# 经验沉淀
这一组文档主要回答两个问题:
- 这个项目开发时有哪些稳定有效的方法论。
- 遇到 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` 一起看,能更快发现风险边界。
## 近期专项记录
- [RPG_DRAFT_IMAGE_PARALLEL_GENERATION_2026-04-24.md](./RPG_DRAFT_IMAGE_PARALLEL_GENERATION_2026-04-24.md):记录 RPG 底稿阶段角色主形象与场景背景图并行生成约束。
- [PLATFORM_HOME_BANNER_IMAGE_SIZE_FIX_2026-04-25.md](./PLATFORM_HOME_BANNER_IMAGE_SIZE_FIX_2026-04-25.md):记录首页 banner 背景图不能进入普通布局流的修复经验。
- [RPG_PUBLISH_GALLERY_REFRESH_FIX_2026-04-25.md](./RPG_PUBLISH_GALLERY_REFRESH_FIX_2026-04-25.md):记录 RPG 发布后首页 / 分类页公开作品列表刷新链路。
- [AGENT_EMPTY_SESSION_DRAFT_VISIBILITY_2026-04-26.md](./AGENT_EMPTY_SESSION_DRAFT_VISIBILITY_2026-04-26.md):记录 Agent 空会话不应进入作品草稿列表的后端判定规则。

View File

@@ -0,0 +1,32 @@
# RPG 幕背景默认描述来源修正 2026-04-24
## 背景
草稿编辑器中“AI 生成幕背景”的“画面内容描述”曾出现类似“温馨员工宿舍第1幕背景玩家入职后的首个落脚处玩家会在温馨员工宿舍接住这一章的开场入口。”的默认文本。这类文本不是大模型直接写出的画面描述而是前端或后端在缺少 `backgroundPromptText` 时,把地点名、幕标题、摘要规则句拼接出来的兜底文案。
## 落地约束
1. 幕背景图的默认画面描述必须来自草稿生成链路里的关键场景生成步骤,字段源为 `landmarks[*].actBackgroundPromptTexts[*]`
2. `sceneChapterBlueprints[*].acts[*].backgroundPromptText` 只承接上述幕级大模型产物,不再用 `title``summary`、地点描述或规则句拼接。
3. 如果大模型漏产某一幕描述,后端规范化只保留空字符串,让后续生图前的 `backgroundPromptText` 校验暴露底稿质量问题,不能伪造可用默认文本。
4. 前端编辑器 sanitize 只展示已有 `act.backgroundPromptText`;缺失时留空,不能在 UI 层重新拼接默认描述。
5. 手动打开 AI 生成面板时,若字段为空,可由用户输入,但系统默认不替用户生成规则句。
## 当前实现
- `server-rs/crates/api-server/src/custom_world_foundation_draft.rs` 的关键场景框架 prompt 要求 LLM 为每个地点生成 3 条 `actBackgroundPromptTexts`
- 草稿合成阶段通过 `build_scene_chapter_blueprints_from_landmarks` 把这些幕级描述写入 `sceneChapterBlueprints[*].acts[*].backgroundPromptText`
- `normalize_scene_act_blueprint` 不再把缺失描述补成“标题 + 摘要 + 通用场景背景”格式。
- `src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx` 不再用地点名、幕标题和 `actSummary` 生成 `backgroundPromptText` fallback。
## 验收要点
- 新草稿中每一幕的 `backgroundPromptText` 应该像自然的画面描述,包含主体、前中远景、站位空间、氛围识别点。
- 不应再出现“第1幕背景玩家会在……”这类明显拼接句。
- 如果 LLM 漏掉 `actBackgroundPromptTexts`,生成幕背景图阶段应失败并提示缺少 `backgroundPromptText`,而不是静默使用拼接文案。
## 2026-04-24 并发限流错误处理补充
- 批量生成幕背景图时,`JoinSet` 子任务的成功值和失败值固定承载 `(chapter_index, act_index, message)`,用于把错误精确标记回对应章节幕。
- `Semaphore::acquire``AcquireError` 不能在子任务中转成裸 `String` 后直接使用 `?`,否则会破坏子任务统一错误类型并导致 `E0277`
- 限流器异常应映射为同一组三元组错误,保持后续 `mark_scene_act_background_generation_error` 和部分成功保留逻辑可复用。

View File

@@ -0,0 +1,23 @@
# RPG 场景幕角色配置面板布局经验 2026-04-26
## 背景
场景多幕配置里的“配置角色”面板是高频编辑弹层,移动端和桌面端都需要快速完成选择并保存。
## 本次约束
1. 面板底部不再放“取消”按钮,关闭统一交给标题栏关闭按钮和遮罩。
2. “保存角色”必须位于面板底部操作区,角色列表较长时只滚动内容区,不把保存按钮滚出视口。
3. 已选角色时仍允许“移除角色”,但移动端纵向排列时保存按钮保持在最底部。
4. 不在面板内新增功能说明文本,维持清爽编辑体验。
5. 吸底操作区必须使用平台语义色 token不能写死深色 Tailwind 背景,避免亮色主题下出现突兀深色底栏。
6. 已选角色时,底部操作区保持同一行:左侧“移除角色”热区占 1/4右侧“保存角色”占 3/4未选角色时保存按钮占满整行。
## 落地位置
- `src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx`
- `SceneActNpcSlotPickerModal`
## 后续复用
以后新增类似独立选择弹层时,优先采用“标题栏 + 中间滚动内容 + 底部固定主动作”的结构;取消类动作不要默认占据底部按钮位,避免和主保存动作抢焦点。

View File

@@ -0,0 +1,46 @@
# RPG 底稿图片并行生成说明 2026-04-24
## 背景
RPG 草稿生成进入底稿素材阶段后,角色主形象与场景幕背景图都依赖同一份结构化底稿,但二者之间没有数据依赖。旧流程先生成所有角色主形象,再生成场景背景图,导致用户需要串行等待两类图片任务。
## 落地约束
1. 角色主形象与场景背景图必须在 API 编排层并行发起,且类内每个角色、每一幕背景也必须同时调用生图接口,不能只做到“角色大类”和“背景大类”并行。这里的场景背景图指 `sceneChapterBlueprints[*].acts[*]` 中每一幕的 `backgroundImageSrc`,不是世界封面图,也不是只按章节或地点生成一张图。
2. SpacetimeDB reducer 只负责持久化操作进度和底稿写入,不承载外部 LLM / 图片生成调用。
3. 生图前必须已经有文本设定:角色主形象使用角色对象的 `visualDescription`;幕背景图使用对应幕的 `backgroundPromptText`。缺字段时应中断并暴露底稿质量问题,不能退回 `description``summary` 或通用兜底词直接生图。
4. 并行分支各自基于同一份底稿副本写入素材字段,完成后只合并背景图生成产物字段,避免覆盖角色图片字段或其他草稿内容。
5. 单个大类失败仍按原有失败语义终止底稿写入,保留“生成角色主形象失败”和“生成幕背景图失败”的进度提示。
## 当前实现
- `server-rs/crates/api-server/src/custom_world.rs``spawn_custom_world_draft_foundation_job` 中使用 `tokio::join!` 同时执行:
- `generate_draft_foundation_role_visuals`
- `generate_draft_foundation_act_backgrounds`
- 角色分支使用 `JoinSet` 把所有角色主形象任务一次性投递,返回后再按角色位置写入 `imageSrc``generatedVisualAssetId`
- 背景分支使用 `JoinSet``sceneChapterBlueprints[*].acts[*]` 的每一幕背景任务一次性投递,返回后写入 `backgroundImageSrc``backgroundAssetId``generatedScenePrompt``generatedSceneModel`
- `merge_generated_act_backgrounds` 只把背景图字段合并回角色分支副本,再进入后续草稿卡编译和 SpacetimeDB 写入。
- 幕背景 prompt 同时兼容 `backgroundPromptText``scenePromptText``visualPromptText``promptText``imagePromptText``backgroundPrompt``visualPrompt`,避免 LLM 输出字段别名导致整批背景图被误判缺失。
- 每个角色主形象、每一幕背景图都必须独立自动重试,单项最多尝试 3 次。幕背景图允许部分成功:只要至少一幕成功,就必须保留已成功写入的 `backgroundImageSrc` 并继续生成草稿卡;全部幕都失败时才把素材阶段标记为“生成幕背景图失败”。
- 图片任务仍然一次性投递,保证角色与幕背景两类任务不回退到串行编排;但真正请求上游生图服务时必须共用并发闸门。并发数由 `GENARRATIVE_DRAFT_ASSET_GENERATION_MAX_CONCURRENT_REQUESTS``DRAFT_ASSET_GENERATION_MAX_CONCURRENT_REQUESTS` 配置,默认 4避免固定为 2 导致多角色、多幕草稿总耗时过长。
- 幕背景图失败文案必须带第几章、第几幕和幕标题不能只显示“第1幕 / 第2幕 / 第3幕”否则多章节同名幕会被用户误认为同一失败项重复上报。
- 中止或部分失败前必须持久化已经成功生成的部分底稿到会话 `draftProfile`,不能因为某个角色或某一幕失败而丢掉其它已生成的 `imageSrc / generatedVisualAssetId / backgroundImageSrc / backgroundAssetId`
- 每一幕自动生图必须记录 operation、session、第几章、第几幕、sceneId、sceneName、attempt、elapsedMs 与供应商真实错误,避免再次出现只看到“生成幕背景图失败”但无法定位哪张图、哪次请求、哪个上游原因的问题。
- 前端看到 `draft_foundation` operation completed 后,不能只延迟一次就读取 `resultPreview`SpacetimeDB 写入、API 读模型和前端状态同步之间可能有短暂时差,必须短轮询等待结果页 profile 可用后再自动跳转到草稿页,避免卡在“底稿已整理”。
- 前端 `CharacterAnimator` 对带 `generatedVisualAssetId` 但尚无 `animationMap` 的自定义角色,所有状态优先渲染生成主图;只有真正发布了动作集后才按动作帧播放,避免运行或战斗状态回落到模板 sprite。
## 后续注意
如果后续图片供应商出现强限流,再在网关层做队列或供应商侧限流;不要在 RPG 底稿编排层恢复逐张串行,否则会重新退化成多张图片总耗时累加。
## 2026-04-25 补充:开局场景也必须逐幕生成背景图
本次排查发现旧草稿合成只从 `landmarks` 编译 `sceneChapterBlueprints`,导致 `camp` 开局场景只有 `camp.imageSrc`,没有进入 `sceneChapterBlueprints[*].acts[*]` 的幕背景生成队列。后续实现必须遵守:
1. `camp` 开局场景必须作为 `sceneChapterBlueprints[0]` 写入,`sceneId` 默认使用 `camp.id`,缺失时使用 `camp-1`
2. `camp.actBackgroundPromptTexts` 必须包含 3 条逐幕画面描述,并和普通场景一样生成 `acts[*].backgroundImageSrc`
3. 结果页场景目录可用场景图兜底展示旧草稿的幕缩略图,但新草稿不能只依赖兜底,必须让开局场景真实进入幕背景图生成链路。
4. 手动同步场景资产时,必须同时更新 `sceneChapterBlueprints` 与兼容字段 `sceneChapters`,当前主链以 `sceneChapterBlueprints` 为准。

View File

@@ -0,0 +1,36 @@
# RPG 发布作品广场刷新修复 2026-04-25
## 背景
已发布 RPG 作品会进入 `custom_world_profile`,并由 SpacetimeDB 模块同步到公开读模型 `custom_world_gallery_entry`。平台首页和分类页不直接读取“我的作品”,而是共同消费 `/api/runtime/custom-world-gallery` 返回的公开作品列表。
本次问题表现为:结果页显示发布完成后,作品没有立即出现在平台首页和分类页。
## 修复原则
1. `publish_world` 必须写入真实作者公开信息。
- Axum 层在执行 `publish_world` 前补充 `authorPublicUserCode``authorDisplayName`
- SpacetimeDB 模块发布时优先使用 payload 中的作者公开信息,再退回历史兜底。
2. 发布完成后必须刷新公开广场列表。
- 前端 `executePublishWorld` 等待发布 operation 完成后,同时刷新 `refreshPublishedGallery()``refreshCustomWorldWorks()`
- 首页和分类页都复用 `publishedGalleryEntries`,因此刷新 gallery 后两个页面会同步看到新发布作品。
## 多玩法公开列表补充
- 平台首页 / 分类页不是只展示 RPG 作品,也需要展示已经发布到公开接口的 Puzzle 作品。
- Puzzle 已有 `/api/runtime/puzzle/gallery` 公开接口;平台入口额外读取该接口,并把 `PuzzleWorkSummary` 映射为首页卡片模型。
- 首页 / 分类页按 `rpg:{ownerUserId}:{profileId}``puzzle:{ownerUserId}:{profileId}` 去重后按发布时间排序。
- 点击 RPG 卡片仍进入 RPG 公开详情;点击 Puzzle 卡片进入现有 Puzzle 广场详情,不复用 RPG 详情链路。
3. 历史已发布作品必须能自动补齐 gallery 投影。
- 公开列表读取 `list_custom_world_gallery_entries` 前,会扫描 `custom_world_profile` 中已发布且未删除的 profile。
- 若已发布 profile 缺少 `custom_world_gallery_entry`,或缺少公开作品码 / 作者叙世号,会先补齐公开字段并同步 gallery 投影。
- 这样旧版本发布成功但未落入广场读模型的作品,在下一次首页 / 分类页读取公开列表时会自动出现。
## 经验
- 作品发布链不能只看“我的创作”列表,必须同时检查公开读模型。
- 分类页的数据不是独立接口,修首页公开列表通常也会影响分类页。
- Agent 发布链和普通 library profile 发布链要共享公开作者字段语义,避免同一个 gallery card 在不同发布入口下字段不一致。
- 已发布 profile 与 gallery 投影不是同一张表,线上修复时要考虑历史数据补投影,不能只修新增发布路径。

View File

@@ -0,0 +1,30 @@
# RPG 角色形象描述数据链路核查 2026-04-24
## 结论
草稿生成阶段会让大模型为每个可扮演角色和场景角色生成 `visualDescription`,该字段是角色主形象生成和资产工坊“形象描述”输入框的同一份默认文本来源。
本次核查发现前端 `normalizeCustomWorldProfileRecord` 曾在规范化 `playableNpcs` / `storyNpcs` 时丢弃 `visualDescription``actionDescription``sceneVisualDescription`。因此后端草稿 JSON 中有大模型生成的文字,但草稿进入前端编辑器后,资产工坊可能只能回退到 `description`,用户看不到真正的角色形象文字描述。
## 数据链路
1. 后端草稿生成:`server-rs/crates/api-server/src/custom_world_foundation_draft.rs`
- 角色框架名单 prompt 要求 LLM 输出 `visualDescription``actionDescription``sceneVisualDescription`
- `visualDescription` 定义为打开角色形象图像生成面板时默认填入的角色形象描述。
2. 后端角色主形象生成:`server-rs/crates/api-server/src/custom_world.rs`
- `generate_draft_foundation_role_visuals` 从角色对象读取 `visualDescription`
- 缺失时直接失败,提示不能在角色形象设定文本生成前生图。
- 生图成功只写回 `imageSrc``generatedVisualAssetId`,不会覆盖 `visualDescription`
3. 草稿持久化:草稿 profile JSON 保留角色对象字段,`visualDescription` 应与图片字段一起进入保存载荷。
4. 前端规范化:`src/data/customWorldLibrary.ts`
- `normalizePlayableNpc` / `normalizeStoryNpc` 必须保留三类资产描述字段。
5. 资产工坊展示:`src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModalImpl.tsx`
- modal 用角色对象构造 `baseRole`
- `buildDefaultRolePromptBundle(baseRole)` 优先把 `role.visualDescription` 转成 `visualPromptText`
- `RpgCreationRoleVisualSection` 的“形象描述” TextArea 展示 `visualPromptText`
## 验收要点
- 草稿生成完毕后,打开某个角色的资产工坊,应在“形象描述”框看到 LLM 生成的 `visualDescription`
- 如果角色有 `visualDescription`,缓存中的旧 `visualPromptText` 不应覆盖它。
- 如果角色缺 `visualDescription`,才允许前端回退到更弱的字段或缓存文本。

View File

@@ -0,0 +1,30 @@
# 世界草稿发布面板与测试入口设计 2026-04-24
## 目标
世界草稿页底部不再只有“发布并进入世界”一个动作,而是拆成两个明确入口:
1. 作品测试:跳过发布阻断项检查,直接进入当前草稿游戏体验。
2. 发布:打开发布面板,在面板内集中处理发布阻断项与封面设置,满足条件后发布到广场。
## 页面范围
- `src/components/rpg-creation-result/RpgCreationResultActionBar.tsx`
- `src/components/rpg-creation-result/RpgCreationResultViewImpl.tsx`
- `src/components/CustomWorldEntityCatalog.tsx`
- `src/components/rpg-entry/useRpgCreationEnterWorld.ts`
- `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
## 交互规则
1. 世界草稿页右下角显示“作品测试”和“发布”两个按钮。
2. “作品测试”只同步当前草稿结果并进入游戏,不触发发布阻断项检查,也不执行发布动作。
3. “发布”打开独立发布面板,不在当前页面下方展开内容。
4. 发布面板显示当前阻断项;没有阻断项时允许执行发布。
5. 发布面板显示封面预览与封面状态,并提供“设置封面”入口。
6. 封面生成、封面上传与封面预览从世界档案页迁移到发布面板;世界档案页不再展示作品封面模块。
7. 移动端仍使用底部弹层式面板PC 端使用居中 modal。
## 发布后行为
发布成功后刷新本地结果档案,并进入正式世界;作品由既有 `publish_world` 流程同步到作品库 / 广场。