7.9 KiB
GameManager 收敛式重构说明
状态:已按修订方案实现
更新日期:2026-07-17
1. 最终职责边界
| 组件 | 职责 |
|---|---|
GameManager |
唯一会话命令入口;编排开始、章节跳转、读档、暂停、返回主界面和退出应用 |
GameSession |
GameManager 持有的纯状态对象;保存状态、校验迁移并计算能力 |
TalkSceneGraphIndex |
从首章图派生运行时章节列表和恢复名称索引 |
SceneLoader |
串行执行 Addressables 场景加载、卸载和场景级资源清理 |
SceneReadiness |
在当前加载 Scene 内收集 ISceneDialogueGate 并等待就绪 |
SaveRestoreOrchestrator |
存档预检和 Snapshot 内部恢复阶段 |
ChapterController |
章节解锁数据及章节面板投影 |
ApplicationBootstrapper |
应用设置、SnapshotRegistry、本地化及主界面启动流程 |
DisplaySettingsController |
窗口模式和分辨率应用,并发送显示变化通知 |
没有引入 NarrativeFlowController、人工维护的 TalkSceneCatalog 或独立全局 Session 服务。TalkSceneSO、SceneExit、章节资产和 Chapter Graph Editor 数据结构保持不变。
2. GameSession
GameSessionPhase:
MainMenu
Starting
Transitioning
Restoring
Playing
ReturningToMenu
只读状态:
PhaseCurrentTalkSceneIsPausedIsDialogueActiveIsActiveIsBusyCanPauseCanResumeCanAdvanceDialogue
外部通过 GameManager.Session 读取;GameManager 内部直接使用 _session。所有写入方法均为 internal,GameSession 不执行协程、不操作 UI、不加载场景,也不发送全局事件。
合法迁移:
MainMenu → Starting / Restoring
Playing → Transitioning / Restoring / ReturningToMenu
Starting / Transitioning → Playing / ReturningToMenu
Restoring → Playing / MainMenu / ReturningToMenu
ReturningToMenu → MainMenu
3. 生命周期事件
GameLoopEnum 已删除,统一使用:
public enum GameLifecycleEvent
{
ApplicationReady,
SessionStarted,
SessionReady,
SessionPaused,
SessionResumed,
SessionEnding,
SessionEnded,
SessionFailed
}
显示设置通知使用 SettingChangeEvent.DisplayChanged。
事件顺序固定为:GameManager 执行命令 → GameSession 完成状态迁移 → GameManager 发送生命周期通知。监听方不能反向修改 Session。
DialogController.DialogueActivityChanged(bool) 是 GameManager 更新对话活动状态的直接 C# 事件;原有对话表现事件继续供其他系统使用。
4. GameManager API
public bool TryStartNewGame();
public bool TryStartChapter(TalkSceneSO chapter);
public bool TryRestoreSlot(int slotIndex, Action<RestoreResult> completed = null);
public bool TryPause();
public bool TryResume();
public bool TryReturnToMainMenu();
public void QuitApplication();
public IReadOnlyList<TalkSceneSO> RuntimeChapters { get; }
开始、转场和恢复共用一个活动命令锁,重复请求直接返回 false。返回主界面请求可以在活动命令期间排队;当前不可中断的 Addressables 或 Provider 单步结束后,流程会在阶段边界停止,并进入唯一的返回主界面协程。
Yarn 场景命令通过以下内部可等待入口执行;章节交接是例外,由源 Runner 同步提交后交给 GameManager 独立执行:
internal bool TryAdvanceChapterFromYarn(string exitName);
internal IEnumerator LoadSceneFromYarn(string sceneName);
internal IEnumerator UnloadSceneFromYarn();
NextYarn 是同步交接命令:它只向 GameManager 提交章节跳转并立即返回,不等待转场,也不在自身命令回调中停止 DialogueRunner。NextYarn 必须是当前分支最后一条可执行命令;GameManager 只等待源节点自然结束,超时视为内容错误并终止转场,不兼容后续选项、jump 或其他命令。load_scene / unload_scene 不替换 YarnProject,因此继续由 Yarn 等待完成。
旧 GameManager 状态字段、接口和命令 API 已删除,没有保留兼容转发层。
5. 章节索引
TalkSceneGraphIndex 从 firstTalkSo 开始,按 exits 顺序稳定深度优先遍历,并用 visited 集合处理环:
- 可达节点进入
RuntimeChapters和名称索引。 - 同名不同资产会被标记为歧义,恢复预检失败。
ChapterController直接投影RuntimeChapters。
这份索引完全从现有创作数据派生,不引入第二份章节目录。
6. 场景加载与 Readiness
SceneLoader 使用 AsyncOperationHandle<SceneInstance>,加载和卸载均返回结构化 SceneOperationResult:
- Busy、空 Key、Addressables 失败和无效 SceneInstance 均明确失败。
- 仅成功后提交当前场景名、Scene 和句柄。
- 卸载失败保留当前场景句柄与名称。
- Loading UI 与
IsLoading在finally中复位。 - DOTween、Fix 注册表和
ResourceSystem场景级生命周期统一由 SceneLoader 清理。
SceneReadiness 在刚加载的 Scene 根对象内查找 Gate,使用 unscaled time,默认超时 15 秒。无 Gate 时等待稳定一帧后成功;Gate 销毁、取消或超时返回失败。普通章节加载在 SceneLoader 后等待;读档在 Provider 后、Anchor 前等待。
7. 恢复边界
PrepareRestore
→ 读槽位或文件
→ schema、场景、章节、YarnProject 和可选节点预检
→ 解析目标 TalkSceneSO
→ 不修改运行时
ExecutePreparedRestore
→ Yarn variables
→ Scene
→ Providers
→ SceneReadiness
→ Anchor
→ Postflight
说明:显式 <<save>> 保留当前来源节点,并写入 startDialogueOnRestore=false。这类快照仍预检章节和 YarnProject;来源节点不存在时只告警,恢复时只加载 YarnProject、不启动节点。
GameManager 管理命令锁、Session Phase、遮罩、旧会话表现清理、成功提交和失败决策。Orchestrator 不再查找或设置 GameManager 的当前章节。
恢复成功后,GameManager 提交 RestoreResult.RestoredTalkScene、解锁章节、进入 Playing 并发送 SessionReady。预检失败恢复发起前的稳定 Phase;执行失败停止后续 Anchor 并排队安全返回主界面。已接受请求的回调只调用一次。
存档文件统一经过当前快照反序列化和 schema 预检,不保留旧 StorageSystem 格式的专用识别、恢复或转写分支。
8. 核心流程
新游戏 / 选章:
校验章节 → 获取命令锁 → Starting → SessionStarted → 遮黑
→ SceneLoader → SceneReadiness → 提交并解锁章节 → 启动 Yarn
→ Playing → SessionReady → 揭示画面
章节跳转:
解析出口 → Transitioning → 遮黑 → SceneLoader → SceneReadiness
→ 提交并解锁章节 → 启动 Yarn → Playing → SessionReady → 揭示画面
读档:
获取命令锁 → Restoring → PrepareRestore → SessionStarted → 遮黑
→ 清理旧会话表现 → ExecutePreparedRestore → 提交目标章节
→ Playing → SessionReady → 揭示画面
返回主界面:
ReturningToMenu → SessionEnding → 恢复 timeScale / 清除暂停 → 遮黑
→ 等待对话停止 → 卸载场景 → 清理表现和相机
→ 清除章节与对话状态 → MainMenu → SessionEnded → 揭示主界面
9. 验证基线
静态验收应确认:
- 不存在
GameLoopEnum、GameManager.state、旧 GameManager API 或CleanSessionFirst。 - 输入只读取
CanAdvanceDialogue;暂停入口只读取CanPause。 - 开始、转场、恢复和 Yarn 场景命令都经过 GameManager 互斥。
- Snapshot 恢复不再通过 GameManager 查找或设置章节。
- 章节面板和存档恢复都只使用从首章可达的正式章节。
运行时冒烟仍需覆盖新游戏、选章、每类新格式存档、Gate 成功/超时、活动命令期间返回主界面,以及退出后场景、对话、暂停、音频、相机和表现 UI 清理。