# 游戏启动流程开发手册 > 文件位置:`Docs/Guides/01_BootFlow_Setup_Guide.md` > 版本:1.0 · 适用项目:zeling_v2 --- ## 目录 1. [架构概览](#1-架构概览) 2. [完整启动时序图](#2-完整启动时序图) 3. [关键脚本说明](#3-关键脚本说明) 4. [事件频道速查表](#4-事件频道速查表) 5. [分步配置教程](#5-分步配置教程) - 5.1 [创建事件频道资产(Step 1)](#51-创建事件频道资产step-1) - 5.2 [配置 Persistent 场景(Step 2)](#52-配置-persistent-场景step-2) - 5.3 [配置 Splash 演出(Step 2 续)](#53-配置-splash-演出step-2-续) - 5.4 [配置 Loading 画面(Step 2 续)](#54-配置-loading-画面step-2-续) - 5.5 [创建并配置主菜单场景(Step 3)](#55-创建并配置主菜单场景step-3) - 5.6 [Build Settings 配置(Step 4)](#56-build-settings-配置step-4) 6. [使用编辑器向导工具](#6-使用编辑器向导工具) 7. [FSM 状态转换关系](#7-fsm-状态转换关系) 8. [自定义扩展指南](#8-自定义扩展指南) 9. [常见问题排查](#9-常见问题排查) --- ## 1. 架构概览 启动流程由以下四个程序集中的组件协同完成,通过事件频道(`ScriptableObject` 事件总线)彻底解耦: ``` BaseGames.Core BaseGames.UI ───────────────────────────── ───────────────────────────── GameManager ◄──────────── SplashScreenController └─ BootSequencer ────────────► (via EVT_SplashStartRequest) └─ GameStateMachine MainMenuController LoadingScreenManager BaseGames.Core.Events (共享) ───────────────────────────── VoidEventChannelSO FloatEventChannelSO IntEventChannelSO StringEventChannelSO SceneLoadRequestEventChannelSO ``` **核心设计原则:** - `BaseGames.Core` 不依赖 `BaseGames.UI`,所有跨程序集通信通过 SO 事件频道进行。 - 所有场景加载请求都汇聚到 `EVT_SceneLoadRequest`,GameManager 和 SceneService 共享同一 SO 实例。 - GameStateMachine 是状态权威,所有 UI 面板的显隐跟随 `EVT_GameStateChanged` 事件。 --- ## 2. 完整启动时序图 ``` 应用启动(首场景加载前) │ ├── [RuntimeInitializeOnLoadMethod] GameBootstrap │ └── 以 Additive 模式加载 Scene_Persistent │ ▼ Scene_Persistent 加载完成 │ ├── GameServiceRegistrar.Awake(-2000) │ └── 向 ServiceLocator 注册:SceneService、DeathRespawnService、 │ EventChannelRegistry、GameSaveManager、… │ ├── GameManager.Awake(-1000) │ ├── DontDestroyOnLoad(root) │ ├── FSM.TransitionTo(Initializing) │ └── 注册所有 FSM 状态 │ └── GameManager.Start() ├── Raise(EVT_GameStateChanged, Initializing) └── StartCoroutine(BootCoroutine) │ ├── BootSequencer.RunBootSequenceCoroutine() │ ├── Raise(EVT_SplashStartRequest) ──► SplashScreenController 开始 │ │ 播放演出(工作室 Logo → │ │ 游戏标题)可任意键跳过 │ │ │ ├── [并行] PreloadCoroutine() │ │ └── Addressables.DownloadDependenciesAsync("Preload") │ │ 每帧 Raise(EVT_LoadingProgressUpdated, progress*0.9) │ │ │ └── WaitUntil(SplashDone && PreloadDone) │ SplashScreenController 结束 → Raise(EVT_SplashComplete) │ └── SceneService.LoadMainMenuCoroutine() └── SceneLoader.LoadSceneCoroutine(Scene_MainMenu) └── 加载成功 → Raise(EVT_SceneLoaded, "Scene_MainMenu") └── GameManager.HandleSceneLoaded └── FSM: Initializing → MainMenu Raise(EVT_GameStateChanged, MainMenu) ───────────────────────────────────────────────────────────────────────────── 主菜单激活 │ ├── MainMenuController.OnEnable() │ └── 订阅 EVT_GameStateChanged / EVT_SlotConfirmed │ └── MainMenuController 响应 EVT_GameStateChanged(MainMenu) └── 播放入场动画(菜单面板下滑) └── RefreshContinueButton()(检查存档是否存在) ───────────────────────────────────────────────────────────────────────────── 新游戏 / 继续 │ ├── 玩家点击「新游戏」或「继续」 │ └── 显示 SaveSlotPanel(选择存档槽 0/1/2) │ ├── SaveSlotController → Raise(EVT_SlotConfirmed, slotIndex) │ ├── MainMenuController.HandleSlotConfirmed() │ └── 关闭 SaveSlotPanel │ Raise(EVT_SceneLoadRequest, {firstGameSceneKey, Scene, ShowLoadingScreen=true}) │ ├── GameManager.HandleSceneLoadRequest() │ └── FSM: MainMenu → LoadingScene │ Raise(EVT_GameStateChanged, LoadingScene) │ ├── SceneLoader.LoadSceneCoroutine() │ ├── Raise(EVT_LoadingStarted) → LoadingScreenManager 显示进度画面 │ ├── 每帧 Raise(EVT_LoadingProgressUpdated, p*0.9) │ ├── Raise(EVT_LoadingProgressUpdated, 1.0) │ └── Raise(EVT_LoadingComplete) → LoadingScreenManager 隐藏 │ Raise(EVT_SceneLoaded, gameSceneName) │ └── GameManager.HandleSceneLoaded() └── FSM: LoadingScene → Gameplay Raise(EVT_GameStateChanged, Gameplay) ───────────────────────────────────────────────────────────────────────────── 暂停 → 返回主菜单 │ ├── PauseMenuController.GoToMainMenu() │ └── Raise(EVT_SceneLoadRequest, {Scene_MainMenu, Scene, ShowLoadingScreen=false}) │ ├── GameManager.HandleSceneLoadRequest() │ └── 目标为 Scene_MainMenu → 跳过 LoadingScene 中间状态 │ └── SceneLoader 加载完成 → Raise(EVT_SceneLoaded, "Scene_MainMenu") └── FSM: Paused → MainMenu ``` --- ## 3. 关键脚本说明 ### `GameManager`(Core/GameManager.cs) 全局游戏管理器,持有 FSM 并协调所有顶层服务。 | 新增字段 | 用途 | |---|---| | `_bootSequencer` | 引用 Persistent 场景中的 BootSequencer | | `_onSceneLoadRequest` | 监听场景加载请求(与 SceneService 共享同一 SO)| | `_onSceneLoaded` | 监听 SceneLoader 加载完成(携带场景名)| `Start()` 启动 `BootCoroutine()`,`HandleSceneLoadRequest()` / `HandleSceneLoaded()` 自动驱动 FSM 转换。 --- ### `BootSequencer`(Core/BootSequencer.cs) 挂载在 Persistent 场景,驱动 Splash 演出与 Addressable 预热并行执行。 | Inspector 字段 | 说明 | |---|---| | `_preloadLabel` | Addressable 预热标签(留空则跳过预热)| | `_onSplashStartRequest` | 赋 `EVT_SplashStartRequest`(Raise)| | `_onSplashComplete` | 赋 `EVT_SplashComplete`(Listen)| | `_onPreloadProgress` | 赋 `EVT_LoadingProgressUpdated`(可选,供进度条显示)| **重要:** `_onSplashComplete` 留空时,BootSequencer 不等待 Splash,直接进入主菜单。适合调试阶段快速跳过 Splash。 --- ### `SplashScreenController`(UI/Splash/SplashScreenController.cs) 挂载在 `Canvas_Splash`(排序层 100),播放两段淡入/淡出动画,任意键可跳过。 | Inspector 字段 | 说明 | |---|---| | `_studioLogoGroup` | 工作室 Logo 的 CanvasGroup | | `_gameTitleGroup` | 游戏标题的 CanvasGroup | | `_onSplashStartRequest` | 赋 `EVT_SplashStartRequest`(Listen)| | `_onSplashComplete` | 赋 `EVT_SplashComplete`(Raise)| | `_fadeDuration` | 淡入/淡出时长(秒,默认 1.0)| | `_holdDuration` | 每帧持续时长(秒,默认 1.5)| --- ### `MainMenuController`(UI/MainMenu/MainMenuController.cs) 挂载在主菜单场景的 Canvas 上,管理按钮、子面板、入场动画。 | Inspector 字段 | 说明 | |---|---| | `_onGameStateChanged` | 赋 `EVT_GameStateChanged`(Listen)| | `_onSceneLoadRequest` | 赋 `EVT_SceneLoadRequest`(Raise)| | `_onSlotConfirmed` | 赋 `EVT_SlotConfirmed`(Listen)| | `_firstGameSceneKey` | 第一个游戏场景的 Addressable Key(**必填**)| | `_btnNewGame/Continue/Settings/Credits/Quit` | 各按钮引用 | | `_menuPanel` | 主按钮区 GameObject(用于入场动画)| | `_saveSlotPanel / _settingsPanel / _creditsPanel` | 子面板 | | `_saveSlotController` | SaveSlotController 引用 | --- ### `SceneLoader`(Core/SceneLoader.cs) 加载时序改为逐帧轮询,当 `ShowLoadingScreen = true` 时自动发布进度事件。 | 新增字段 | 用途 | |---|---| | `_onLoadingStarted` | 赋 `EVT_LoadingStarted`(Raise 给 LoadingScreenManager)| | `_onLoadingComplete` | 赋 `EVT_LoadingComplete`(Raise 给 LoadingScreenManager)| | `_onLoadingProgressUpdated` | 赋 `EVT_LoadingProgressUpdated`(Raise 进度值 0~1)| --- ## 4. 事件频道速查表 | SO 资产名 | 类型 | 发布者 | 监听者 | 说明 | |---|---|---|---|---| | `EVT_SplashStartRequest` | VoidEventChannelSO | BootSequencer | SplashScreenController | 触发 Splash 演出开始 | | `EVT_SplashComplete` | VoidEventChannelSO | SplashScreenController | BootSequencer | Splash 结束通知 | | `EVT_LoadingStarted` | VoidEventChannelSO | SceneLoader | LoadingScreenManager | 加载开始,显示进度画面 | | `EVT_LoadingComplete` | VoidEventChannelSO | SceneLoader | LoadingScreenManager | 加载结束,隐藏进度画面 | | `EVT_LoadingProgressUpdated` | FloatEventChannelSO | SceneLoader / BootSequencer | LoadingScreenManager | 进度值 0~1 | | `EVT_SlotConfirmed` | IntEventChannelSO | SaveSlotController | MainMenuController | 存档槽选择完成(携带槽索引)| | `EVT_SceneLoadRequest` | SceneLoadRequestEventChannelSO | MainMenuController / PauseMenuController / … | GameManager + SceneService | 场景加载请求(共享同一 SO)| | `EVT_SceneLoaded` | StringEventChannelSO | SceneLoader | GameManager | 加载完成(携带场景名)| | `EVT_GameStateChanged` | GameStateEventChannelSO | GameManager | MainMenuController / UIManager / … | FSM 状态改变通知 | > **注意:** `EVT_SceneLoadRequest` 和 `EVT_SceneLoaded` 在 Inspector 中必须使用同一个 SO 实例赋给所有相关组件。不同组件使用不同 SO 实例是最常见的配置错误。 --- ## 5. 分步配置教程 ### 5.1 创建事件频道资产(Step 1) **方法 A — 一键创建(推荐):** 1. 打开菜单 **BaseGames → Tools → Boot Flow Wizard**。 2. 在 **Step 1** 区域点击 **「一键创建所有缺失资产」**。 3. 资产将创建在 `Assets/_Game/Data/Events/UI/Splash/`、`UI/Loading/`、`UI/MainMenu/`、`Core/` 目录下。 4. 检查清单全部变绿后进入下一步。 **方法 B — 手动创建:** 在 Project 窗口右键 → Create,按以下清单逐一创建: ``` Assets/_Game/Data/Events/UI/Splash/ EVT_SplashStartRequest.asset (VoidEventChannelSO) EVT_SplashComplete.asset (VoidEventChannelSO) Assets/_Game/Data/Events/UI/Loading/ EVT_LoadingStarted.asset (VoidEventChannelSO) EVT_LoadingComplete.asset (VoidEventChannelSO) EVT_LoadingProgressUpdated.asset (FloatEventChannelSO) Assets/_Game/Data/Events/UI/MainMenu/ EVT_SlotConfirmed.asset (IntEventChannelSO) ``` --- ### 5.2 配置 Persistent 场景(Step 2) **前置条件:** 已完成 Step 1,`Scene_Persistent` 已在 Hierarchy 中打开。 **方法 A — 自动脚手架(推荐):** 1. 在 Hierarchy 中打开 `Scene_Persistent`(双击或在 Build Settings 中打开)。 2. 打开 **BaseGames → Tools → Boot Flow Wizard**,点击 **Step 2** 中的 **「脚手架 Persistent 场景」**。 3. 工具将自动: - 在 `[Services]` 下创建 `BootSequencer` GameObject 并挂载组件。 - 在 `[UI]` 下创建 `Canvas_Splash`(排序层 100)并挂载 `SplashScreenController`。 - 在 `[UI]` 下创建 `Canvas_Loading`(排序层 99)并挂载 `LoadingScreenManager`。 - 自动绑定所有存在的事件频道 SO。 - 为 `GameManager` 绑定 `_bootSequencer`、`_onSceneLoadRequest`、`_onSceneLoaded`。 - 为 `SceneLoader` 绑定三个加载事件频道。 4. 保存场景(Ctrl+S)。 **方法 B — 手动配置(如需精细控制):** 1. 在 `[Services]` 下新建空 GameObject 命名为 `BootSequencer`,挂载 `BootSequencer` 组件。 2. 在 Inspector 中赋值: - `_onSplashStartRequest` → `EVT_SplashStartRequest` - `_onSplashComplete` → `EVT_SplashComplete` - `_onPreloadProgress` → `EVT_LoadingProgressUpdated`(可选) - `_preloadLabel` → Addressable 标签名(如 `"Preload"`,留空则跳过) 3. 在 `GameManager` 组件 Inspector 中赋值: - `_bootSequencer` → 上述 BootSequencer 组件 - `_onSceneLoadRequest` → `EVT_SceneLoadRequest` - `_onSceneLoaded` → `EVT_SceneLoaded` 4. 在 `SceneLoader` 组件 Inspector 中赋值: - `_onLoadingStarted` → `EVT_LoadingStarted` - `_onLoadingComplete` → `EVT_LoadingComplete` - `_onLoadingProgressUpdated` → `EVT_LoadingProgressUpdated` --- ### 5.3 配置 Splash 演出(Step 2 续) 1. 找到 `Canvas_Splash` GameObject。 2. 在子节点中创建两个 Image + CanvasGroup 结构: ``` Canvas_Splash (SplashScreenController) └── StudioLogo <- 工作室 Logo 图片 └── CanvasGroup └── GameTitle <- 游戏标题图片/文字 └── CanvasGroup ``` 3. 将 `StudioLogo` 的 CanvasGroup 赋给 `SplashScreenController._studioLogoGroup`。 4. 将 `GameTitle` 的 CanvasGroup 赋给 `SplashScreenController._gameTitleGroup`。 5. 调整 `_fadeDuration`(淡入/淡出时长)和 `_holdDuration`(停留时长)。 **跳过 Splash(调试模式):** - 将 `BootSequencer._onSplashComplete` 留空,BootSequencer 不会等待 Splash 完成,直接进入主菜单。 --- ### 5.4 配置 Loading 画面(Step 2 续) 1. 找到 `Canvas_Loading` GameObject(默认已由脚手架创建,初始 `SetActive(false)`)。 2. 为 `LoadingScreenManager` 创建所需 UI 子节点: ``` Canvas_Loading (LoadingScreenManager) └── LoadingPanel (背景遮罩 + 内容) └── ProgressBar (Slider) └── TipText (可选:随机提示文字) ``` 3. 在 `LoadingScreenManager` Inspector 中赋值: - `_loadingPanel` → `LoadingPanel` GameObject - `_progressBar` → `ProgressBar` Slider 组件 - `_onLoadingStarted` → `EVT_LoadingStarted`(已由脚手架绑定) - `_onLoadingComplete` → `EVT_LoadingComplete`(已由脚手架绑定) - `_onLoadingProgressUpdated` → `EVT_LoadingProgressUpdated`(已由脚手架绑定) --- ### 5.5 创建并配置主菜单场景(Step 3) **创建场景:** 1. 在 Project 窗口右键 → Create → Scene,命名为 `Scene_MainMenu`。 2. 将其放置在 `Assets/_Game/Scenes/` 目录下。 3. 双击打开场景。 **自动脚手架:** 1. 打开 **Boot Flow Wizard → Step 3**,点击 **「脚手架 MainMenu 场景」**。 2. 工具将在场景中生成: ``` [MainMenu] └── Canvas_MainMenu (Canvas, CanvasScaler, GraphicRaycaster, MainMenuController) └── MenuPanel (VerticalLayoutGroup) ├── Btn_NewGame (Image, Button) ├── Btn_Continue (Image, Button) ├── Btn_Settings (Image, Button) ├── Btn_Credits (Image, Button) └── Btn_Quit (Image, Button) ├── SaveSlotPanel (SetActive=false, SaveSlotController) ├── SettingsPanel (SetActive=false) └── CreditsPanel (SetActive=false) ``` 3. **必须手动填写** `MainMenuController._firstGameSceneKey`: - 在 Inspector 中输入第一个游戏场景的 Addressable Key(字符串)。 - 例如:`"Scene_Prologue"` 或 `"Scene_Town_01"`。 **SaveSlotPanel 配置:** 1. 打开 `SaveSlotPanel`。 2. 为 `SaveSlotController` 补充三个存档槽按钮引用(`_slot0Btn`、`_slot1Btn`、`_slot2Btn`)。 3. 为每个按钮添加适当的 UI 样式(背景图、存档信息文本等)。 **SettingsPanel / CreditsPanel:** 这两个面板为空节点,由各自项目美术/策划填充内容。`MainMenuController` 通过 `_settingsPanel.SetActive(true/false)` 控制其显隐。 --- ### 5.6 Build Settings 配置(Step 4) 1. 打开菜单 **File → Build Settings**(或 Boot Flow Wizard 中点击 **「打开 Build Settings」**)。 2. 将以下场景加入 Scenes in Build(顺序重要): | 索引 | 场景 | 说明 | |---|---|---| | 0 | `Assets/_Game/Scenes/Scene_Boot.unity` | 启动入口(仅包含 `GameBootstrap`)| | — | `Assets/_Game/Scenes/Scene_Persistent.unity` | DontDestroyOnLoad 场景(不需要显式索引)| | — | `Assets/_Game/Scenes/Scene_MainMenu.unity` | 主菜单(通过 Addressables 加载)| 3. 确保 `Scene_Boot` 为索引 0(Player 设置中的第一场景)。 > **注意:** 其他所有游戏场景(关卡等)应通过 Addressables 打包,**不应**加入 Build Settings 的 Scene 列表,以避免包体膨胀。 --- ## 6. 使用编辑器向导工具 ### Boot Flow Wizard 菜单:**BaseGames → Tools → Boot Flow Wizard** 窗口提供四个步骤的实时状态检查: - **✅(绿色)** = 该项已正确配置 - **⬜(灰色)** = 该项尚未完成 | 步骤 | 功能 | |---|---| | Step 1 | 检测所有 8 个启动流程事件频道资产,一键创建缺失项 | | Step 2 | 检测 Persistent 场景中 9 个组件/字段绑定状态,一键脚手架 | | Step 3 | 检测主菜单场景组件状态,一键脚手架,快速打开 Build Settings | | Step 4 | 运行全量验证并在 Console 输出带位置信息的报告 | 底部状态栏实时显示 `通过 N / 总计 M 项检查`。 ### Create Event Channel Assets 菜单:**BaseGames → Tools → Create Event Channel Assets** 在 `Assets/_Game/Data/Events/` 目录下批量创建**所有**系统所需事件频道(含启动流程部分)。已存在资产自动跳过(幂等操作)。 ### Scaffold Persistent Scene / Scaffold Main Menu Scene 菜单:**BaseGames → Tools → Scaffold Persistent Scene** 菜单:**BaseGames → Tools → Scaffold Main Menu Scene**(优先级 202) 独立的脚手架工具,适合不打开向导窗口时直接执行。 --- ## 7. FSM 状态转换关系 启动流程涉及的 GameStateMachine 转换: ``` Initializing │ └──(EVT_SceneLoaded: Scene_MainMenu)──► MainMenu │ ┌─────────────────────┤ │ │ (新游戏/继续) (其他) │ ▼ LoadingScene │ └──(EVT_SceneLoaded: GameScene)──► Gameplay │ ┌──────────┤ │ │ (Pause) (PlayerDied) │ │ Paused Dead │ ┌──────────────┤ │ │ (Resume) (GoToMainMenu) │ │ Gameplay MainMenu GameOver ──(EVT_SceneLoaded: Scene_MainMenu)──► MainMenu ``` **`HandleSceneLoadRequest` 逻辑(GameManager):** - `TransitionType.Scene` + 目标 ≠ `Scene_MainMenu` → FSM 当前在 MainMenu/Gameplay/BossFight → 转换到 `LoadingScene` - `TransitionType.Scene` + 目标 = `Scene_MainMenu` → 不经过 `LoadingScene`,由 `HandleSceneLoaded` 直接转换 - `TransitionType.Room` → 完全忽略,状态保持 Gameplay --- ## 8. 自定义扩展指南 ### 添加第三段 Splash(如游戏内 IP 授权方 Logo) 在 `SplashScreenController` 中 `PlayAsync()` 方法末尾追加: ```csharp // 第三段:IP Logo if (_ipLogoGroup != null) { yield return StartCoroutine(Fade(_ipLogoGroup, 0f, 1f, _fadeDuration)); yield return new WaitForSeconds(_holdDuration); yield return StartCoroutine(Fade(_ipLogoGroup, 1f, 0f, _fadeDuration)); } ``` 同时在 Inspector 中添加 `[SerializeField] private CanvasGroup _ipLogoGroup;` 字段。 --- ### 修改 Loading 画面为视频背景 1. 给 `Canvas_Loading` 添加 `RawImage` 组件用于显示视频。 2. 在 `LoadingScreenManager` 的 `Show()` 中启动 `VideoPlayer.Play()`,`Hide()` 中停止。 3. 使用 `EVT_LoadingStarted` / `EVT_LoadingComplete` 频道触发视频播放/停止。 --- ### 新游戏时跳过存档选择(单存档模式) 在 `MainMenuController` 中,将 `OnNewGameClicked` 修改为直接触发槽 0: ```csharp private void OnNewGameClicked() { // 单存档:直接使用槽 0 HandleSlotConfirmed(0); } ``` --- ### 添加主菜单背景音乐 在 `MainMenuController.Start()` 或响应 `EVT_GameStateChanged(MainMenu)` 时: ```csharp _onBGMRequest?.Raise("BGM_MainMenu"); // 赋值 EVT_BGMRequest 频道 ``` AudioManager 的 `EVT_BGMRequest` 监听器会自动淡入播放。 --- ## 9. 常见问题排查 ### ❌ 游戏启动后停在黑屏,不显示 Splash 也不进入主菜单 **原因:** `GameManager._bootSequencer` 未绑定,BootCoroutine 无法执行。 **解决:** 打开 Boot Flow Wizard → Step 2,检查 `GameManager._bootSequencer 已绑定` 是否为绿色;否则重新执行脚手架。 --- ### ❌ Splash 演出播放完毕后一直黑屏(不进入主菜单) **原因 1:** `GameManager._onSceneLoaded` 未绑定或绑定了错误的 SO 实例,`HandleSceneLoaded` 从未被调用。 **解决:** 确认 `GameManager._onSceneLoaded` 与 `SceneLoader._onSceneLoaded` 绑定同一个 `EVT_SceneLoaded.asset`。 **原因 2:** `ISceneService` 未注册(`GameServiceRegistrar` 未正确引用 `SceneService`)。 **解决:** Console 中搜索 `[GameManager] ISceneService 未注册`,检查 Persistent 场景中的 `GameServiceRegistrar._sceneService` 引用。 --- ### ❌ 点击「新游戏」后进入黑屏,加载进度条不出现 **原因 1:** `LoadingScreenManager._onLoadingStarted` 未绑定。 **原因 2:** `LoadingScreenManager._loadingPanel` 为空(未赋值 UI 根节点)。 **解决:** 检查 Boot Flow Wizard Step 2 中 `SceneLoader._onLoadingStarted 已绑定` 状态。 --- ### ❌ `MainMenuController._firstGameSceneKey` 填写后仍报 Addressable 加载失败 **原因:** Key 填写有误,或对应场景未在 Addressables 中标记。 **解决:** 1. 打开 **Window → Asset Management → Addressables → Groups**。 2. 找到目标场景资产,确认其 Address 与 `_firstGameSceneKey` 完全一致(大小写敏感)。 3. 确保该场景已勾选 Include in Build。 --- ### ❌ 返回主菜单时 FSM 报 Invalid transition 警告 **原因:** 通常是从 `Dead` 或 `GameOver` 状态直接触发 `HandleSceneLoaded(MainMenu)`,而这两个状态的 `ValidNextStates` 不包含 `MainMenu`。 **解决:** 确保 `DeathRespawnService.StartGameOverCoroutine()` 在加载主菜单场景**之前**已调用 `GameManager.RequestTransition(GameOver)`。参见 `BuiltinGameStates.cs` 中 `GameOverState.ValidNextStates`。 --- ### ❌ 编辑器中运行正常,打包后 Splash 不显示 **原因:** `Canvas_Splash` 上的 Image Sprite 未加入 Addressable Build 或 Sprite Atlas。 **解决:** 将 Splash 使用的所有 Texture/Sprite 加入 Addressable Group(标签 `"Preload"` 以便启动时预热),或直接内嵌进默认 Resources。 --- *文档最后更新:2026-05-19*