新增独立的场景加载功能: 全局脚本和"加载场景"

This commit is contained in:
jkboy 2025-05-20 15:32:47 +08:00
parent 5f61a2fb9c
commit dbf6357bf1
8 changed files with 421 additions and 138 deletions

View File

@ -0,0 +1,26 @@
# LoadingContext.gd
# Autoload Singleton
# 负责在 start 场景和 LoadingScene 之间传递加载指令和必要的存档数据。
extends Node
# 加载操作的类型
# 可能的值: "new_game", "load_game", 或空字符串 (表示无操作或已处理)
var action_to_perform: String = ""
# 当 action_to_perform 为 "load_game" 时,这里会存储从 SaveLoadManager 加载到的存档数据字典。
# 其他情况下应为空字典。
var save_data_for_loading: Dictionary = {}
# 清理上下文,在 LoadingScene 读取完数据后调用,避免旧数据影响下一次加载。
func clear_context() -> void:
action_to_perform = ""
save_data_for_loading = {}
print("LoadingContext: Context cleared.")
# (可选) 一个辅助函数,用于在设置上下文时打印信息,方便调试
func set_context(action: String, data: Dictionary = {}) -> void:
action_to_perform = action
save_data_for_loading = data.duplicate(true) # 使用深拷贝以防外部修改
print("LoadingContext: Context set - Action: ", action_to_perform, ", Data keys: ", save_data_for_loading.keys())

View File

@ -0,0 +1 @@
uid://cmx5osjpib4n5

View File

@ -1,6 +1,6 @@
# 核心 Autoload 管理器说明文档 (v1.4 - 基于代码审查)
# 核心 Autoload 管理器说明文档 (v1.5 - 基于代码审查与新增 LoadingContext)
**文档目的:** 本文档旨在详细说明游戏核心的个 Autoload 管理器 (`InitializationManager`, `InitialDataManager`, `GameState`, `SaveLoadManager`) 的设计、职责、交互方式以及对外提供的接口API。**特别注意:本文档描述的是基于“数据枢纽”模型的架构,并已根据最新代码实现进行更新。** 它是后续开发其他游戏玩法模块如员工管理、项目管理、UI 等)时的**主要参考依据**,确保各模块能正确、高效地与核心系统协作。
**文档目的:** 本文档旨在详细说明游戏核心的个 Autoload 管理器 (`InitializationManager`, `InitialDataManager`, `GameState`, `SaveLoadManager`, `LoadingContext`) 的设计、职责、交互方式以及对外提供的接口API。**特别注意:本文档描述的是基于“数据枢纽”模型的架构,并已根据最新代码实现进行更新。** 它是后续开发其他游戏玩法模块如员工管理、项目管理、UI 等)时的**主要参考依据**,确保各模块能正确、高效地与核心系统协作。
---
@ -11,8 +11,8 @@
1. **Key 管理:** 定义并维护模块自身所需状态数据在 `GameState` 中的唯一 `key`。建议采用命名空间或前缀避免冲突 (e.g., `"employee.list"`, `"project.active_ids"`)。
2. **数据结构定义:** 定义与模块相关的 `key` 所对应的 `value` 的具体数据结构(字典、数组、基础类型等)。
3. **初始化/状态恢复:**
* **必须**监听 `GameState``state_restored` 信号。
* 在信号的回调函数中,调用 `GameState.get_value(key)` 来获取本模块所需的所有数据。
* **必须**监听 `GameState``state_restored` 信号 (用于读档后恢复) 以及 `state_initialized` 信号 (用于新游戏初始化后,如果模块需要在此时进行特定设置)
* 在这些信号的回调函数中,调用 `GameState.get_value(key)` 来获取本模块所需的所有数据。
* 使用获取到的数据完成模块自身的初始化或状态恢复(更新内部变量、刷新 UI 等)。
4. **数据读取与更新:** 通过 `GameState.get_value(key)` 读取状态,通过 `GameState.set_value(key, new_value)` 更新状态。
5. **业务逻辑验证:** **所有**业务逻辑相关的验证(如花钱前检查余额、操作是否允许等)**必须**在调用 `GameState.set_value` **之前**由模块自身完成。`GameState` 不进行任何业务逻辑校验。
@ -25,40 +25,40 @@
### 目标 (Purpose/Goal)
* 作为游戏启动流程的总控制器,统一协调新游戏开始或存档加载的准备工作。
* 确保在通知模块进行状态恢复前,`GameState` 已被正确的数据填充。
* 提供一个清晰的入口点来响应用户的启动选择(新游戏/加载)。
* 提供一个清晰的入口点来响应用户的启动选择(新游戏/加载)通常由加载场景Loading Scene在获取必要数据后调用
### 核心职责 (Responsibilities)
* **流程决策:** 根据用户输入(来自主菜单 UI决定启动“新游戏”流程还是“载入存档”流程。
* **流程决策:** 根据传递的指令(通常来自加载场景,该场景会根据 `LoadingContext` 的信息决定)启动“新游戏”流程还是“载入存档”流程。
* **数据准备:**
* **新游戏:** 确保 `InitialDataManager` 数据加载完成 -> 调用 `InitialDataManager` 获取初始数据 (`settings_data`, `task_dev_data`, `npc_initial_data`) -> **调用 `_build_initial_state` 方法,根据获取的数据构建注入 `GameState` 的初始状态字典。此方法会:**
* **合并基础设置 (`Init_Base.json`) 到状态字典的顶层。**
* **处理任务开发相关数据 (`task_development.json`),提取其中需要动态追踪的状态(如平台的 `enabled`, `Market_share`;玩法/主题的 `enabled`, `Experience`;策略的 `enabled`;产品侧重点的完整结构),并将这些动态状态组织在一个名为 `"task_development"` 的子字典内,再将该子字典存入主状态字典。**
* **处理 NPC 数据 (`npc.json`),将从 `npc_initial_data` 中获取的 NPC 定义(通常是 `npc_defs["npc"]` 部分,即 `npc.json` 文件中 "npc" 键对应的值)存入主状态字典的 `"npcs"` 键下。**
* **载入存档:** **接收**由外部(例如存档选择 UI它会调用 `SaveLoadManager.load_game`)加载并传入的存档数据字典
* **合并基础设置 (`Init_Base.json` 的内容,通过 `settings_data` 传入) 到状态字典的顶层 (进行深拷贝)。**
* **处理任务开发相关数据 (`task_development.json` 的内容,通过 `task_dev_data` 传入),将其内部定义的 "platforms", "gameplays", "themes", "strategies", "product_focus_points" 等数据(经过深拷贝)组织在一个名为 `"task_development"` 的子字典内,再将该子字典存入主状态字典。**
* **处理 NPC 数据 (`npc.json` 的内容,通过 `npc_initial_data` 传入),将从 `npc_initial_data` 中获取的 NPC 定义 (通常是 `npc_initial_data["npc"]` 部分,即 `npc.json` 文件中 "npc" 键对应的值,进行深拷贝) 存入主状态字典的 `"npcs"` 键下。**
* **载入存档:** **接收**由外部(例如加载场景,它会从 `LoadingContext` 获取由 `SaveLoadManager.load_game` 加载的存档数据)传入的存档数据字典 (`save_data`)
* **数据注入:**
* **新游戏:** 将构建好的初始状态字典传递给 `GameState.initialize_for_new_game()`
* **载入存档:** 将接收到的存档数据字典传递给 `GameState.restore_state()`
* **流程控制:** 管理初始化过程中的用户反馈(如显示/隐藏加载界面),并在 `GameState` 准备就绪后(通过信号通知),触发到主游戏场景的切换(此部分逻辑通常在此管理器或调用它的 UI 中实现)。
* **错误处理:** 捕获并处理在初始化过程中(如 `InitialDataManager` 加载失败、构建初始状态失败)发生的错误,向用户提供反馈并阻止进入游戏。
* **流程控制:** 管理初始化过程中的用户反馈(如显示/隐藏加载界面,通常由调用此管理器的加载场景处理),并在 `GameState` 准备就绪后(通过信号通知),触发到主游戏场景的切换(此部分逻辑通常在此管理器触发信号后由加载场景或主UI控制器实现)。
* **错误处理:** 捕获并处理在初始化过程中(如 `InitialDataManager` 加载失败、构建初始状态失败、获取的初始数据为空)发生的错误,向用户提供反馈并阻止进入游戏。
### **对外接口 (Public API)**
* `start_new_game_process() -> bool`: 由主菜单调用,启动新游戏流程。成功返回 `true`,失败返回 `false`
* `load_game_process(save_data: Dictionary) -> bool`: **接收**已加载的存档数据字典 `save_data`,启动加载流程。成功返回 `true`,失败返回 `false`。(**注意:** 此函数**不负责**从文件加载数据,它期望 `save_data` 已经被加载好并传入)。
* **注意:** 其他游戏玩法模块通常**不需要**在游戏运行过程中直接调用此管理器。其主要交互对象是启动阶段的 UI
* `start_new_game_process() -> bool`: 由加载场景或等效的流程控制器调用,启动新游戏流程。成功返回 `true`,失败返回 `false`
* `load_game_process(save_data: Dictionary) -> bool`: 由加载场景或等效的流程控制器调用,**接收**已加载的存档数据字典 `save_data`,启动加载流程。成功返回 `true`,失败返回 `false`。(**注意:** 此函数**不负责**从文件加载数据,它期望 `save_data` 已经被加载好并传入)。
* **注意:** 其他游戏玩法模块通常**不需要**在游戏运行过程中直接调用此管理器。其主要交互对象是启动阶段的加载场景或主菜单后的流程控制逻辑
### **对外信号 (Public Signals)**
* `new_game_initialized`: 在 `start_new_game_process` 成功执行,`GameState` 被初始化后发出。
* `game_loaded`: 在 `load_game_process` 成功执行,`GameState` 被恢复后发出。
### 交互关键点 (Key Interactions)
* **接收:** 来自 UI 的调用 (`start_new_game_process`, `load_game_process`)。
* **接收:** 来自加载场景 (Loading Scene) 或类似流程控制器的调用 (`start_new_game_process`, `load_game_process(save_data)`). `save_data` 通常通过 `LoadingContext` 传递给加载场景
* **调用:** `InitialDataManager` (在新游戏流程中调用 `ensure_data_loaded`, `get_starting_settings`, `get_task_development_data`, `get_npc_initial_data`), `GameState` (调用 `initialize_for_new_game``restore_state`)。
* **发出:** `new_game_initialized`, `game_loaded` 信号。
* **(可能)** `SceneTree` (用于切换场景,虽然未在提供的代码片段中直接显示,但通常是其职责一部分)。
* **(可能)** 间接影响 `SceneTree` (通过信号触发场景切换,由其他脚本执行)。
### 核心逻辑流程 (Core Logic/Flow)
* **新游戏:** (UI 调用 `start_new_game_process`) -> 显示加载 -> 确保 `InitialDataManager` 就绪 -> 获取初始数据 (`settings`, `task_dev`, `npc_initial_data`) -> **调用 `_build_initial_state` 构建包含 `"task_development"` 和 `"npcs"` 结构的初始状态字典** -> 调用 `GameState.initialize_for_new_game()` -> 发出 `new_game_initialized` 信号 -> (后续逻辑) 切换场景 -> 隐藏加载。
* **载入存档:** (UI 调用 `SaveLoadManager.load_game(slot_id)` 得到 `save_data` 字典) -> (UI 调用 `load_game_process(save_data)`) -> 显示加载 -> (可选:确保 `InitialDataManager` 就绪) -> 调用 `GameState.restore_state(save_data)` -> 发出 `game_loaded` 信号 -> (后续逻辑) 切换场景 -> 隐藏加载。
* **新游戏:** (UI 设置 `LoadingContext.action_to_perform = "new_game"`) -> (切换到加载场景) -> (加载场景调用 `start_new_game_process`) -> 显示加载 -> 确保 `InitialDataManager` 就绪 -> 获取初始数据 (`settings`, `task_dev`, `npc_initial_data`) -> **调用 `_build_initial_state` 构建包含 `"task_development"` 和 `"npcs"` 结构的初始状态字典** -> 调用 `GameState.initialize_for_new_game()` -> 发出 `new_game_initialized` 信号 -> (后续逻辑,如加载场景处理) 切换到主游戏场景 -> 隐藏加载。
* **载入存档:** (UI 调用 `SaveLoadManager.load_game(slot_id)` 得到 `save_data` 字典) -> (UI 设置 `LoadingContext.set_context("load_game", save_data)`) -> (切换到加载场景) -> (加载场景读取 `LoadingContext`调用 `InitializationManager.load_game_process(LoadingContext.save_data_for_loading)`) -> 显示加载 -> (可选:确保 `InitialDataManager` 就绪,以防需要参考静态数据) -> 调用 `GameState.restore_state(save_data)` -> 发出 `game_loaded` 信号 -> (后续逻辑,如加载场景处理) 切换到主游戏场景 -> 隐藏加载。
* 在每个关键步骤后检查成功/失败状态,失败则中止流程并反馈用户。
### 配置 (Configuration)
@ -80,11 +80,11 @@
* **数据提供:** 通过函数接口提供数据的**深拷贝**副本。
### **对外接口 (Public API / Data Access Functions)**
* `ensure_data_loaded() -> bool`: 确保所有必需的初始数据 (`Init_Base.json`, `task_development.json`, `npc.json`) 已加载。通常由 `InitializationManager` 在新游戏流程开始时调用。返回加载是否成功。
* `ensure_data_loaded() -> bool`: 确保所有必需的初始数据 (`Init_Base.json`, `task_development.json`, `npc.json`) 已加载。通常由 `InitializationManager` 在新游戏流程开始时或加载游戏流程中调用。返回加载是否成功。
* `get_starting_settings() -> Dictionary`: 获取 `Init_Base.json` 中的初始游戏设定。返回数据的深拷贝。
* `get_task_development_data() -> Dictionary`: 获取 `task_development.json` 中的所有数据。返回数据的深拷贝。
* `get_npc_initial_data() -> Dictionary`: 获取 `npc.json` 中的所有数据。返回数据的深拷贝。
* **数据规范:** 返回的数据结构遵循 JSON 文件结构。所有通过此管理器获取的数据都应视为**只读**。
* **数据规范:** 返回的数据结构遵循 JSON 文件结构。所有通过此管理器获取的数据都应视为**只读**。`npc.json` 文件应为字典结构,`task_development.json``Init_Base.json` 也应为字典结构。
### **对外信号 (Public Signals)**
* 通常不发出信号,因为它主要提供静态数据查询。
@ -113,21 +113,21 @@
### 核心职责 (Responsibilities)
* **状态存储:** 内部维护一个核心字典 (`_state_data`),用于存储所有动态游戏状态的 Key-Value 对。
* **通用状态访问:** 提供通用的 `set_value`, `get_value` 接口。
* **状态初始化:** 提供 `initialize_for_new_game` 方法,使用初始设置数据填充状态字典,**并在此过程中调用内部方法(如 `add_init_data`)根据已填充的 `_state_data` 的一部分(例如 `"task_development"`)来进一步初始化其他状态数据(例如 `"task_info"`** 完成后发出 `state_initialized` 信号。
* **状态恢复与通知:** 提供 `restore_state` 方法用于接收并覆盖整个状态字典,完成后发出 `state_restored` 信号。
* **状态初始化:** 提供 `initialize_for_new_game` 方法,使用初始设置数据填充状态字典,**并在此过程中调用内部方法 (`add_init_data`),该方法会读取 `_state_data["task_development"]` 中的数据(如平台、玩法、主题、策略的启用状态和产品侧重点配置),用于构建并填充 `_state_data["task_development_info"]` 字典,该字典包含了当前选定的开发相关初始信息。** 完成后发出 `state_initialized` 信号。
* **状态恢复与通知:** 提供 `restore_state` 方法用于接收并覆盖整个状态字典(进行深拷贝),完成后发出 `state_restored` 信号。
* **存档数据提供:** 提供 `get_savable_state` 方法,返回内部状态字典的**深拷贝**副本,供 `SaveLoadManager` 使用。
* **系统状态管理:** 通过内部的 `"system_status"` 字典(存储在 `_state_data["system_status"]`)管理如游戏暂停 (`game_pause`)、是否在任务中 (`is_on_task`) 等状态。提供方法来修改和查询这些特定状态。
### **对外接口 (Public API / Generic State Access)**
* `initialize_for_new_game(initial_settings: Dictionary) -> void`: **(供 InitializationManager 调用)** 使用 `initial_settings` 字典(进行深拷贝)来初始化内部的 `_state_data`。**随后,会调用一个内部函数 (`add_init_data`),该函数读取 `_state_data` 中已存在的 `"task_development"` 数据,并用其来填充 `_state_data` 中的 `"task_info"` 键。** 完成后发出 `state_initialized` 信号。
* `restore_state(loaded_data: Dictionary) -> void`: **(供 InitializationManager 调用)** 用 `loaded_data` 字典(进行深拷贝)**完全替换**内部的 `_state_data` 字典,然后发出 `state_restored` 信号。
* `initialize_for_new_game(initial_settings: Dictionary) -> void`: **(供 InitializationManager 调用)** 使用 `initial_settings` 字典(进行深拷贝)来初始化内部的 `_state_data`。**随后,会调用一个内部函数 (`add_init_data`),该函数基于 `_state_data["task_development"]` 的内容(例如,查找各分类如平台、玩法、主题、开发策略中首个标记为 `enabled:true` 的条目,并获取产品侧重点数据)来构建和填充 `_state_data` 中的 `"task_development_info"` 字典(其包括:"平台"、"玩法"、"题材"、"开发策略"、"预算"、"产品侧重点"等)。** 完成后发出 `state_initialized` 信号。如果 `_state_data` 中存在 `"time"` 键且其值为字典,还会发出 `date_changed` 兼容性信号。
* `restore_state(loaded_data: Dictionary) -> void`: **(供 InitializationManager 调用)** 用 `loaded_data` 字典(进行深拷贝)**完全替换**内部的 `_state_data` 字典,然后发出 `state_restored` 信号。如果 `_state_data` 中存在 `"time"` 键且其值为字典,还会发出 `date_changed` 兼容性信号。
* `get_savable_state() -> Dictionary`: **(供 SaveLoadManager 调用)** 返回内部 `_state_data` 字典的**深拷贝** (`duplicate(true)`),用于存档,确保返回的是当前状态的一个独立快照。
* `get_value(key: String, default = null) -> Variant`: 根据 `key` 从内部字典检索 `value`。如果 `key` 不存在,返回 `default` 值。
* `set_value(key: String, value: Variant) -> void`: 向内部字典设置或更新一个 Key-Value 对。**注意:调用者负责确保 `value` 是可序列化的,并进行必要的业务逻辑验证。** 只有当新值与旧值**通过 `!=` (浅比较) 判断为不同**时,才会实际更新并发出 `state_value_changed` 信号。如果 `key``"time"``value` 是字典,还会额外发出 `date_changed` 兼容性信号。
* `pause_game() -> void`: **通过 `set_system_status_value("game_pause", true)` 修改状态,将 `"system_status"` 中的 `game_pause` 设置为 `true`。**
* `resume_game() -> void`: **通过 `set_system_status_value("game_pause", false)` 修改状态,将 `"system_status"` 中的 `game_pause` 设置为 `false`。**
* `set_system_status_value(key: String, value: bool) -> void`: 设置 `"system_status"` 字典中指定 `key` (`"game_pause"``"is_on_task"`) 的布尔值。会触发 `state_value_changed` 信号(针对 `"system_status"` 键,传递更新后的整个 `"system_status"` 字典)。
* `set_system_status_value(key: String, value: bool) -> void`: 设置 `"system_status"` 字典中指定 `key` (`"game_pause"``"is_on_task"`) 的布尔值。会触发 `state_value_changed` 信号(针对 `"system_status"` 键,传递更新后的整个 `"system_status"` 字典的副本)。
* `is_game_paused() -> bool`: 返回游戏是否暂停 (从 `"system_status"` 读取 `game_pause`,若 `"system_status"` 或键不存在则返回 `false`)。
* `is_on_task_status() -> bool`: 返回是否处于任务中状态 (从 `"system_status"` 读取 `is_on_task`,若 `"system_status"` 或键不存在则返回 `false`)。
@ -135,10 +135,10 @@
* `state_initialized(initial_state: Dictionary)`: 在 `initialize_for_new_game` 方法成功执行,内部数据被初始化后发出。传递初始化后的状态字典的副本。
* `state_restored(restored_state: Dictionary)`: 在 `restore_state` 方法成功执行,内部数据被完全替换后发出。**所有需要状态的功能模块都应监听此信号**,以触发它们的初始化/状态恢复逻辑。传递恢复后的状态字典的副本。
* `state_value_changed(key: String, new_value: Variant)`: 在 `set_value` 被调用且值发生改变时发出。可用于需要对特定状态变化做出实时反应的模块。
* `date_changed(new_date: Dictionary)`: **[兼容性信号]** 当 `set_value` 更新了键为 `"time"` 的状态并且其新值为字典类型时发出。主要用于保持与旧代码或特定UI如日期显示的兼容性。传递新日期字典的副本。
* `date_changed(new_date: Dictionary)`: **[兼容性信号]** 当 `set_value` 更新了键为 `"time"` 的状态,并且其新值为字典类型时发出,或者在 `initialize_for_new_game` / `restore_state` 后如果存在 `"time"` 键时发出。主要用于保持与旧代码或特定UI如日期显示的兼容性。传递新日期字典的副本。
### 数据处理 (Data Handled)
* **核心:** 管理一个包含所有动态游戏状态的 Key-Value 字典 (`_state_data`)。**系统级状态如暂停 (`game_pause`) 和是否在任务中 (`is_on_task`) 被存储在 `_state_data` 下的 `"system_status"` 子字典中。**
* **核心:** 管理一个包含所有动态游戏状态的 Key-Value 字典 (`_state_data`)。**系统级状态如暂停 (`game_pause`) 和是否在任务中 (`is_on_task`) 被存储在 `_state_data` 下的 `"system_status"` 子字典中 (如果该子字典不存在,访问时会提供默认值)。** `_state_data` 中的 `"task_development_info"` 键则存储了当前选定的开发相关初始信息,由 `add_init_data` 方法在初始化时基于 `_state_data["task_development"]` 构建。
* **数据规范:** 不强制规定具体 Key 或 Value 的结构,由各个功能模块自行定义和管理。仅要求 Value 是可序列化的 `Variant`
### 配置 (Configuration)
@ -153,14 +153,14 @@
* 封装文件操作和 JSON 序列化/反序列化。
### 核心职责 (Responsibilities)
* **存档 (Save):** 调用 `GameState.get_savable_state()` 获取状态字典 -> 添加元数据 (存档时间, 游戏版本) -> 序列化为 JSON -> 写入 `user://` 目录下的存档文件。
* **读档 (Load):**`user://` 目录读取存档文件 -> 解析 JSON 为字典 -> 验证基本结构 -> **返回**提取出的 `game_state` 部分字典给调用者 (例如 `InitializationManager` 或调用它的 UI)
* **文件管理:** 管理 `user://` 目录下的存档文件命名和存在性检查。
* **错误处理:** 处理文件读写和 JSON 解析过程中的错误。
* **存档 (Save):** 调用 `GameState.get_savable_state()` 获取状态字典 -> 添加元数据 (存档时间 ISO 8601 格式, 项目版本号) -> 序列化为 JSON (使用制表符缩进以提高可读性) -> 写入 `user://` 目录下的存档文件 (文件名如 `save_1.json`)
* **读档 (Load):**`user://` 目录读取存档文件 -> 解析 JSON 为字典 -> 验证基本结构 (如存在 `game_state` 键且其值为字典) -> **返回**提取出的 `game_state` 部分字典给调用者 (例如主菜单UI它随后会将此数据通过 `LoadingContext` 传递给加载流程)。如果失败(文件不存在、无法解析、格式错误等),则返回 `null`
* **文件管理:** 管理 `user://` 目录下的存档文件命名 (基于槽位 ID`save_1.json`) 和存在性检查。
* **错误处理:** 处理文件读写和 JSON 解析过程中的错误,并通过返回值或打印错误信息 (`printerr`) 进行反馈
### **对外接口 (Public API)**
* `save_game(slot_id: int) -> bool`: 执行存档操作。调用 `GameState.get_savable_state()` 获取数据,然后写入到指定槽位的文件。返回是否成功。 (通常由存档 UI 调用)
* `load_game(slot_id: int) -> Variant`: 执行读档操作。读取指定槽位的文件,解析 JSON。**成功时返回包含 `game_state` 数据的字典 (Dictionary),失败时返回 `null`。** (通常由需要加载存档的 UI 或流程控制器调用,其结果传递给 `InitializationManager.load_game_process`)
* `load_game(slot_id: int) -> Variant`: 执行读档操作。读取指定槽位的文件,解析 JSON。**成功时返回包含 `game_state` 数据的字典 (Dictionary),失败时返回 `null`。** (通常由需要加载存档的 UI 或流程控制器调用,其结果随后通过 `LoadingContext` 传递给 `InitializationManager.load_game_process`)
* `does_save_exist(slot_id: int) -> bool`: 检查指定槽位是否存在存档文件。
### **对外信号 (Public Signals)**
@ -168,13 +168,51 @@
### 交互关键点 (Key Interactions)
* **被调用:** UI (存档菜单调用 `save_game`, `does_save_exist`; 读档菜单调用 `load_game`)。
* **调用:** `GameState` (仅在 `save_game` 时调用 `get_savable_state()`), `FileAccess` API, `JSON` API, `Time` API, `ProjectSettings` API, `OS` API。
* **调用:** `GameState` (仅在 `save_game` 时调用 `get_savable_state()`), `FileAccess` API, `JSON` API, `Time` API, `ProjectSettings` API, `OS` API (用于 `is_debug_build` 检查)
* **重要:** 开发者在编写其他模块时,**无需直接与 `SaveLoadManager` 交互**。模块的状态通过 `GameState` 间接被保存和加载。
### 数据处理 (Data Handled)
* **输入 (Save):**`GameState` 获取的状态字典。
* **输出 (Save):** `user://` 目录下的 JSON 文件 (包含 metadata 和 game_state)。
* **输出 (Save):** `user://` 目录下的 JSON 文件 (包含 `metadata``game_state`)。
* **输入 (Load):** `user://` 目录下的 JSON 文件。
* **输出 (Load):** 解析后的 `game_state` 字典 (返回给调用者) 或 `null`
---
## 5. 加载上下文管理器 (LoadingContext)
### 目标 (Purpose/Goal)
* 作为在不同场景之间(尤其是从启动界面/主菜单到加载场景)传递加载指令和必要数据的临时容器。
* 确保加载场景能够获取执行新游戏或加载存档所需的信息。
### 核心职责 (Responsibilities)
* **数据传递:** 临时存储加载操作的类型 (`"new_game"``"load_game"`)。
* **存档数据暂存:** 当操作类型为 `"load_game"` 时,暂存从 `SaveLoadManager` 加载到的存档数据字典。
* **上下文清理:** 提供方法以在数据使用完毕后(通常在加载场景完成其任务后)清除上下文,避免旧数据影响后续操作。
### **对外属性 (Public Properties)**
* `action_to_perform: String`: 存储当前的加载操作指令。
* 可能的值: `"new_game"`, `"load_game"`, 或空字符串 (表示无操作或已处理)。
* `save_data_for_loading: Dictionary`: 当 `action_to_perform``"load_game"` 时,这里存储从 `SaveLoadManager.load_game()` 获取到的存档数据字典。其他情况下为空字典。
### **对外接口 (Public API)**
* `clear_context() -> void`: 清理 `action_to_perform``save_data_for_loading`,将它们重置为空字符串和空字典。通常在加载场景使用完上下文信息后调用。
* `set_context(action: String, data: Dictionary = {}) -> void`: 设置加载操作类型和可选的存档数据。传入的 `data` 会被深拷贝 (`duplicate(true)`) 以防止外部修改影响存储的上下文。
### **对外信号 (Public Signals)**
* *此管理器不发出信号。*
### 交互关键点 (Key Interactions)
* **被设置:** 通常由主菜单或启动场景中的 UI 逻辑设置。
* 例如,点击“新游戏”按钮时,调用 `set_context("new_game")`
* 例如,选择存档并成功调用 `SaveLoadManager.load_game(slot_id)` 后,调用 `set_context("load_game", loaded_save_data)`
* **被读取:** 主要由加载场景 (Loading Scene) 读取。加载场景根据 `action_to_perform` 的值来决定是调用 `InitializationManager.start_new_game_process()` 还是 `InitializationManager.load_game_process(save_data_for_loading)`
* **调用:** 无直接调用其他核心管理器,它是一个被动的数据容器。
* **清理:** 加载场景在完成对 `InitializationManager` 的调用并准备切换到主游戏场景之前,应调用 `clear_context()`
### 数据处理 (Data Handled)
* **输入:** 加载操作类型 (字符串) 和可选的存档数据 (字典)。
* **存储:** 内部临时存储这些输入。
* **输出:** 加载场景通过直接访问其公共属性来读取这些数据。
---

View File

@ -0,0 +1,189 @@
# LoadingScene.gd
# 负责驱动加载流程更新UI并在完成后切换到游戏主场景。
extends Control
# --- 节点引用 ---
@onready var progress_bar: ProgressBar = $MainContainer/LoadingProgressBar
@onready var loading_label: Label = $MainContainer/LoadingLabel
# --- 常量 ---
const GAME_SCENE_PATH = "res://Entity/Level/GameScene.tscn" # 请确保这个路径是正确的!
# --- 内部状态变量 ---
var _current_action: String = ""
var _save_data_to_process: Dictionary = {}
var _target_animation_progress: float = 0.0 # 进度条动画的目标值 (用于20%-80%的平滑过渡)
var _current_animation_progress: float = 0.0 # 进度条动画的当前值
var _animation_speed: float = 30.0 # 进度条动画速度 (百分比/秒),可以根据需要调整
var _initialization_started: bool = false # 标记核心初始化流程是否已开始
var _initialization_complete: bool = false # 标记核心初始化流程是否已完成
func _ready():
print("LoadingScene: Scene Ready.")
# 1. 初始化UI
_update_progress(0.0, "准备加载...")
# 2. 从 LoadingContext 获取参数
if not ProjectSettings.has_setting("autoload/LoadingContext"):
_handle_critical_error("LoadingContext Autoload 未配置!")
return
_current_action = LoadingContext.action_to_perform
# 对存档数据进行深拷贝以防万一LoadingContext中的原始数据被意外修改
_save_data_to_process = LoadingContext.save_data_for_loading.duplicate(true)
# 3. 清空 LoadingContext (这是一个好习惯)
LoadingContext.clear_context()
# 验证获取到的action是否有效
if _current_action != "new_game" and _current_action != "load_game":
_handle_critical_error("无效的加载指令: " + _current_action)
return
print("LoadingScene: Action to perform - ", _current_action)
if _current_action == "load_game":
print("LoadingScene: Save data keys to process - ", _save_data_to_process.keys())
# 4. 更新进度到阶段1 (10%)
_update_progress(10.0, "加载参数读取完毕...")
# 5. 连接 InitializationManager 的信号
# 使用 CONNECT_ONE_SHOT 确保信号只触发一次,处理后自动断开,避免重复处理或内存泄漏
if not ProjectSettings.has_setting("autoload/InitializationManager"):
_handle_critical_error("InitializationManager Autoload 未配置!")
return
if _current_action == "new_game":
if InitializationManager.is_connected("new_game_initialized", Callable(self, "_on_initialization_manager_finished")):
printerr("LoadingScene: Warning - new_game_initialized signal already connected by this instance. This might indicate a logic error if _ready is called multiple times unexpectedly.")
else:
InitializationManager.new_game_initialized.connect(self._on_initialization_manager_finished, CONNECT_ONE_SHOT)
print("LoadingScene: Connected to InitializationManager.new_game_initialized.")
elif _current_action == "load_game":
if InitializationManager.is_connected("game_loaded", Callable(self, "_on_initialization_manager_finished")):
printerr("LoadingScene: Warning - game_loaded signal already connected by this instance.")
else:
InitializationManager.game_loaded.connect(self._on_initialization_manager_finished, CONNECT_ONE_SHOT)
print("LoadingScene: Connected to InitializationManager.game_loaded.")
# 6. 使用 call_deferred 延迟调用实际的初始化启动函数
# 这可以确保当前 _ready() 函数中的UI更新等操作完成后再开始耗时操作。
call_deferred("_start_actual_initialization")
func _start_actual_initialization() -> void:
print("LoadingScene: Starting actual initialization...")
# 更新进度到阶段2 (20%)
_update_progress(20.0, "开始初始化核心数据...")
_initialization_started = true
_current_animation_progress = 20.0 # 动画从20%开始
_target_animation_progress = 80.0 # 动画目标到80%
var success: bool = false
if _current_action == "new_game":
success = InitializationManager.start_new_game_process()
if not success:
_handle_initialization_failure("新游戏流程启动失败 (InitializationManager.start_new_game_process 返回 false)。")
elif _current_action == "load_game":
if _save_data_to_process.is_empty() and _current_action == "load_game":
# 这是一个潜在问题,如果我们要加载游戏,但没有存档数据
_handle_initialization_failure("加载游戏失败:没有提供有效的存档数据。")
return
success = InitializationManager.load_game_process(_save_data_to_process)
if not success:
_handle_initialization_failure("加载游戏流程启动失败 (InitializationManager.load_game_process 返回 false)。")
if success:
print("LoadingScene: Initialization process started via InitializationManager.")
# 如果 success 为 false_handle_initialization_failure 已经被调用
func _process(delta: float) -> void:
# 如果核心初始化已开始且未完成,则驱动进度条动画
if _initialization_started and not _initialization_complete:
if _current_animation_progress < _target_animation_progress:
_current_animation_progress = move_toward(_current_animation_progress, _target_animation_progress, _animation_speed * delta)
# 只更新进度条的值,不频繁更新文本,避免性能开销和视觉跳动
progress_bar.value = _current_animation_progress
# (可选) 如果动画已达到80%但仍在等待信号,可以添加一个小的等待动画或文本提示
# else:
# loading_label.text = "正在等待核心数据处理完成..." (确保只设置一次)
# 当 InitializationManager 完成新游戏或加载游戏流程后,此函数被调用
func _on_initialization_manager_finished() -> void: # success 参数已移除,因为信号发出即代表成功
print("LoadingScene: Received signal from InitializationManager (initialization finished).")
_initialization_complete = true
# 确保动画进度至少达到目标或者直接跳到90%
_current_animation_progress = max(_current_animation_progress, _target_animation_progress)
# 更新进度到阶段3 (90%)
_update_progress(90.0, "核心数据准备完毕!")
# 【增加延时】在切换到游戏场景之前
#print("LoadingScene: Adding a DEBUG delay before transitioning to GameScene...")
#await get_tree().create_timer(3.0).timeout # <--- 增加3秒延时 (用于调试)
#print("LoadingScene: DEBUG delay finished.")
# 延迟切换到游戏场景
call_deferred("_transition_to_game_scene")
func _transition_to_game_scene() -> void:
print("LoadingScene: Preparing to transition to GameScene...")
# 更新进度到阶段4 (100%)
_update_progress(100.0, "正在进入游戏...")
# (可选) 给用户一个非常短暂的时间看到100%
# await get_tree().create_timer(0.1).timeout
# 注意: 上面的 await 会使函数变成异步,如果后续有更多同步代码,需要注意。
# 对于简单的场景切换,直接调用通常没问题。
var err = get_tree().change_scene_to_file(GAME_SCENE_PATH)
if err != OK:
_handle_critical_error("切换到 GameScene 失败! 错误码: " + str(err) + "路径: " + GAME_SCENE_PATH)
# 此时游戏可能处于一个无法恢复的状态,可以考虑显示一个永久的错误信息
# --- 辅助函数 ---
func _update_progress(value: float, message: String = "") -> void:
progress_bar.value = value
if not message.is_empty():
# 为了避免过于频繁地更新Label内容导致潜在的性能问题或闪烁
# 我们可以只在关键阶段更新文本,或者确保文本内容确实改变了再更新。
# 这里我们假设在关键阶段更新文本是可以接受的。
loading_label.text = message # +" " + str(int(value)) + "%"
# ProgressBar的show_percentage=true会自动显示百分比
print("LoadingScene: Progress updated - ", int(value), "% - ", message)
func _handle_initialization_failure(error_message: String) -> void:
printerr("LoadingScene: Initialization Failed - ", error_message)
_initialization_started = false # 停止任何进行中的动画逻辑
_initialization_complete = true # 标记流程结束(虽然是失败的结束)
# 更新UI以反映错误状态
progress_bar.value = progress_bar.min_value # 或者一个特定的错误值/样式
# 可以考虑给ProgressBar添加一个自定义主题当发生错误时改变其颜色
# progress_bar.add_theme_stylebox_override("fill", preload("res://error_progress_bar_fill_style.tres"))
loading_label.text = "错误: " + error_message + "\n请尝试返回主菜单或重启游戏。"
# 在这里,可以考虑启用一个“返回主菜单”的按钮,或者其他用户可进行的操作。
# 例如: get_node("Path/To/RetryButton").disabled = false
func _handle_critical_error(error_message: String) -> void:
printerr("LoadingScene: CRITICAL ERROR - ", error_message)
_initialization_started = false
_initialization_complete = true # 阻止进一步操作
if progress_bar: progress_bar.value = 0
if loading_label: loading_label.text = "严重错误: " + error_message + "\n游戏可能无法继续。"
# 对于严重错误,可能除了显示信息外做不了太多
# 可以禁用所有交互,或者尝试安全退出游戏
# get_tree().quit() # 如果错误非常严重

View File

@ -0,0 +1 @@
uid://dlvo4fwjsy7r5

View File

@ -0,0 +1,47 @@
[gd_scene load_steps=3 format=3 uid="uid://4wbnffj2cbmc"]
[ext_resource type="FontFile" uid="uid://egugs822n8gr" path="res://UI/font/AlimamaFangYuanTiVF-Thin.ttf" id="1_7week"]
[ext_resource type="Script" uid="uid://dlvo4fwjsy7r5" path="res://Entity/Level/LoadingScene.gd" id="1_sd15f"]
[node name="LoadingScene" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_sd15f")
[node name="BackgroundColor" type="ColorRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 1)
[node name="MainContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -150.0
offset_top = -23.5
offset_right = 150.0
offset_bottom = 23.5
grow_horizontal = 2
grow_vertical = 2
[node name="LoadingLabel" type="Label" parent="MainContainer"]
layout_mode = 2
size_flags_horizontal = 4
theme_override_fonts/font = ExtResource("1_7week")
text = "加载中..."
horizontal_alignment = 1
[node name="LoadingProgressBar" type="ProgressBar" parent="MainContainer"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2

View File

@ -3,13 +3,13 @@
extends Node2D
# --- 节点引用 ---
# !! 重要:请根据你的实际场景结构调整以下节点路径 !!
@onready var new_game_button = $UI/Select_List/New
@onready var load_game_button = $UI/Select_List/Load
@onready var new_game_button: Button = $UI/Select_List/New
@onready var load_game_button: Button = $UI/Select_List/Load
# @onready var error_message_label = $UI/ErrorMessageLabel # 如果有错误提示标签
# --- 场景路径 ---
var GameScene_path = "res://Entity/Level/GameScene.tscn"
# const GameScene_path = "res://Entity/Level/GameScene.tscn" # 不再由此脚本直接切换
const LOADING_SCENE_PATH = "res://Entity/Level/LoadingScene.tscn" # <--- 新增LoadingScene的路径请确保正确
# --- Godot 生命周期方法 ---
@ -17,13 +17,15 @@ func _ready():
print("Start Scene: Initializing...")
# --- 连接 UI 信号 ---
if new_game_button:
new_game_button.pressed.connect(_on_new_game_pressed)
if not new_game_button.is_connected("pressed", Callable(self, "_on_new_game_pressed")):
new_game_button.pressed.connect(self._on_new_game_pressed)
print("Start Scene: Connected 'pressed' signal for New Game button.")
else:
printerr("Start Scene Error: New Game button node not found at path specified in @onready var.")
if load_game_button:
load_game_button.pressed.connect(_on_load_game_pressed)
if not load_game_button.is_connected("pressed", Callable(self, "_on_load_game_pressed")):
load_game_button.pressed.connect(self._on_load_game_pressed)
print("Start Scene: Connected 'pressed' signal for Load Game button.")
# 示例:根据存档是否存在禁用加载按钮 (需要 SaveLoadManager Autoload)
# if ProjectSettings.has_setting("autoload/SaveLoadManager"):
@ -33,146 +35,124 @@ func _ready():
else:
printerr("Start Scene Error: Load Game button node not found at path specified in @onready var.")
# --- 连接 InitializationManager 信号 ---
# 确保 InitializationManager 是 Autoload 并且已经注册
if ProjectSettings.has_setting("autoload/InitializationManager"):
# 连接新游戏初始化完成信号
if not InitializationManager.is_connected("new_game_initialized", Callable(self, "_on_initialization_complete")):
InitializationManager.new_game_initialized.connect(_on_initialization_complete)
print("Start Scene: Connected to InitializationManager.new_game_initialized signal.")
# --- 移除 InitializationManager 信号连接 ---
# 不再需要在此处连接 InitializationManager 的信号,这些将由 LoadingScene 处理。
# 例如,以下代码需要被移除或注释掉:
# if ProjectSettings.has_setting("autoload/InitializationManager"):
# if not InitializationManager.is_connected("new_game_initialized", Callable(self, "_on_initialization_complete")):
# InitializationManager.new_game_initialized.connect(self._on_initialization_complete)
# if not InitializationManager.is_connected("game_loaded", Callable(self, "_on_initialization_complete")):
# InitializationManager.game_loaded.connect(self._on_initialization_complete)
# else:
# printerr("Start Scene Error: InitializationManager Autoload is not configured...")
# 连接加载游戏完成信号
if not InitializationManager.is_connected("game_loaded", Callable(self, "_on_initialization_complete")):
InitializationManager.game_loaded.connect(_on_initialization_complete)
print("Start Scene: Connected to InitializationManager.game_loaded signal.")
else:
# 如果没有配置 Autoload这是一个严重错误
printerr("Start Scene Error: InitializationManager Autoload is not configured in Project Settings!")
# 在这种情况下,游戏可能无法正常启动新游戏或加载
if new_game_button: new_game_button.disabled = true
if load_game_button: load_game_button.disabled = true
# 当此节点从场景树中移除时调用
func _exit_tree():
# 断开与 Autoload 信号的连接,这是一个良好的实践
if ProjectSettings.has_setting("autoload/InitializationManager"):
# 断开新游戏信号
if InitializationManager.is_connected("new_game_initialized", Callable(self, "_on_initialization_complete")):
InitializationManager.new_game_initialized.disconnect(Callable(self, "_on_initialization_complete"))
print("Start Scene: Disconnected from InitializationManager.new_game_initialized signal.")
# 断开加载游戏信号
if InitializationManager.is_connected("game_loaded", Callable(self, "_on_initialization_complete")):
InitializationManager.game_loaded.disconnect(Callable(self, "_on_initialization_complete"))
print("Start Scene: Disconnected from InitializationManager.game_loaded signal.")
# --- 移除 InitializationManager 信号断开 ---
# 同样,不再需要在此处断开 InitializationManager 的信号。
# if ProjectSettings.has_setting("autoload/InitializationManager"):
# if InitializationManager.is_connected("new_game_initialized", Callable(self, "_on_initialization_complete")):
# InitializationManager.new_game_initialized.disconnect(Callable(self, "_on_initialization_complete"))
# if InitializationManager.is_connected("game_loaded", Callable(self, "_on_initialization_complete")):
# InitializationManager.game_loaded.disconnect(Callable(self, "_on_initialization_complete"))
pass # 如果没有其他需要在退出时清理的,可以留空或移除此函数
# --- UI 信号回调函数 ---
# 当“新游戏”按钮被按下时调用
func _on_new_game_pressed():
print("Start Scene: 'New Game' button pressed. Requesting new game initialization...")
if new_game_button: new_game_button.disabled = true
print("Start Scene: 'New Game' button pressed. Transitioning to Loading Scene...")
if new_game_button: new_game_button.disabled = true # 禁用按钮防止重复点击
if load_game_button: load_game_button.disabled = true
if ProjectSettings.has_setting("autoload/InitializationManager"):
# 调用 InitializationManager 启动流程
# start_new_game_process 返回 true/false 表示调用是否成功启动(例如数据加载失败)
if not InitializationManager.start_new_game_process():
# 如果启动流程本身就失败了(例如 InitialDataManager 无法加载数据)
printerr("Start Scene: InitializationManager.start_new_game_process() failed immediately.")
# 显示错误信息给用户
# if error_message_label: error_message_label.text = "无法启动新游戏,请检查配置。"
# 重新启用按钮
# 检查 LoadingContext Autoload 是否存在
if not ProjectSettings.has_setting("autoload/LoadingContext"):
printerr("Start Scene CRITICAL Error: LoadingContext Autoload not configured!")
# 重新启用按钮,让用户可以重试或意识到问题
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
# else: # 如果启动成功,则等待 _on_initialization_complete 信号
# print("Start Scene: New game process started, waiting for completion signal...")
else:
printerr("Start Scene Error: Cannot start new game - InitializationManager Autoload not found!")
# (可选) 显示错误信息给用户
# if error_message_label: error_message_label.text = "错误:无法准备加载流程!"
return
# 设置 LoadingContext
LoadingContext.action_to_perform = "new_game"
LoadingContext.save_data_for_loading = {} # 确保对于新游戏,存档数据为空
# 切换到 LoadingScene
var err = get_tree().change_scene_to_file(LOADING_SCENE_PATH)
if err != OK:
printerr("Start Scene CRITICAL ERROR: Failed to switch to Loading Scene! Error code: " + str(err))
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
# (可选) 显示错误信息给用户
# if error_message_label: error_message_label.text = "错误:无法打开加载界面!"
# 当“加载游戏”按钮被按下时调用
func _on_load_game_pressed():
var slot_to_load = 1 # <-- 硬编码加载的存档槽位 ID
var slot_to_load = 1 # <-- 您可以根据需要修改或添加选择存档槽的逻辑
print("Start Scene: 'Load Game' button pressed. Requesting load for slot " + str(slot_to_load) + "...")
if new_game_button: new_game_button.disabled = true
if load_game_button: load_game_button.disabled = true
# --- 需要 SaveLoadManager 来获取存档数据 ---
# 检查必要的 Autoloads
if not ProjectSettings.has_setting("autoload/SaveLoadManager"):
printerr("Start Scene Error: Cannot load game - SaveLoadManager Autoload not found!")
printerr("Start Scene CRITICAL Error: SaveLoadManager Autoload not configured!")
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
return # 无法继续
return
if not ProjectSettings.has_setting("autoload/LoadingContext"):
printerr("Start Scene CRITICAL Error: LoadingContext Autoload not configured!")
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
return
# 1. 尝试从 SaveLoadManager 加载数据 (现在期望返回字典或 null)
# 1. 尝试从 SaveLoadManager 加载数据
var loaded_data: Variant = SaveLoadManager.load_game(slot_to_load)
# 2. 检查加载是否成功 (返回 null 表示失败)
# 2. 检查加载是否成功
if loaded_data == null:
printerr("Start Scene: Failed to load save data from slot " + str(slot_to_load) + " (SaveLoadManager returned null).")
# 显示错误信息给用户
# if error_message_label: error_message_label.text = "加载存档失败!"
# 重新启用按钮
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
return # 加载失败,中止
# (可选) 显示错误信息给用户
# if error_message_label: error_message_label.text = "加载存档失败!"
return
# 2.1 (可选但推荐) 再次确认加载的数据是字典类型
if not loaded_data is Dictionary:
printerr("Start Scene Error: Loaded data from SaveLoadManager is not a Dictionary. Type:", typeof(loaded_data))
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
return # 数据类型错误,中止
return
# 3. 如果存档数据加载成功 (是字典),再调用 InitializationManager 处理
if ProjectSettings.has_setting("autoload/InitializationManager"):
# 将加载到的字典传递给 load_game_process
if not InitializationManager.load_game_process(loaded_data): # <--- 传递字典
printerr("Start Scene: InitializationManager.load_game_process() failed.")
# 显示错误信息给用户
# if error_message_label: error_message_label.text = "应用存档状态时出错。"
# 重新启用按钮
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
# else: # 如果启动成功,则等待 _on_initialization_complete 信号
# print("Start Scene: Load game process started, waiting for completion signal...")
else:
printerr("Start Scene Error: Cannot process loaded game - InitializationManager Autoload not found!")
# 3. 设置 LoadingContext
LoadingContext.action_to_perform = "load_game"
LoadingContext.save_data_for_loading = loaded_data # 将加载到的字典传递过去
# 4. 切换到 LoadingScene
var err = get_tree().change_scene_to_file(LOADING_SCENE_PATH)
if err != OK:
printerr("Start Scene CRITICAL ERROR: Failed to switch to Loading Scene! Error code: " + str(err))
if new_game_button: new_game_button.disabled = false
if load_game_button: load_game_button.disabled = false
# --- InitializationManager 信号回调函数 ---
# 当 InitializationManager 完成新游戏或加载游戏流程后,此函数被调用
func _on_initialization_complete(): # 移除了 success 参数
print("Start Scene: Received initialization complete signal (New Game or Load).")
# 既然信号被发出,我们认为初始化流程是成功的
print("--------------------------------------------------")
print("Start Scene: Initialization successful!")
print("--------------------------------------------------")
# 切换到主游戏场景
Go_To_GameScene() # 保持不变
# 注意:这里没有处理初始化失败的情况,因为失败应该在
# start_new_game_process 或 load_game_process 返回 false 时,
# 或者在 SaveLoadManager.load_game 失败时,在调用处直接处理。
# 如果 InitializationManager 内部在发出信号前还可能失败,
# 则需要重新考虑信号设计或错误处理流程。
# func _on_initialization_complete(): # <--- 此方法不再需要,可以安全移除或注释掉
# print("Start Scene: Received initialization complete signal (New Game or Load).")
# print("--------------------------------------------------")
# print("Start Scene: Initialization successful!")
# print("--------------------------------------------------")
# Go_To_GameScene()
# --- 辅助函数 ---
# 切换到主游戏场景 (保持不变)
func Go_To_GameScene():
print("Start Scene: Attempting to switch to Game Scene:", GameScene_path)
var err = get_tree().change_scene_to_file(GameScene_path)
if err != OK:
printerr("Start Scene CRITICAL ERROR: Failed to switch to main game scene!")
printerr(" Path: " + GameScene_path)
printerr(" Error code: " + str(err))
# if error_message_label: error_message_label.text = "无法加载主游戏场景!"
# func Go_To_GameScene(): # <--- 此方法不再需要,因为场景切换由 LoadingScene 处理
# print("Start Scene: Attempting to switch to Game Scene:", GameScene_path)
# var err = get_tree().change_scene_to_file(GameScene_path)
# if err != OK:
# printerr("Start Scene CRITICAL ERROR: Failed to switch to main game scene!")

View File

@ -23,6 +23,7 @@ InitialDataManager="*res://Autoload/InitialDataManager.gd"
GameState="*res://Autoload/GameState.gd"
SaveLoadManager="*res://Autoload/SaveLoadManager.gd"
InitializationManager="*res://Autoload/InitializationManager.gd"
LoadingContext="*res://Autoload/LoadingContext.gd"
[display]