3.7 KiB
RPG 幕预览启动卡载入修复(2026-04-30)
背景
编辑器内点击“幕预览”后,独立预览层会一直停在“正在载入这一幕的游戏流程...”,无法进入真实 RPG 运行壳。
根因
SceneActPreviewRuntime 先调用 handleCustomWorldSelect(profile),紧接着调用 handleCharacterSelect(previewCharacter)。
但 handleCharacterSelect() 读取的是当前 render 闭包中的旧 gameState。此时 handleCustomWorldSelect() 写入的 worldType 还没有完成 React 状态提交,所以选角入口看到 worldType 为空后直接返回。随后幕预览虽然又手动写入了 currentScene / currentScenePreset / currentEncounter,却没有写入 playerCharacter,导致 isPreviewReady 永远不成立。
另一个隐患是:有 currentEncounter 时 story controller 不会主动生成普通首段剧情,而是交给 NPC 交互流接管;若预览没有显式注入一个可展示的 currentStory,运行面板也可能无法稳定挂载。
本轮继续修复
继续复测时发现,SceneActPreviewRuntime 虽然已经不再调用 handleCharacterSelect(),但仍会调用 handleCustomWorldSelect(profile) 来同步 runtime 静态资料。
这个入口是正式运行态的“选择世界”入口,会排队写入“已选择世界、尚未选角”的中间 GameState。在幕预览本地 setGameState() 写入玩家、场景与故事后,这个中间态仍可能覆盖回来,导致 currentScenePreset 或 playerCharacter 被清掉,预览层重新停在“正在载入这一幕的游戏流程...”。
本轮调整后:
- 幕预览不再调用
handleCustomWorldSelect(profile)。 - 幕预览直接调用
setRuntimeCustomWorldProfile(profile)与setRuntimeCharacterOverrides(buildCustomWorldRuntimeCharacters(profile))同步静态资料层。 isPreviewReady同时校验:currentScene === "Story"runtimeSessionId === "runtime-scene-act-preview"- 当前玩家就是本次预览角色
- 当前场景就是本次预览场景
- 当前 story 已经完成注入
- 这样 preview ready 只依赖本次预览自己的完整启动结果,不再被正式选世界中间态影响。
修复口径
- 幕预览不再调用
handleCharacterSelect()触发后端开局副作用。 - 幕预览不调用正式
handleCustomWorldSelect(profile),而是直接同步 runtime 静态资料层。 - 随后在同一个
setGameState中一次性写入:playerCharacterruntimeMode: "play"runtimePersistenceDisabled: truecurrentScene / currentScenePreset / currentEncounter- 玩家血蓝、技能冷却、装备、统计、进度、队伍、任务等运行态基础字段
- 当前幕
currentSceneActState
- 幕预览使用固定临时
runtimeSessionId: "runtime-scene-act-preview",并通过禁持久化快照保持不写正式存档。 - 启动时同步
hydrateStoryState(),注入当前幕 NPC 的本地开场 story,让RpgRuntimeShell立即满足挂载条件。
约束
- 幕预览继续复用正式
play运行链,不恢复旧preview/test行为分支。 - 幕预览只允许前端做临时运行态装配;正式游戏开局仍由
server-rs裁决。 - 后续如把幕预览也迁到后端 bootstrap,应新增专门的禁持久化 bootstrap 入口,而不是再次依赖
handleCharacterSelect()的异步状态顺序。
验证
新增回归覆盖:
npm test -- --run src/components/CustomWorldEntityEditorModal.test.tsx
断言幕预览打开后:
- 不再显示“正在载入这一幕的游戏流程...”。
RpgRuntimeShell已收到预览玩家角色。- 运行态为
play且禁用持久化。 - 当前 story 已注入为当前幕 NPC 开场内容。