docs: GameManager大重构计划,需要重新审视
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
# GameManager 重构计划(待重新评审)
|
||||
|
||||
> 状态:已暂存方案,尚未获得最终认可,后续继续讨论前不要据此扩大实现。
|
||||
>
|
||||
> 记录日期:2026-07-16
|
||||
|
||||
## 当前结论
|
||||
|
||||
目标是把 `GameManager` 收敛为游戏会话协调器,但本轮方案和实现涉及范围明显大于最初预期。目前不应把现有实现视为最终架构,应先逐项重新评审,再决定保留、简化或撤回哪些改动。
|
||||
|
||||
已明确需要重新讨论的问题:
|
||||
|
||||
1. `NarrativeFlowController`、`TalkSceneCatalog`、`ChapterController` 的职责是否拆分过度。
|
||||
2. `NarrativeFlowController` 与 `ChapterController` 当前存在双向依赖,边界没有真正理顺。
|
||||
3. `TalkSceneCatalog` 与现有 Chapter Graph Editor 只做到数据层兼容,没有做到创作工作流兼容。
|
||||
4. Chapter Graph Editor 新建、删除章节时不会同步 Catalog;Catalog 的首章、角色、显示状态和顺序还需要单独维护。
|
||||
5. 在决定保留 Catalog 前,需要先确认章节编辑器是否应继续作为唯一内容创作入口。
|
||||
|
||||
## 原始目标
|
||||
|
||||
将 `GameManager` 收敛为唯一的游戏会话协调器:
|
||||
|
||||
- 只管理会话状态、命令互斥、开始、读档、转场、暂停和退出。
|
||||
- 不再持有章节目录、显示设置、应用启动和 Fix 内部清理职责。
|
||||
- 一次性迁移旧调用方,不长期保留旧 API 或兼容转发层。
|
||||
- 保留 `GameStart` 的早期语义,增加 `SessionReady` 表示场景和交互真正就绪。
|
||||
- 新格式存档结构及 `sceneSoName` 身份保持不变。
|
||||
- 旧格式存档停止运行时恢复。
|
||||
|
||||
## 候选组件边界
|
||||
|
||||
| 组件 | 候选职责 | 重新评审重点 |
|
||||
|---|---|---|
|
||||
| `AppBootstrapper` | 设置、SnapshotRegistry、本地化、`AppStart` 和启动遮罩 | 是否值得从 GameManager 单独拆出 |
|
||||
| `GameManager` | 会话状态、互斥、开始、读档、暂停、返回主界面 | 保留为核心目标 |
|
||||
| `NarrativeFlowController` | 当前章节、进章和出口跳转 | 是否可以进一步简化,是否与 ChapterController 重叠 |
|
||||
| `TalkSceneCatalog` | 正式章节、恢复兼容章节和显示顺序目录 | 是否保留;若保留,必须与章节编辑器整合 |
|
||||
| `SceneLoader` | 串行、可验证的 Addressables 场景加载与卸载 | 可以独立保留 |
|
||||
| `SceneReadinessService` | 收集当前场景 Gate 并等待业务就绪 | 是否需要独立类型或可并入场景流程 |
|
||||
| `SaveRestoreOrchestrator` | 快照预检、Provider Barrier、Anchor 恢复和结构化结果 | 与 GameManager 的编排边界需复核 |
|
||||
| `ChapterController` | 解锁数据和章节面板数据 | 应重命名、收紧职责或合并 |
|
||||
| `DisplaySettingsController` | 窗口模式和分辨率应用 | 是否值得独立拆出 |
|
||||
|
||||
## 候选会话状态
|
||||
|
||||
```csharp
|
||||
public enum GameSessionPhase
|
||||
{
|
||||
MainMenu,
|
||||
StartingSession,
|
||||
Transitioning,
|
||||
Restoring,
|
||||
Playing,
|
||||
ReturningToMenu
|
||||
}
|
||||
```
|
||||
|
||||
会话只读状态候选字段:
|
||||
|
||||
- `Phase`
|
||||
- `IsPaused`
|
||||
- `IsDialogueActive`
|
||||
- `IsInGame`
|
||||
- `IsTransitioning`
|
||||
- `CanPause`
|
||||
- `IsInputAvailable`
|
||||
|
||||
需要重新确认:这些派生状态是否与现有输入、UI 和存档判断相比确实降低复杂度,而不是单纯扩大迁移范围。
|
||||
|
||||
## GameManager 候选公共 API
|
||||
|
||||
```csharp
|
||||
public GameSessionState State { get; }
|
||||
|
||||
public bool TryStartNewGame();
|
||||
public bool TryStartChapter(TalkSceneSO chapter);
|
||||
public bool TryRestoreSlot(int slotIndex, Action<RestoreResult> completed = null);
|
||||
public bool TryRestoreFile(
|
||||
string path,
|
||||
RestoreOptions options,
|
||||
Action<RestoreResult> completed = null);
|
||||
|
||||
public void PauseSession();
|
||||
public void ResumeSession();
|
||||
public void ReturnToMainMenu();
|
||||
public void QuitApp();
|
||||
```
|
||||
|
||||
Yarn 内部候选入口:
|
||||
|
||||
```csharp
|
||||
internal IEnumerator AdvanceChapterRoutine(string exitName);
|
||||
internal IEnumerator LoadNarrativeSceneRoutine(string sceneName);
|
||||
internal IEnumerator UnloadNarrativeSceneRoutine();
|
||||
```
|
||||
|
||||
旧 API 的删除范围需要在重新评审后逐项确定,避免为了接口整洁产生不必要的大规模迁移。
|
||||
|
||||
## 事件语义候选
|
||||
|
||||
保留:
|
||||
|
||||
- `AppStart`
|
||||
- `GameStart`
|
||||
- `GameQuit`
|
||||
- `PauseGame`
|
||||
- `UnPauseGame`
|
||||
|
||||
候选新增:
|
||||
|
||||
- `SessionReady`
|
||||
- `SessionTeardownStarted`
|
||||
- `SessionFailed`
|
||||
|
||||
核心规则:
|
||||
|
||||
1. `GameStart` 表示开始进入正式游戏会话。
|
||||
2. 切章和 Yarn 内部换场景不重复发送 `GameStart`。
|
||||
3. 场景、Gate、对话或 Anchor 全部成功后发送 `SessionReady`。
|
||||
4. GameManager 先更新自身状态,再广播事件。
|
||||
5. `DialogStart/DialogEnd` 只更新对话活动状态。
|
||||
6. 新会话、失败和退出时原子清理暂停及对话状态。
|
||||
|
||||
## 转场互斥和取消候选规则
|
||||
|
||||
- GameManager 只允许一个活动命令。
|
||||
- 开始、切章、读档进行中,普通请求直接拒绝,不排队。
|
||||
- 返回主界面允许请求取消当前流程。
|
||||
- 已经开始的 Addressables 操作不强行中断;底层操作完成后停止后续 Gate、对话或 Anchor。
|
||||
- Provider 和 SceneLoader 的不可中断单步允许完成,但阶段之间检查取消。
|
||||
- 所有活动命令必须通过 `try/finally` 释放。
|
||||
|
||||
## 章节系统待决方案
|
||||
|
||||
### 原 Catalog 设想
|
||||
|
||||
Catalog 显式登记:
|
||||
|
||||
- `FirstScene`
|
||||
- Runtime 章节
|
||||
- RestoreOnly 章节
|
||||
- 章节面板显示状态
|
||||
- 章节面板顺序
|
||||
- 以 `TalkSceneSO.name` 为键的恢复查询
|
||||
|
||||
原校验规则包括:
|
||||
|
||||
- 引用和资产名唯一。
|
||||
- FirstScene 必须属于 Runtime。
|
||||
- Runtime 出口目标必须在 Runtime Catalog。
|
||||
- RestoreOnly 不能显示在章节面板。
|
||||
- Runtime 必须具有 YarnProject 和场景 Key。
|
||||
|
||||
### 当前暴露的问题
|
||||
|
||||
现有 Chapter Graph Editor 以指定文件夹中的 `TalkSceneSO` 为数据源,并直接编辑 `exits`。Catalog 是另一份显式目录,因此形成两个需要同步的创作入口。
|
||||
|
||||
如果继续保留 Catalog,至少需要做到:
|
||||
|
||||
- Chapter Graph Editor 新建章节时同步新增 Runtime 条目。
|
||||
- 删除章节时同步移除 Catalog 引用,并处理其他节点出口。
|
||||
- 在章节编辑器内设置 FirstScene。
|
||||
- 在章节编辑器内维护 Runtime/RestoreOnly、是否显示和章节顺序。
|
||||
- RestoreOnly 使用独立区域,不混入正式剧情图。
|
||||
- 加载和校验时区分“当前文件夹过滤”与“全局 Catalog 缺失”,避免误报。
|
||||
- 所有 Catalog 修改支持 Undo。
|
||||
|
||||
重新评审时需要在以下方向中选择:
|
||||
|
||||
1. 保留 Catalog,并让 Chapter Graph Editor 成为唯一维护入口。
|
||||
2. 取消显式 Catalog,继续由首章链和少量恢复补充目录组成运行时数据。
|
||||
3. 由章节编辑器生成只供运行时使用的 Catalog,不要求人工维护 Catalog Inspector。
|
||||
|
||||
在该选择确定之前,不继续扩大章节系统改动。
|
||||
|
||||
## 场景加载与 Gate 候选规则
|
||||
|
||||
SceneLoader 候选约束:
|
||||
|
||||
- 真正等待正在执行的场景操作完成。
|
||||
- 使用强类型 `AsyncOperationHandle<SceneInstance>`。
|
||||
- 校验空 Key、Addressables 状态和 SceneInstance。
|
||||
- 只有加载成功后更新当前场景名。
|
||||
- `try/finally` 复位状态和 Loading UI。
|
||||
- 加载失败回滚 ResourceSystem 场景生命周期。
|
||||
- 卸载失败保留真实句柄和场景名。
|
||||
- 移除 fire-and-forget 场景入口。
|
||||
|
||||
Gate 候选规则:
|
||||
|
||||
- 场景加载后从对应 Scene 根对象收集 `ISceneDialogueGate`。
|
||||
- 不维护跨场景静态注册表。
|
||||
- 使用 unscaled time,超时 15 秒。
|
||||
- 无 Gate 时等待稳定一帧后成功。
|
||||
- Gate 销毁、取消或超时都返回结构化失败。
|
||||
- 普通进章在加载后等待。
|
||||
- 读档在 Provider Barrier 后、Anchor 前等待。
|
||||
- 超时不得强行启动 Yarn。
|
||||
|
||||
## 存档恢复候选流程
|
||||
|
||||
恢复拆成:
|
||||
|
||||
```text
|
||||
PrepareRestore:读盘、格式识别、章节/Yarn/节点预检,不修改运行时
|
||||
ExecuteRestore:变量、场景、章节、Provider、Readiness、Anchor
|
||||
```
|
||||
|
||||
候选恢复顺序:
|
||||
|
||||
```text
|
||||
获取恢复锁
|
||||
→ PrepareRestore
|
||||
→ GameStart(从主界面恢复时)
|
||||
→ Phase=Restoring
|
||||
→ 遮黑
|
||||
→ 清理现有会话(如有)
|
||||
→ Yarn variables
|
||||
→ Scene
|
||||
→ 设置当前章节
|
||||
→ Providers
|
||||
→ SceneReadiness
|
||||
→ RestoreAnchor
|
||||
→ Postflight
|
||||
→ Phase=Playing
|
||||
→ SessionReady
|
||||
→ 揭示画面
|
||||
```
|
||||
|
||||
恢复失败时不得继续 Anchor,应安全清理并回到主界面。旧格式运行时恢复应明确返回不支持,不修改 Yarn 变量或自动档。
|
||||
|
||||
需要重新评审:恢复编排究竟应由 GameManager 掌握到什么粒度,以及哪些失败清理可以继续留在 Orchestrator 内部。
|
||||
|
||||
## 暂停和返回主界面候选规则
|
||||
|
||||
暂停:
|
||||
|
||||
```text
|
||||
更新暂停状态
|
||||
→ Time.timeScale = 0
|
||||
→ PauseGame
|
||||
```
|
||||
|
||||
恢复:
|
||||
|
||||
```text
|
||||
Time.timeScale = 1
|
||||
→ 更新暂停状态
|
||||
→ UnPauseGame
|
||||
```
|
||||
|
||||
返回主界面:
|
||||
|
||||
```text
|
||||
取消当前转场
|
||||
→ 遮黑
|
||||
→ SessionTeardownStarted
|
||||
→ 等待对话停止
|
||||
→ 卸载场景
|
||||
→ State=MainMenu
|
||||
→ GameQuit
|
||||
→ 错误提示(如有)
|
||||
→ 揭示主界面
|
||||
```
|
||||
|
||||
暂停和清理副作用是否全部下沉到 Audio、Dialog UI、Screen Effect、Camera 和 UIManager,需要逐个确认现有监听器是否因此变得更复杂。
|
||||
|
||||
## 应用启动与显示设置候选
|
||||
|
||||
候选 `AppBootstrapper` 职责:
|
||||
|
||||
- `SettingLoader.Init`
|
||||
- `SnapshotRegistry.EnsureInitialized`
|
||||
- 启动遮罩
|
||||
- 等待本地化
|
||||
- 发送 `AppStart`
|
||||
- UI 本地化刷新
|
||||
- 显示主界面
|
||||
|
||||
候选 `DisplaySettingsController` 职责:
|
||||
|
||||
- 应用窗口模式、分辨率和全屏状态。
|
||||
- 等待实际分辨率变化。
|
||||
- 发送独立显示事件。
|
||||
- CameraAspectAdapter 改监听显示事件。
|
||||
|
||||
这两项都需要按“是否确实降低 GameManager 复杂度”重新判断,不以拆分类数量为目标。
|
||||
|
||||
## 调用方迁移候选范围
|
||||
|
||||
- Yarn `next`、`load_scene`、`unload_scene`。
|
||||
- `LineAdvanceInput`。
|
||||
- `UIManager`、`CursorManager` 和游戏内 Terminal。
|
||||
- `SavePointEvaluator`。
|
||||
- Snapshot 捕获和恢复。
|
||||
- `ChapterController` 和章节选择 UI。
|
||||
- 所有退出主界面入口。
|
||||
- 开发存档跳转工具。
|
||||
|
||||
重新实施时应分批迁移并保持每批可编译,不再默认一次性扩大所有调用方改动。
|
||||
|
||||
## 候选验收矩阵
|
||||
|
||||
1. Unity batchmode 编译无错误。
|
||||
2. 章节目录和 Chapter Graph 校验无错误。
|
||||
3. 新格式开发存档逐档冒烟成功。
|
||||
4. 连续点击两次新游戏只接受一次。
|
||||
5. 场景加载中返回主界面不会启动对话,并最终安全卸载。
|
||||
6. 暂停后返回主界面不会残留 `timeScale`、暂停或对话状态。
|
||||
7. 默认出口和命名出口进入正确章节。
|
||||
8. 非法出口、非法章节和场景加载失败安全回主界面。
|
||||
9. Fix、Subway、Outside 在 Gate Ready 后启动对话。
|
||||
10. Gate 永久不 Ready 时超时失败,不启动 Yarn。
|
||||
11. 读档 Provider 完成后经过 Gate,再恢复 Anchor。
|
||||
12. 读档预检失败不加载目标场景。
|
||||
13. 旧格式存档提示不兼容且不修改运行时。
|
||||
14. Yarn 场景命令不绕过互斥。
|
||||
15. 返回主界面和切场景后没有资源、对话、相机和表现 UI 残留。
|
||||
|
||||
## 下次继续时的建议顺序
|
||||
|
||||
1. 先决定是否保留 `TalkSceneCatalog`,以及章节编辑器谁是唯一数据维护入口。
|
||||
2. 再确定 `NarrativeFlowController` 与 `ChapterController` 是保留、重命名还是合并。
|
||||
3. 画出最小调用链,限定 GameManager 必须负责和明确不负责的部分。
|
||||
4. 对现有未提交实现逐文件分类:保留、简化、撤回。
|
||||
5. 先处理章节与恢复主链,再考虑 AppBootstrapper、DisplaySettings 和错误 UI 等外围拆分。
|
||||
6. 每批修改后单独编译和冒烟,不再把整份方案视作必须一次完成的整体。
|
||||
|
||||
## 当前工作区说明
|
||||
|
||||
当前工作区已经存在一版按原方案实施的未提交改动。该实现通过了当时的 Unity 编译及现有测试,但这不代表架构已获认可。下次继续时应先检查当前 diff,并以本文件记录的待决问题为准,避免直接在现有实现上继续叠加。
|
||||
Reference in New Issue
Block a user