D5/Autoload/[文档]核心管理器说明文档.md

22 KiB
Raw Blame History

核心 Autoload 管理器说明文档 (v1.5 - 基于代码审查与新增 LoadingContext)

文档目的: 本文档旨在详细说明游戏核心的五个 Autoload 管理器 (InitializationManager, InitialDataManager, GameState, SaveLoadManager, LoadingContext) 的设计、职责、交互方式以及对外提供的接口API特别注意:本文档描述的是基于“数据枢纽”模型的架构,并已根据最新代码实现进行更新。 它是后续开发其他游戏玩法模块如员工管理、项目管理、UI 等)时的主要参考依据,确保各模块能正确、高效地与核心系统协作。


重要:模块开发者的责任 (Module Developer Responsibilities)

在当前的“数据枢纽”模型下,GameState 仅作为通用数据容器。因此各个功能模块如员工管理、项目管理、UI 等)承担以下关键责任:

  1. Key 管理: 定义并维护模块自身所需状态数据在 GameState 中的唯一 key。建议采用命名空间或前缀避免冲突 (e.g., "employee.list", "project.active_ids")。
  2. 数据结构定义: 定义与模块相关的 key 所对应的 value 的具体数据结构(字典、数组、基础类型等)。
  3. 初始化/状态恢复:
    • 必须监听 GameStatestate_restored 信号 (用于读档后恢复) 以及 state_initialized 信号 (用于新游戏初始化后,如果模块需要在此时进行特定设置)。
    • 在这些信号的回调函数中,调用 GameState.get_value(key) 来获取本模块所需的所有数据。
    • 使用获取到的数据完成模块自身的初始化或状态恢复(更新内部变量、刷新 UI 等)。
  4. 数据读取与更新: 通过 GameState.get_value(key) 读取状态,通过 GameState.set_value(key, new_value) 更新状态。
  5. 业务逻辑验证: 所有业务逻辑相关的验证(如花钱前检查余额、操作是否允许等)必须在调用 GameState.set_value 之前由模块自身完成。GameState 不进行任何业务逻辑校验。
  6. 数据序列化: 确保存入 GameState 的所有数据都是可被 Godot 的 JSON 模块序列化的(避免存入 Node 引用等)。

1. 初始化管理器 (InitializationManager)

目标 (Purpose/Goal)

  • 作为游戏启动流程的总控制器,统一协调新游戏开始或存档加载的准备工作。
  • 确保在通知模块进行状态恢复前,GameState 已被正确的数据填充。
  • 提供一个清晰的入口点来响应用户的启动选择(新游戏/加载通常由加载场景Loading Scene在获取必要数据后调用。

核心职责 (Responsibilities)

  • 流程决策: 根据传递的指令(通常来自加载场景,该场景会根据 LoadingContext 的信息决定)启动“新游戏”流程还是“载入存档”流程。
  • 数据准备:
    • 新游戏: 确保 InitialDataManager 数据加载完成 -> 调用 InitialDataManager 获取初始数据 (settings_data, task_dev_data, npc_initial_data) -> 调用 _build_initial_state 方法,根据获取的数据构建注入 GameState 的初始状态字典。此方法会:
      • 合并基础设置 (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 加载失败、构建初始状态失败、获取的初始数据为空)发生的错误,向用户提供反馈并阻止进入游戏。

对外接口 (Public API)

  • 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)

  • 接收: 来自加载场景 (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_gamerestore_state)。
  • 发出: new_game_initialized, game_loaded 信号。
  • (可能) 间接影响 SceneTree (通过信号触发场景切换,由其他脚本执行)。

核心逻辑流程 (Core Logic/Flow)

  • 新游戏: (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)

  • 必须配置为 Godot 的 Autoload (单例),以便全局访问。

2. 初始化数据管理器 (InitialDataManager)

目标 (Purpose/Goal)

  • 负责加载和管理游戏的基础静态配置数据
  • 提供对这些数据的只读访问接口。
  • 作为新游戏流程中构建初始状态字典的数据来源之一。
  • 实现数据与游戏逻辑代码的分离。

核心职责 (Responsibilities)

  • 数据读取与解析:res://Data/ 下的 .json 文件加载数据 (当前加载 Init_Base.json, task_development.json, 和 npc.json)。
  • 数据存储与缓存: 内部存储解析后的数据,确保只加载一次。
  • 数据提供: 通过函数接口提供数据的深拷贝副本。

对外接口 (Public API / Data Access Functions)

  • 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 文件结构。所有通过此管理器获取的数据都应视为只读npc.json 文件应为字典结构,task_development.jsonInit_Base.json 也应为字典结构。

对外信号 (Public Signals)

  • 通常不发出信号,因为它主要提供静态数据查询。

交互关键点 (Key Interactions)

  • 被调用: InitializationManager (用于构建初始状态字典), 以及任何需要在运行时查询基础静态配置的模块(如 UI 显示、逻辑判断等)。

数据处理 (Data Handled)

  • 输入: res://Data/ 路径下的 Init_Base.json, task_development.json, npc.json 文件。
  • 输出: 解析后的 Godot 数据结构(字典),通过函数接口提供(作为深拷贝副本)。

配置 (Configuration)

  • 必须配置为 Godot 的 Autoload (单例)。

3. 游戏状态管理器 (GameState)

目标 (Purpose/Goal)

  • 作为游戏运行时动态状态中央存储库 (Data Hub)
  • 提供通用的 Key-Value 接口供其他系统存储和检索游戏状态数据。
  • 不理解存储数据的具体含义或结构。
  • 在状态数据被完全初始化或恢复后,通知所有监听模块。
  • 管理游戏暂停状态及其他系统级状态(如是否在任务中),这些状态统一存储在 _state_data"system_status" 键下的字典中。

核心职责 (Responsibilities)

  • 状态存储: 内部维护一个核心字典 (_state_data),用于存储所有动态游戏状态的 Key-Value 对。
  • 通用状态访问: 提供通用的 set_value, get_value 接口。
  • 状态初始化: 提供 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"] 的内容(例如,查找各分类如平台、玩法、主题、开发策略中首个标记为 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" 字典的副本)。
  • is_game_paused() -> bool: 返回游戏是否暂停 (从 "system_status" 读取 game_pause,若 "system_status" 或键不存在则返回 false)。
  • is_on_task_status() -> bool: 返回是否处于任务中状态 (从 "system_status" 读取 is_on_task,若 "system_status" 或键不存在则返回 false)。

对外信号 (Public Signals)

  • 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" 的状态,并且其新值为字典类型时发出,或者在 initialize_for_new_game / restore_state 后如果存在 "time" 键时发出。主要用于保持与旧代码或特定UI如日期显示的兼容性。传递新日期字典的副本。

数据处理 (Data Handled)

  • 核心: 管理一个包含所有动态游戏状态的 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)

  • 必须配置为 Godot 的 Autoload (单例)。

4. 存档管理器 (SaveLoadManager)

目标 (Purpose/Goal)

  • 负责将 GameState 提供的完整状态字典持久化存储到文件系统,以及从文件加载状态字典。
  • 封装文件操作和 JSON 序列化/反序列化。

核心职责 (Responsibilities)

  • 存档 (Save): 调用 GameState.get_savable_state() 获取状态字典 -> 添加元数据 (存档时间 ISO 8601 格式, 项目版本号) -> 序列化为 JSON (使用制表符缩进以提高可读性) -> 写入 user:// 目录下的存档文件 (文件名如 save_1.json)。
  • 读档 (Load):user:// 目录读取存档文件 -> 解析 JSON 为字典 -> 验证基本结构 (如存在 game_state 键且其值为字典) -> 返回提取出的 game_state 部分字典给调用者 (例如主菜单UI它随后会将此数据通过 LoadingContext 传递给加载流程)。如果失败(文件不存在、无法解析、格式错误等),则返回 null
  • 文件管理: 管理 user:// 目录下的存档文件命名 (基于槽位 IDsave_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 或流程控制器调用,其结果随后通过 LoadingContext 传递给 InitializationManager.load_game_process)
  • does_save_exist(slot_id: int) -> bool: 检查指定槽位是否存在存档文件。

对外信号 (Public Signals)

  • 当前脚本未定义信号。 如果需要向 UI 提供存档/读档过程的反馈(如开始、结束、进度),可以在未来添加相关信号。

交互关键点 (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 (用于 is_debug_build 检查)。
  • 重要: 开发者在编写其他模块时,无需直接与 SaveLoadManager 交互。模块的状态通过 GameState 间接被保存和加载。

数据处理 (Data Handled)

  • 输入 (Save):GameState 获取的状态字典。
  • 输出 (Save): user:// 目录下的 JSON 文件 (包含 metadatagame_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_performsave_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)

  • 输入: 加载操作类型 (字符串) 和可选的存档数据 (字典)。
  • 存储: 内部临时存储这些输入。
  • 输出: 加载场景通过直接访问其公共属性来读取这些数据。