第3章 全局系统与自动加载

全局系统与自动加载是游戏框架的核心基础设施,它们负责管理跨场景共享的数据和服务,协调系统初始化顺序,并提供统一的访问接口。本章将详细介绍如何在 Godot C# 中设计和实现这些关键系统。

3.1.1 AutoLoad设计原理

Godot的AutoLoad机制是游戏开发中最基础也是最重要的架构模式之一。理解其设计原理有助于正确构建全局系统。

为什么需要全局系统

游戏开发中常见的全局需求:

1. 跨场景状态保持

  • 玩家数据需要在不同关卡间传递
  • 游戏设置需要在整个游戏生命周期内有效
  • 成就进度需要持久化

2. 资源管理

  • 音频管理器需要全局访问
  • 输入系统需要持续监听
  • 网络连接需要保持

3. 事件协调

  • 全局事件总线需要跨场景工作
  • 成就系统需要监听各种游戏事件
  • 分析系统需要追踪玩家行为

AutoLoad vs 静态单例

两种实现全局系统的方式对比:

特性AutoLoad静态单例
生命周期随场景树管理随应用程序生命周期
Godot功能完整使用Godot API受限(无节点树访问)
初始化时机_EnterTree/_Ready首次访问时
调试友好可在编辑器查看需要日志调试
测试难度需要场景可直接实例化

推荐:优先使用AutoLoad,除非是纯数据类(如配置常量)。

AutoLoad的初始化顺序

AutoLoad节点的加载顺序至关重要:

初始化顺序:
1. 服务容器(ServiceProvider)
2. 事件总线(EventBus)
3. 资源管理器(ResourceManager)
4. 音频管理器(AudioManager)
5. 输入管理器(InputManager)
6. 游戏状态管理器(GameStateManager)

设计原则:被依赖的节点先初始化

AutoLoad的依赖管理

复杂的依赖关系需要仔细设计:

// 不推荐:循环依赖
// AudioManager 依赖 Settings
// Settings 依赖 AudioManager(获取默认音量)

// 推荐:分层依赖
// Core(无依赖)
//   ├── Settings
//   ├── EventBus
//   └── Logger
// Services(依赖Core)
//   ├── AudioManager(依赖Settings, EventBus)
//   ├── InputManager
//   └── ResourceManager

延迟初始化策略

某些服务可能不需要立即初始化:

public partial class AchievementManager : Node
{
    private bool _isInitialized = false;

    public override void _Ready()
    {
        // 延迟到实际需要时初始化
        // 可以监听游戏开始事件
        EventBus.Instance.GameStarted += OnGameStarted;
    }

    private void OnGameStarted()
    {
        if (!_isInitialized)
        {
            InitializeAchievements();
            _isInitialized = true;
        }
    }
}

3.1.2 服务容器设计思路

服务容器(Service Container)是实现依赖注入的核心组件,它将对象的创建和使用分离。

服务定位器 vs 依赖注入

两种依赖管理模式的对比:

服务定位器模式

// 主动获取依赖
public class Player
{
    private IAudioService _audio = ServiceLocator.GetService<IAudioService>();
}

优点:

  • 使用简单,不需要配置
  • 向后兼容现有代码

缺点:

  • 隐藏了类的依赖关系
  • 难以进行单元测试
  • 运行时错误风险

依赖注入模式

// 被动接收依赖
public class Player
{
    private IAudioService _audio;

    public Player(IAudioService audio)
    {
        _audio = audio;
    }
}

优点:

  • 依赖关系显式可见
  • 易于Mock测试
  • 编译期类型检查

缺点:

  • 需要配置容器
  • 增加代码复杂度

Godot中的服务容器设计

结合Godot特性的服务容器设计:

public interface IGameService
{
    void Initialize();
    void Shutdown();
}

public partial class ServiceProvider : Node
{
    private Dictionary<Type, IGameService> _services = new();

    // 注册服务
    public void Register<T>(T service) where T : class, IGameService
    {
        _services[typeof(T)] = service;
        service.Initialize();
    }

    // 获取服务
    public T GetService<T>() where T : class, IGameService
    {
        if (_services.TryGetValue(typeof(T), out var service))
            return (T)service;
        throw new Exception($"Service {typeof(T)} not found");
    }

    // 尝试获取服务(安全)
    public bool TryGetService<T>(out T service) where T : class, IGameService
    {
        if (_services.TryGetValue(typeof(T), out var s))
        {
            service = (T)s;
            return true;
        }
        service = null;
        return false;
    }
}

服务生命周期管理

不同服务需要不同的生命周期策略:

单例服务(游戏期间一直存在):

public class AudioManager : IGameService, IDisposable
{
    public void Initialize() { /* 初始化音频系统 */ }
    public void Shutdown() { /* 释放资源 */ }
    public void Dispose() { Shutdown(); }
}

场景服务(随场景变化):

public class LevelManager : IGameService
{
    public void Initialize()
    {
        SceneManager.Instance.SceneChanged += OnSceneChanged;
    }

    private void OnSceneChanged(string sceneName)
    {
        // 新场景加载时重新初始化
        LoadLevelData(sceneName);
    }
}

按需服务(延迟加载):

public class CloudSaveManager : IGameService
{
    private bool _isInitialized = false;

    public void EnsureInitialized()
    {
        if (!_isInitialized)
        {
            Initialize();
            _isInitialized = true;
        }
    }
}

3.1.3 跨场景数据持久化策略

跨场景数据持久化是全局系统的核心需求之一。

持久化需求分类

数据类型持久化需求存储位置
玩家进度长期(跨游戏会话)本地文件/云端
游戏设置长期本地配置
当前关卡短期(场景间)内存/静态变量
临时状态即时(当前会话)AutoLoad节点

内存级持久化

最简单的跨场景数据传递:

// 不推荐:静态变量
public static class GameData
{
    public static int PlayerHealth;
    public static int CurrentLevel;
}

// 推荐:AutoLoad + 数据对象
public partial class GameState : Node
{
    public PlayerData CurrentPlayer { get; set; }
    public LevelData CurrentLevel { get; set; }

    public void SaveCheckpoint()
    {
        _lastCheckpoint = new GameCheckpoint
        {
            PlayerData = CurrentPlayer.Clone(),
            LevelData = CurrentLevel.Clone()
        };
    }
}

文件级持久化

需要长期保存的数据使用文件存储:

public class SaveSystem : IGameService
{
    private string SaveDirectory =>
        Path.Combine(OS.GetUserDataDir(), "Saves");

    public void SaveGame(string slotName)
    {
        var saveData = new SaveData
        {
            Timestamp = DateTime.Now,
            PlayerState = ServiceProvider.Instance.GetService<GameState>().CurrentPlayer,
            Inventory = ServiceProvider.Instance.GetService<InventoryManager>().GetAllItems(),
            UnlockedLevels = ProgressManager.Instance.UnlockedLevels
        };

        string json = JsonSerializer.Serialize(saveData, new JsonSerializerOptions
        {
            WriteIndented = true
        });

        string path = Path.Combine(SaveDirectory, $"{slotName}.json");
        File.WriteAllText(path, json);
    }
}

自动保存策略

减少玩家进度丢失的自动保存机制:

  • 检查点自动保存:到达检查点时自动保存
  • 定时自动保存:每5-10分钟自动保存
  • 事件触发保存:重要事件后保存(如获得稀有物品)
  • 后台保存:不阻塞游戏线程的异步保存
public class AutoSaveManager : Node
{
    [Export] public float AutoSaveInterval { get; set; } = 300f; // 5分钟

    private float _timer = 0f;

    public override void _Process(double delta)
    {
        _timer += (float)delta;
        if (_timer >= AutoSaveInterval)
        {
            PerformAutoSave();
            _timer = 0f;
        }
    }

    private async void PerformAutoSave()
    {
        // 异步保存避免卡顿
        await Task.Run(() => SaveSystem.Instance.SaveGame("autosave"));
        GD.Print("[AutoSave] 自动保存完成");
    }
}

3.1 AutoLoad 服务容器设计

3.1.1 AutoLoad 概念和配置

Godot 的 AutoLoad(自动加载)功能允许我们将节点设置为在场景树中自动实例化,这些节点会在游戏启动时创建,并在整个游戏生命周期中保持存在。这使得 AutoLoad 节点成为实现单例服务容器的理想选择。

项目配置步骤:

  1. 在 Godot 编辑器中,点击 项目 -> 项目设置 -> 自动加载 标签
  2. 点击文件夹图标选择要自动加载的脚本或场景
  3. 设置节点名称(通常以 “Global” 或 “Manager” 为后缀)
  4. 勾选 启用 复选框
  5. 点击 添加 按钮

配置注意事项:

  • AutoLoad 节点的添加顺序决定了它们在场景树中的初始化顺序
  • 排在后面的 AutoLoad 可以依赖前面已初始化的 AutoLoad
  • 建议将服务容器类(如 ServiceProvider)放在第一个位置

3.1.2 单例模式实现

在 Godot C# 中,我们可以通过静态属性和 _Ready 方法来实现单例模式。

// 文件路径: Scripts/Core/ServiceProvider.cs
using Godot;
using System;
using System.Collections.Generic;

namespace GameFramework.Core
{
    /// <summary>
    /// 服务提供者 - 全局服务容器
    /// 作为所有管理器的中央注册表,提供依赖注入和服务定位功能
    /// </summary>
    public partial class ServiceProvider : Node
    {
        // 步骤1: 定义静态实例字段
        private static ServiceProvider _instance;

        // 步骤2: 定义公共静态属性,提供全局访问点
        public static ServiceProvider Instance
        {
            get
            {
                // 注意: 如果在游戏启动前访问,会抛出异常
                if (_instance == null)
                {
                    GD.PushError("ServiceProvider 尚未初始化!确保它已配置为 AutoLoad");
                }
                return _instance;
            }
        }

        // 步骤3: 创建服务注册表,用于存储已注册的服务
        private Dictionary<Type, object> _services = new Dictionary<Type, object>();

        // 步骤4: 创建服务初始化顺序列表
        private List<IService> _initializedServices = new List<IService>();

        // 步骤5: 在 _Ready 中设置单例实例
        public override void _Ready()
        {
            base._Ready();

            // 注意: 确保只有一个实例存在
            if (_instance != null)
            {
                GD.PushWarning("ServiceProvider 存在多个实例!这是设计错误。");
                QueueFree(); // 销毁重复的实例
                return;
            }

            _instance = this;
            GD.Print("[ServiceProvider] 服务容器已初始化");
        }

        // 步骤6: 注册服务方法
        /// <summary>
        /// 注册服务实例
        /// </summary>
        /// <typeparam name="T">服务接口类型</typeparam>
        /// <param name="implementation">服务实现实例</param>
        public void RegisterService<T>(T implementation) where T : class
        {
            Type serviceType = typeof(T);

            // 注意: 检查是否已存在相同类型的服务
            if (_services.ContainsKey(serviceType))
            {
                GD.PushWarning($"[ServiceProvider] 服务 {serviceType.Name} 已被注册,将被覆盖");
                _services.Remove(serviceType);
            }

            _services[serviceType] = implementation;

            // 如果服务实现了 IService 接口,调用初始化
            if (implementation is IService service)
            {
                service.Initialize();
                _initializedServices.Add(service);
            }

            GD.Print($"[ServiceProvider] 服务已注册: {serviceType.Name}");
        }

        // 步骤7: 获取服务方法
        /// <summary>
        /// 获取已注册的服务
        /// </summary>
        /// <typeparam name="T">服务接口类型</typeparam>
        /// <returns>服务实例,如果不存在则返回 null</returns>
        public T GetService<T>() where T : class
        {
            Type serviceType = typeof(T);

            if (_services.TryGetValue(serviceType, out object service))
            {
                return service as T;
            }

            GD.PushError($"[ServiceProvider] 未找到服务: {serviceType.Name}");
            return null;
        }

        // 步骤8: 检查服务是否已注册
        public bool HasService<T>() where T : class
        {
            return _services.ContainsKey(typeof(T));
        }

        // 步骤9: 游戏退出时清理
        public override void _Notification(int what)
        {
            // 注意: NOTIFICATION_WM_CLOSE_REQUEST 在窗口关闭时触发
            if (what == NotificationWMCloseRequest)
            {
                Shutdown();
            }
        }

        /// <summary>
        /// 关闭所有服务并清理资源
        /// </summary>
        private void Shutdown()
        {
            GD.Print("[ServiceProvider] 正在关闭服务...");

            // 按相反顺序关闭服务
            for (int i = _initializedServices.Count - 1; i >= 0; i--)
            {
                _initializedServices[i].Shutdown();
            }

            _initializedServices.Clear();
            _services.Clear();
            _instance = null;
        }
    }

    /// <summary>
    /// 服务接口 - 所有需要初始化的服务都应实现此接口
    /// </summary>
    public interface IService
    {
        /// <summary>
        /// 初始化服务
        /// </summary>
        void Initialize();

        /// <summary>
        /// 关闭服务,释放资源
        /// </summary>
        void Shutdown();
    }
}

3.1.3 服务容器设计

服务容器是整个框架的核心,它负责管理所有全局服务的生命周期。下面展示如何基于 ServiceProvider 构建具体的管理器。

// 文件路径: Scripts/Core/IManager.cs
namespace GameFramework.Core
{
    /// <summary>
    /// 管理器接口 - 定义所有管理器的通用契约
    /// </summary>
    public interface IManager : IService
    {
        /// <summary>
        /// 管理器优先级,数值越小越早初始化
        /// </summary>
        int Priority { get; }

        /// <summary>
        /// 管理器是否已就绪
        /// </summary>
        bool IsReady { get; }
    }
}
// 文件路径: Scripts/Core/ServiceInitializer.cs
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;

namespace GameFramework.Core
{
    /// <summary>
    /// 服务初始化器 - 自动注册和初始化所有管理器
    /// 必须配置为 AutoLoad,且在 ServiceProvider 之后加载
    /// </summary>
    public partial class ServiceInitializer : Node
    {
        // 步骤1: 定义需要自动注册的管理器列表
        [Export]
        public Godot.Collections.Array<NodePath> AutoLoadManagers { get; set; } =
            new Godot.Collections.Array<NodePath>();

        public override void _Ready()
        {
            base._Ready();

            // 步骤2: 确保 ServiceProvider 已就绪
            if (ServiceProvider.Instance == null)
            {
                GD.PushError("[ServiceInitializer] ServiceProvider 未初始化!");
                return;
            }

            // 步骤3: 从配置中查找管理器节点并注册
            InitializeManagers();

            GD.Print("[ServiceInitializer] 所有管理器初始化完成");
        }

        private void InitializeManagers()
        {
            // 步骤4: 收集所有实现了 IManager 接口的节点
            List<IManager> managers = new List<IManager>();

            // 从 AutoLoad 路径中查找
            foreach (var path in AutoLoadManagers)
            {
                if (path == null) continue;

                Node node = GetNodeOrNull<Node>(path);
                if (node is IManager manager)
                {
                    managers.Add(manager);
                }
            }

            // 步骤5: 从当前场景树中查找其他管理器
            foreach (Node child in GetTree().Root.GetChildren())
            {
                if (child is IManager manager && !managers.Contains(manager))
                {
                    managers.Add(manager);
                }
            }

            // 步骤6: 按优先级排序(数值小的先初始化)
            managers = managers.OrderBy(m => m.Priority).ToList();

            // 步骤7: 注册所有管理器
            foreach (var manager in managers)
            {
                RegisterManager(manager);
            }
        }

        private void RegisterManager(IManager manager)
        {
            // 注意: 使用管理器的具体类型进行注册
            Type managerType = manager.GetType();

            // 获取或创建 RegisterService 的泛型方法
            var method = typeof(ServiceProvider).GetMethod("RegisterService");
            var genericMethod = method.MakeGenericMethod(managerType);
            genericMethod.Invoke(ServiceProvider.Instance, new object[] { manager });

            GD.Print($"[ServiceInitializer] 已注册管理器: {managerType.Name} (优先级: {manager.Priority})");
        }
    }
}

3.2 跨场景数据持久化方案

3.2.1 场景切换时的数据保持

在 Godot 中,场景切换默认会销毁当前场景中的所有节点。为了保持跨场景的数据,我们需要设计专门的数据持久化方案。

// 文件路径: Scripts/Data/IDataPersist.cs
namespace GameFramework.Data
{
    /// <summary>
    /// 数据持久化接口 - 定义可保存/加载的数据契约
    /// </summary>
    public interface IDataPersist
    {
        /// <summary>
        /// 获取持久化数据对象
        /// </summary>
        object GetPersistentData();

        /// <summary>
        /// 从持久化数据对象恢复
        /// </summary>
        /// <param name="data">持久化数据</param>
        void LoadPersistentData(object data);
    }
}
// 文件路径: Scripts/Managers/DataManager.cs
using Godot;
using System;
using System.Collections.Generic;
using GameFramework.Core;

namespace GameFramework.Managers
{
    /// <summary>
    /// 数据管理器 - 管理游戏中所有需要持久化的数据
    /// 作为 AutoLoad 节点存在,跨场景保持数据
    /// </summary>
    public partial class DataManager : Node, IManager
    {
        // 步骤1: 实现 IManager 接口
        public int Priority => 0; // 最高优先级,最早初始化
        public bool IsReady { get; private set; }

        // 步骤2: 定义数据存储字典
        // Key: 数据类型全名, Value: 序列化后的数据
        private Dictionary<string, string> _persistentData = new Dictionary<string, string>();

        // 步骤3: 定义场景过渡数据存储(用于场景间临时传递数据)
        private Dictionary<string, object> _transitionData = new Dictionary<string, object>();

        // 步骤4: 定义自动保存间隔(秒)
        [Export]
        public float AutoSaveInterval { get; set; } = 300f; // 默认5分钟

        private double _timeSinceLastSave = 0;

        public override void _Ready()
        {
            base._Ready();

            // 注意: 防止场景切换时重复创建
            if (GetTree().Root.GetChildCount() > 0 &&
                GetTree().Root.GetChild(0) != this &&
                GetTree().Root.GetChild(0) is DataManager)
            {
                QueueFree();
                return;
            }
        }

        // 步骤5: 初始化接口实现
        public void Initialize()
        {
            LoadGameData();
            IsReady = true;
            GD.Print("[DataManager] 数据管理器初始化完成");
        }

        // 步骤6: 每帧检查自动保存
        public override void _Process(double delta)
        {
            if (AutoSaveInterval > 0)
            {
                _timeSinceLastSave += delta;
                if (_timeSinceLastSave >= AutoSaveInterval)
                {
                    AutoSave();
                    _timeSinceLastSave = 0;
                }
            }
        }

        /// <summary>
        /// 保存实现了 IDataPersist 接口的对象数据
        /// </summary>
        /// <param name="persistObject">需要保存数据的对象</param>
        public void SaveData(IDataPersist persistObject)
        {
            // 步骤7: 获取对象类型作为键
            string key = persistObject.GetType().FullName;

            // 步骤8: 序列化数据为 JSON
            object data = persistObject.GetPersistentData();
            string json = JSON.Stringify(data);

            // 步骤9: 存储到字典
            _persistentData[key] = json;

            GD.Print($"[DataManager] 数据已保存: {key}");
        }

        /// <summary>
        /// 加载数据到实现了 IDataPersist 接口的对象
        /// </summary>
        /// <param name="persistObject">需要加载数据的对象</param>
        /// <returns>是否成功加载</returns>
        public bool LoadData(IDataPersist persistObject)
        {
            string key = persistObject.GetType().FullName;

            // 步骤10: 检查是否存在保存的数据
            if (!_persistentData.ContainsKey(key))
            {
                GD.Print($"[DataManager] 未找到保存的数据: {key}");
                return false;
            }

            // 步骤11: 解析 JSON 并恢复数据
            string json = _persistentData[key];
            var data = JSON.Parse(json);

            if (data.Error != Error.Ok)
            {
                GD.PushError($"[DataManager] 解析数据失败: {key}, 错误: {data.Error}");
                return false;
            }

            // 步骤12: 将数据加载到对象
            persistObject.LoadPersistentData(data.Result);
            GD.Print($"[DataManager] 数据已加载: {key}");

            return true;
        }

        /// <summary>
        /// 设置场景过渡数据(用于场景切换时临时传递数据)
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="key">数据键</param>
        /// <param name="data">数据值</param>
        public void SetTransitionData<T>(string key, T data)
        {
            _transitionData[key] = data;
        }

        /// <summary>
        /// 获取场景过渡数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="key">数据键</param>
        /// <returns>数据值,如果不存在返回默认值</returns>
        public T GetTransitionData<T>(string key)
        {
            if (_transitionData.TryGetValue(key, out object value) && value is T typedValue)
            {
                // 注意: 获取后可以选择删除,使数据只能被消费一次
                // _transitionData.Remove(key);
                return typedValue;
            }
            return default(T);
        }

        /// <summary>
        /// 消费场景过渡数据(获取后自动删除)
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="key">数据键</param>
        /// <returns>数据值</returns>
        public T ConsumeTransitionData<T>(string key)
        {
            T value = GetTransitionData<T>(key);
            _transitionData.Remove(key);
            return value;
        }

        /// <summary>
        /// 清除所有场景过渡数据
        /// </summary>
        public void ClearTransitionData()
        {
            _transitionData.Clear();
            GD.Print("[DataManager] 场景过渡数据已清除");
        }

        // 步骤13: 自动保存实现
        private void AutoSave()
        {
            SaveGameData();
            GD.Print("[DataManager] 自动保存完成");
        }

        /// <summary>
        /// 保存游戏数据到文件
        /// </summary>
        public void SaveGameData()
        {
            // 注意: 使用用户数据目录存储存档
            string savePath = GetSaveFilePath();

            // 序列化所有数据
            var saveData = new Godot.Collections.Dictionary<string, string>(_persistentData);
            string json = saveData.ToString();

            // 使用 FileAccess 写入文件
            using var file = FileAccess.Open(savePath, FileAccess.ModeFlags.Write);
            if (file != null)
            {
                file.StoreString(json);
                GD.Print($"[DataManager] 游戏数据已保存到: {savePath}");
            }
            else
            {
                GD.PushError($"[DataManager] 保存游戏数据失败: {FileAccess.GetOpenError()}");
            }
        }

        /// <summary>
        /// 从文件加载游戏数据
        /// </summary>
        public void LoadGameData()
        {
            string savePath = GetSaveFilePath();

            // 检查文件是否存在
            if (!FileAccess.FileExists(savePath))
            {
                GD.Print("[DataManager] 未找到存档文件,将创建新存档");
                return;
            }

            // 读取文件内容
            using var file = FileAccess.Open(savePath, FileAccess.ModeFlags.Read);
            if (file == null)
            {
                GD.PushError($"[DataManager] 读取存档失败: {FileAccess.GetOpenError()}");
                return;
            }

            string json = file.GetAsText();
            var data = JSON.Parse(json);

            if (data.Error == Error.Ok && data.Result is Godot.Collections.Dictionary dict)
            {
                _persistentData.Clear();
                foreach (var key in dict.Keys)
                {
                    _persistentData[key.ToString()] = dict[key].ToString();
                }
                GD.Print("[DataManager] 游戏数据加载完成");
            }
            else
            {
                GD.PushError("[DataManager] 解析存档数据失败");
            }
        }

        /// <summary>
        /// 获取存档文件路径
        /// </summary>
        /// <returns>存档文件完整路径</returns>
        private string GetSaveFilePath()
        {
            // 注意: 使用 GetUserDir() 获取用户数据目录,确保跨平台兼容性
            string userDir = OS.GetUserDataDir();
            return System.IO.Path.Combine(userDir, "savegame.json");
        }

        // 步骤14: 关闭接口实现
        public void Shutdown()
        {
            SaveGameData();
            _persistentData.Clear();
            _transitionData.Clear();
            IsReady = false;
            GD.Print("[DataManager] 数据管理器已关闭");
        }
    }
}

3.2.2 数据传递模式

// 文件路径: Scripts/Data/SceneDataPackage.cs
using Godot;
using System.Collections.Generic;

namespace GameFramework.Data
{
    /// <summary>
    /// 场景数据包 - 用于场景间传递复杂数据
    /// </summary>
    public class SceneDataPackage
    {
        // 步骤1: 定义数据存储字典
        private Dictionary<string, object> _data = new Dictionary<string, object>();

        // 步骤2: 定义场景进入参数
        public string TargetScenePath { get; set; }
        public string EntryPointId { get; set; }
        public Vector2? PlayerPosition { get; set; }

        /// <summary>
        /// 添加数据到包
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="key">数据键</param>
        /// <param name="value">数据值</param>
        public void Set<T>(string key, T value)
        {
            _data[key] = value;
        }

        /// <summary>
        /// 从包中获取数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="key">数据键</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns>数据值</returns>
        public T Get<T>(string key, T defaultValue = default)
        {
            if (_data.TryGetValue(key, out object value) && value is T typedValue)
            {
                return typedValue;
            }
            return defaultValue;
        }

        /// <summary>
        /// 检查是否包含指定键
        /// </summary>
        /// <param name="key">数据键</param>
        /// <returns>是否包含</returns>
        public bool Contains(string key)
        {
            return _data.ContainsKey(key);
        }

        /// <summary>
        /// 清除所有数据
        /// </summary>
        public void Clear()
        {
            _data.Clear();
            TargetScenePath = null;
            EntryPointId = null;
            PlayerPosition = null;
        }
    }
}

3.2.3 存档与加载

// 文件路径: Scripts/Managers/SaveManager.cs
using Godot;
using System;
using System.Collections.Generic;
using System.IO;
using GameFramework.Core;

namespace GameFramework.Managers
{
    /// <summary>
    /// 存档管理器 - 提供完整的存档/读档功能
    /// 支持多个存档槽位和自动备份
    /// </summary>
    public partial class SaveManager : Node, IManager
    {
        // 步骤1: 实现 IManager 接口
        public int Priority => 1;
        public bool IsReady { get; private set; }

        // 步骤2: 定义存档配置
        [Export]
        public int MaxSaveSlots { get; set; } = 5; // 最大存档槽位数

        [Export]
        public bool EnableAutoBackup { get; set; } = true; // 是否启用自动备份

        [Export]
        public int MaxBackupCount { get; set; } = 3; // 每个存档的最大备份数

        // 步骤3: 定义存档数据结构
        [System.Serializable]
        public class SaveData
        {
            public string SaveId { get; set; }
            public string SaveName { get; set; }
            public DateTime SaveTime { get; set; }
            public string GameVersion { get; set; }
            public int PlayTimeSeconds { get; set; }
            public Godot.Collections.Dictionary<string, string> GameData { get; set; }

            // 元数据用于显示
            public string SceneName { get; set; }
            public string PlayerLevel { get; set; }
        }

        // 步骤4: 定义存档事件
        [Signal]
        public delegate void SaveCompletedEventHandler(string savePath);

        [Signal]
        public delegate void LoadCompletedEventHandler(string savePath);

        [Signal]
        public delegate void SaveErrorEventHandler(string errorMessage);

        private string _saveDirectory;

        public void Initialize()
        {
            // 步骤5: 初始化存档目录
            _saveDirectory = Path.Combine(OS.GetUserDataDir(), "Saves");

            // 注意: 确保存档目录存在
            if (!Directory.Exists(_saveDirectory))
            {
                Directory.CreateDirectory(_saveDirectory);
            }

            IsReady = true;
            GD.Print($"[SaveManager] 存档管理器初始化完成,存档目录: {_saveDirectory}");
        }

        /// <summary>
        /// 创建新存档
        /// </summary>
        /// <param name="slotIndex">存档槽位(0-MaxSaveSlots-1)</param>
        /// <param name="saveName">存档名称</param>
        /// <returns>是否成功</returns>
        public bool CreateSave(int slotIndex, string saveName = null)
        {
            // 步骤6: 验证槽位有效性
            if (slotIndex < 0 || slotIndex >= MaxSaveSlots)
            {
                EmitSignal(SignalName.SaveError, $"无效的存档槽位: {slotIndex}");
                return false;
            }

            // 步骤7: 如果启用备份,先备份现有存档
            if (EnableAutoBackup && SaveExists(slotIndex))
            {
                BackupSave(slotIndex);
            }

            // 步骤8: 构建存档数据
            var saveData = new SaveData
            {
                SaveId = Guid.NewGuid().ToString(),
                SaveName = saveName ?? $"存档 {slotIndex + 1}",
                SaveTime = DateTime.Now,
                GameVersion = GetGameVersion(),
                PlayTimeSeconds = GetPlayTime(),
                GameData = CollectGameData(),
                SceneName = GetCurrentSceneName(),
                PlayerLevel = GetPlayerLevel()
            };

            // 步骤9: 序列化并保存
            string savePath = GetSaveFilePath(slotIndex);
            if (WriteSaveFile(savePath, saveData))
            {
                EmitSignal(SignalName.SaveCompleted, savePath);
                GD.Print($"[SaveManager] 存档已创建: {savePath}");
                return true;
            }

            return false;
        }

        /// <summary>
        /// 加载存档
        /// </summary>
        /// <param name="slotIndex">存档槽位</param>
        /// <returns>是否成功</returns>
        public bool LoadSave(int slotIndex)
        {
            // 步骤10: 验证存档存在
            if (!SaveExists(slotIndex))
            {
                EmitSignal(SignalName.SaveError, $"存档不存在: 槽位 {slotIndex}");
                return false;
            }

            // 步骤11: 读取存档文件
            string savePath = GetSaveFilePath(slotIndex);
            SaveData saveData = ReadSaveFile(savePath);

            if (saveData == null)
            {
                EmitSignal(SignalName.SaveError, $"读取存档失败: {savePath}");
                return false;
            }

            // 步骤12: 验证版本兼容性
            if (!IsVersionCompatible(saveData.GameVersion))
            {
                GD.PushWarning($"[SaveManager] 存档版本不匹配: {saveData.GameVersion} vs {GetGameVersion()}");
            }

            // 步骤13: 恢复游戏数据
            RestoreGameData(saveData.GameData);

            EmitSignal(SignalName.LoadCompleted, savePath);
            GD.Print($"[SaveManager] 存档已加载: {savePath}");
            return true;
        }

        /// <summary>
        /// 删除存档
        /// </summary>
        /// <param name="slotIndex">存档槽位</param>
        /// <returns>是否成功</returns>
        public bool DeleteSave(int slotIndex)
        {
            string savePath = GetSaveFilePath(slotIndex);

            if (File.Exists(savePath))
            {
                File.Delete(savePath);

                // 同时删除备份
                string backupDir = GetBackupDirectory(slotIndex);
                if (Directory.Exists(backupDir))
                {
                    Directory.Delete(backupDir, true);
                }

                GD.Print($"[SaveManager] 存档已删除: {savePath}");
                return true;
            }

            return false;
        }

        /// <summary>
        /// 检查存档是否存在
        /// </summary>
        /// <param name="slotIndex">存档槽位</param>
        /// <returns>是否存在</returns>
        public bool SaveExists(int slotIndex)
        {
            return File.Exists(GetSaveFilePath(slotIndex));
        }

        /// <summary>
        /// 获取存档信息
        /// </summary>
        /// <param name="slotIndex">存档槽位</param>
        /// <returns>存档数据,如果不存在返回 null</returns>
        public SaveData GetSaveInfo(int slotIndex)
        {
            if (!SaveExists(slotIndex))
                return null;

            return ReadSaveFile(GetSaveFilePath(slotIndex));
        }

        /// <summary>
        /// 获取所有存档信息
        /// </summary>
        /// <returns>存档信息列表</returns>
        public List<SaveData> GetAllSaveInfo()
        {
            var saves = new List<SaveData>();
            for (int i = 0; i < MaxSaveSlots; i++)
            {
                saves.Add(GetSaveInfo(i));
            }
            return saves;
        }

        /// <summary>
        /// 快速存档(使用自动存档槽位)
        /// </summary>
        /// <returns>是否成功</returns>
        public bool QuickSave()
        {
            return CreateSave(0, "快速存档");
        }

        /// <summary>
        /// 快速读档(从自动存档槽位)
        /// </summary>
        /// <returns>是否成功</returns>
        public bool QuickLoad()
        {
            return LoadSave(0);
        }

        // 步骤14: 备份相关方法
        private void BackupSave(int slotIndex)
        {
            string sourcePath = GetSaveFilePath(slotIndex);
            string backupDir = GetBackupDirectory(slotIndex);

            if (!Directory.Exists(backupDir))
            {
                Directory.CreateDirectory(backupDir);
            }

            // 生成备份文件名(带时间戳)
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string backupPath = Path.Combine(backupDir, $"save_{timestamp}.sav");

            File.Copy(sourcePath, backupPath, true);

            // 清理旧备份
            CleanupOldBackups(backupDir);

            GD.Print($"[SaveManager] 存档已备份: {backupPath}");
        }

        private void CleanupOldBackups(string backupDir)
        {
            var backupFiles = new List<FileInfo>(new DirectoryInfo(backupDir).GetFiles("*.sav"));
            backupFiles.Sort((a, b) => b.CreationTime.CompareTo(a.CreationTime)); // 按时间倒序

            // 删除超出保留数量的旧备份
            for (int i = MaxBackupCount; i < backupFiles.Count; i++)
            {
                backupFiles[i].Delete();
            }
        }

        // 步骤15: 文件操作方法
        private string GetSaveFilePath(int slotIndex)
        {
            return Path.Combine(_saveDirectory, $"save_{slotIndex}.sav");
        }

        private string GetBackupDirectory(int slotIndex)
        {
            return Path.Combine(_saveDirectory, $"backup_{slotIndex}");
        }

        private bool WriteSaveFile(string path, SaveData data)
        {
            try
            {
                // 使用 JSON 序列化
                var dict = new Godot.Collections.Dictionary
                {
                    ["saveId"] = data.SaveId,
                    ["saveName"] = data.SaveName,
                    ["saveTime"] = data.SaveTime.ToString("O"),
                    ["gameVersion"] = data.GameVersion,
                    ["playTimeSeconds"] = data.PlayTimeSeconds,
                    ["gameData"] = data.GameData,
                    ["sceneName"] = data.SceneName,
                    ["playerLevel"] = data.PlayerLevel
                };

                string json = dict.ToString();

                using var file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
                if (file != null)
                {
                    file.StoreString(json);
                    return true;
                }
            }
            catch (Exception ex)
            {
                GD.PushError($"[SaveManager] 写入存档失败: {ex.Message}");
            }

            return false;
        }

        private SaveData ReadSaveFile(string path)
        {
            try
            {
                using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
                if (file == null) return null;

                string json = file.GetAsText();
                var result = JSON.Parse(json);

                if (result.Error != Error.Ok || !(result.Result is Godot.Collections.Dictionary dict))
                    return null;

                return new SaveData
                {
                    SaveId = dict["saveId"].AsString(),
                    SaveName = dict["saveName"].AsString(),
                    SaveTime = DateTime.Parse(dict["saveTime"].AsString()),
                    GameVersion = dict["gameVersion"].AsString(),
                    PlayTimeSeconds = dict["playTimeSeconds"].AsInt32(),
                    SceneName = dict.ContainsKey("sceneName") ? dict["sceneName"].AsString() : "",
                    PlayerLevel = dict.ContainsKey("playerLevel") ? dict["playerLevel"].AsString() : ""
                };
            }
            catch (Exception ex)
            {
                GD.PushError($"[SaveManager] 读取存档失败: {ex.Message}");
            }

            return null;
        }

        // 步骤16: 辅助方法
        private string GetGameVersion()
        {
            // 从项目设置或其他配置获取版本号
            return (string)ProjectSettings.GetSetting("application/config/version") ?? "1.0.0";
        }

        private int GetPlayTime()
        {
            // 从 TimeManager 或其他系统获取游戏时间
            return 0;
        }

        private string GetCurrentSceneName()
        {
            var sceneManager = ServiceProvider.Instance?.GetService<ISceneManager>();
            return sceneManager?.CurrentScene?.Name ?? "Unknown";
        }

        private string GetPlayerLevel()
        {
            // 从 PlayerManager 或其他系统获取玩家等级
            return "1";
        }

        private Godot.Collections.Dictionary<string, string> CollectGameData()
        {
            // 收集所有需要保存的游戏数据
            var data = new Godot.Collections.Dictionary<string, string>();

            // 触发数据收集事件,让各个系统添加自己的数据
            // EmitSignal(SignalName.CollectingSaveData, data);

            return data;
        }

        private void RestoreGameData(Godot.Collections.Dictionary<string, string> data)
        {
            // 恢复游戏数据到各个系统
            // EmitSignal(SignalName.RestoringSaveData, data);
        }

        private bool IsVersionCompatible(string saveVersion)
        {
            // 实现版本兼容性检查逻辑
            return saveVersion == GetGameVersion();
        }

        public void Shutdown()
        {
            IsReady = false;
            GD.Print("[SaveManager] 存档管理器已关闭");
        }
    }
}

3.3 启动流程控制与初始化顺序

3.3.1 游戏启动流程设计

// 文件路径: Scripts/Core/GameBoot.cs
using Godot;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GameFramework.Managers;

namespace GameFramework.Core
{
    /// <summary>
    /// 游戏启动器 - 控制整个游戏的启动流程
    /// 这是第一个被加载的 AutoLoad 节点
    /// </summary>
    public partial class GameBoot : Node
    {
        // 步骤1: 定义启动阶段枚举
        public enum BootPhase
        {
            None = 0,
            PreInitialize = 1,    // 预初始化
            CoreServices = 2,     // 核心服务
            DataLoading = 3,      // 数据加载
            ContentLoading = 4,   // 内容加载
            PostInitialize = 5,   // 后初始化
            Complete = 6          // 完成
        }

        // 步骤2: 定义启动配置
        [ExportGroup("启动配置")]
        [Export]
        public string InitialScene { get; set; } = "res://Scenes/MainMenu.tscn";

        [Export]
        public bool SkipIntro { get; set; } = false;

        [Export]
        public bool LoadLastSave { get; set; } = false;

        // 步骤3: 定义启动事件
        [Signal]
        public delegate void BootPhaseChangedEventHandler(BootPhase phase, float progress);

        [Signal]
        public delegate void BootCompletedEventHandler();

        [Signal]
        public delegate void BootErrorEventHandler(string error);

        // 步骤4: 启动状态
        public BootPhase CurrentPhase { get; private set; } = BootPhase.None;
        public float BootProgress { get; private set; } = 0f;
        public bool IsBooting { get; private set; } = false;
        public bool IsComplete { get; private set; } = false;

        // 步骤5: 启动任务队列
        private Queue<BootTask> _bootTasks = new Queue<BootTask>();
        private List<BootTask> _completedTasks = new List<BootTask>();

        public override void _Ready()
        {
            base._Ready();

            // 步骤6: 设置进程模式为始终运行(即使在暂停时)
            ProcessMode = ProcessModeEnum.Always;

            // 步骤7: 开始启动流程
            StartBootSequence();
        }

        /// <summary>
        /// 开始启动序列
        /// </summary>
        private async void StartBootSequence()
        {
            if (IsBooting) return;

            IsBooting = true;
            GD.Print("[GameBoot] 游戏启动序列开始...");

            try
            {
                // 步骤8: 按顺序执行各个启动阶段
                await ExecutePhase(BootPhase.PreInitialize, InitializePreServices);
                await ExecutePhase(BootPhase.CoreServices, InitializeCoreServices);
                await ExecutePhase(BootPhase.DataLoading, InitializeDataLoading);
                await ExecutePhase(BootPhase.ContentLoading, InitializeContentLoading);
                await ExecutePhase(BootPhase.PostInitialize, InitializePostServices);

                // 步骤9: 启动完成
                await CompleteBoot();
            }
            catch (Exception ex)
            {
                GD.PushError($"[GameBoot] 启动失败: {ex.Message}");
                EmitSignal(SignalName.BootError, ex.Message);
            }
            finally
            {
                IsBooting = false;
            }
        }

        /// <summary>
        /// 执行启动阶段
        /// </summary>
        private async Task ExecutePhase(BootPhase phase, Func<Task> phaseAction)
        {
            CurrentPhase = phase;
            EmitSignal(SignalName.BootPhaseChanged, (int)phase, BootProgress);

            GD.Print($"[GameBoot] 进入阶段: {phase}");

            if (phaseAction != null)
            {
                await phaseAction();
            }

            // 更新进度
            BootProgress = (int)phase / (float)BootPhase.Complete;
        }

        // 步骤10: 各阶段初始化实现
        private async Task InitializePreServices()
        {
            // 初始化日志系统
            InitializeLogging();

            // 初始化配置系统
            InitializeConfiguration();

            // 初始化性能监控
            InitializePerformanceMonitor();

            await Task.CompletedTask;
        }

        private async Task InitializeCoreServices()
        {
            // 确保 ServiceProvider 已就绪
            if (ServiceProvider.Instance == null)
            {
                throw new Exception("ServiceProvider 未初始化");
            }

            // 等待一帧确保其他 AutoLoad 节点已 _Ready
            await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
        }

        private async Task InitializeDataLoading()
        {
            // 加载存档数据
            var dataManager = ServiceProvider.Instance.GetService<DataManager>();
            if (dataManager != null)
            {
                // 等待 DataManager 就绪
                while (!dataManager.IsReady)
                {
                    await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
                }
            }

            // 加载配置数据
            await LoadConfigurationData();

            // 加载本地化数据
            await LoadLocalizationData();
        }

        private async Task InitializeContentLoading()
        {
            // 预加载必要资源
            await PreloadEssentialResources();

            // 初始化对象池
            InitializeObjectPools();
        }

        private async Task InitializePostServices()
        {
            // 应用用户设置
            ApplyUserSettings();

            // 初始化输入系统
            InitializeInputSystem();

            // 初始化音频系统
            InitializeAudioSystem();
        }

        private async Task CompleteBoot()
        {
            CurrentPhase = BootPhase.Complete;
            BootProgress = 1f;
            IsComplete = true;

            EmitSignal(SignalName.BootPhaseChanged, (int)BootPhase.Complete, 1f);
            EmitSignal(SignalName.BootCompleted);

            GD.Print("[GameBoot] 游戏启动序列完成");

            // 切换到初始场景
            await SwitchToInitialScene();
        }

        /// <summary>
        /// 切换到初始场景
        /// </summary>
        private async Task SwitchToInitialScene()
        {
            var sceneManager = ServiceProvider.Instance.GetService<ISceneManager>();
            if (sceneManager != null)
            {
                await sceneManager.LoadSceneAsync(InitialScene);
            }
            else
            {
                // 如果 SceneManager 不可用,使用默认方式切换场景
                GetTree().ChangeSceneToFile(InitialScene);
            }
        }

        // 步骤11: 辅助初始化方法
        private void InitializeLogging()
        {
            // 配置日志系统
            GD.Print("[GameBoot] 日志系统初始化");
        }

        private void InitializeConfiguration()
        {
            // 加载配置文件
            GD.Print("[GameBoot] 配置系统初始化");
        }

        private void InitializePerformanceMonitor()
        {
            // 设置性能监控
            GD.Print("[GameBoot] 性能监控初始化");
        }

        private async Task LoadConfigurationData()
        {
            GD.Print("[GameBoot] 加载配置数据...");
            await Task.Delay(100); // 模拟加载延迟
        }

        private async Task LoadLocalizationData()
        {
            GD.Print("[GameBoot] 加载本地化数据...");
            await Task.Delay(100);
        }

        private async Task PreloadEssentialResources()
        {
            GD.Print("[GameBoot] 预加载必要资源...");
            await Task.Delay(200);
        }

        private void InitializeObjectPools()
        {
            GD.Print("[GameBoot] 对象池初始化");
        }

        private void ApplyUserSettings()
        {
            GD.Print("[GameBoot] 应用用户设置");
        }

        private void InitializeInputSystem()
        {
            GD.Print("[GameBoot] 输入系统初始化");
        }

        private void InitializeAudioSystem()
        {
            GD.Print("[GameBoot] 音频系统初始化");
        }

        // 步骤12: 启动任务类定义
        private class BootTask
        {
            public string Name { get; set; }
            public BootPhase Phase { get; set; }
            public Func<Task> Action { get; set; }
            public bool IsCompleted { get; set; }
            public bool IsOptional { get; set; }
        }
    }
}

3.3.2 系统初始化顺序管理

// 文件路径: Scripts/Core/InitializationOrder.cs
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;

namespace GameFramework.Core
{
    /// <summary>
    /// 初始化顺序管理器 - 确保系统按正确顺序初始化
    /// </summary>
    public class InitializationOrder
    {
        // 步骤1: 定义系统依赖关系
        private Dictionary<Type, List<Type>> _dependencies = new Dictionary<Type, List<Type>>();

        // 步骤2: 定义系统优先级
        private Dictionary<Type, int> _priorities = new Dictionary<Type, int>();

        // 步骤3: 已排序的系统列表
        private List<Type> _sortedSystems;

        /// <summary>
        /// 注册系统及其依赖
        /// </summary>
        /// <typeparam name="T">系统类型</typeparam>
        /// <param name="priority">优先级(数值越小越早初始化)</param>
        /// <param name="dependencies">依赖的系统类型</param>
        public void RegisterSystem<T>(int priority, params Type[] dependencies) where T : IManager
        {
            Type systemType = typeof(T);
            _priorities[systemType] = priority;
            _dependencies[systemType] = new List<Type>(dependencies);

            // 注意: 注册新系统后需要重新排序
            _sortedSystems = null;
        }

        /// <summary>
        /// 获取按正确顺序排列的系统类型列表
        /// </summary>
        /// <returns>排序后的系统类型列表</returns>
        public List<Type> GetOrderedSystems()
        {
            if (_sortedSystems != null)
                return _sortedSystems;

            // 步骤4: 使用拓扑排序确保依赖关系正确
            _sortedSystems = TopologicalSort();
            return _sortedSystems;
        }

        /// <summary>
        /// 拓扑排序算法
        /// </summary>
        private List<Type> TopologicalSort()
        {
            var result = new List<Type>();
            var visited = new HashSet<Type>();
            var visiting = new HashSet<Type>();

            // 步骤5: 对所有系统进行拓扑排序
            var allSystems = new HashSet<Type>(_priorities.Keys);
            foreach (var depList in _dependencies.Values)
            {
                foreach (var dep in depList)
                {
                    allSystems.Add(dep);
                }
            }

            foreach (var system in allSystems)
            {
                if (!visited.Contains(system))
                {
                    if (!Visit(system, visited, visiting, result))
                    {
                        throw new InvalidOperationException("检测到循环依赖!");
                    }
                }
            }

            return result;
        }

        private bool Visit(Type system, HashSet<Type> visited, HashSet<Type> visiting, List<Type> result)
        {
            // 步骤6: 检测循环依赖
            if (visiting.Contains(system))
                return false; // 发现循环依赖

            if (visited.Contains(system))
                return true;

            visiting.Add(system);

            // 步骤7: 先访问依赖项
            if (_dependencies.ContainsKey(system))
            {
                foreach (var dependency in _dependencies[system])
                {
                    if (!Visit(dependency, visited, visiting, result))
                        return false;
                }
            }

            visiting.Remove(system);
            visited.Add(system);

            // 步骤8: 添加当前系统到结果
            // 注意: 按优先级插入到正确位置
            int insertIndex = result.Count;
            int currentPriority = GetPriority(system);

            for (int i = 0; i < result.Count; i++)
            {
                if (GetPriority(result[i]) > currentPriority)
                {
                    insertIndex = i;
                    break;
                }
            }

            result.Insert(insertIndex, system);
            return true;
        }

        private int GetPriority(Type system)
        {
            return _priorities.TryGetValue(system, out int priority) ? priority : int.MaxValue;
        }
    }

    /// <summary>
    /// 场景管理器接口
    /// </summary>
    public interface ISceneManager : IManager
    {
        Node CurrentScene { get; }
        System.Threading.Tasks.Task LoadSceneAsync(string path);
        void ChangeScene(string path);
    }
}
// 文件路径: Scripts/Managers/SceneManager.cs
using Godot;
using System;
using System.Threading.Tasks;
using GameFramework.Core;
using GameFramework.Data;

namespace GameFramework.Managers
{
    /// <summary>
    /// 场景管理器 - 处理场景切换和过渡效果
    /// </summary>
    public partial class SceneManager : Node, ISceneManager
    {
        // 步骤1: 实现 IManager 接口
        public int Priority => 5; // 中等优先级
        public bool IsReady { get; private set; }

        // 步骤2: 定义场景切换配置
        [Export]
        public PackedScene LoadingScreenScene { get; set; }

        [Export]
        public float TransitionDuration { get; set; } = 0.5f;

        [Export]
        public bool UseLoadingScreen { get; set; } = true;

        // 步骤3: 场景状态
        public Node CurrentScene { get; private set; }
        public bool IsTransitioning { get; private set; }
        public string CurrentScenePath { get; private set; }

        // 步骤4: 事件
        [Signal]
        public delegate void SceneLoadedEventHandler(string scenePath);

        [Signal]
        public delegate void SceneUnloadedEventHandler(string scenePath);

        [Signal]
        public delegate void TransitionStartedEventHandler();

        [Signal]
        public delegate void TransitionCompletedEventHandler();

        // 步骤5: 过渡动画节点
        private CanvasLayer _transitionLayer;
        private ColorRect _fadeRect;
        private AnimationPlayer _animationPlayer;

        public void Initialize()
        {
            // 步骤6: 创建过渡层
            CreateTransitionLayer();

            // 步骤7: 获取当前场景
            UpdateCurrentScene();

            IsReady = true;
            GD.Print("[SceneManager] 场景管理器初始化完成");
        }

        /// <summary>
        /// 异步加载场景
        /// </summary>
        /// <param name="path">场景资源路径</param>
        /// <param name="transitionData">传递给新场景的数据</param>
        public async Task LoadSceneAsync(string path, SceneDataPackage transitionData = null)
        {
            if (IsTransitioning)
            {
                GD.PushWarning("[SceneManager] 正在切换场景中,请求被忽略");
                return;
            }

            if (CurrentScenePath == path)
            {
                GD.PushWarning($"[SceneManager] 已经在场景 {path} 中");
                return;
            }

            IsTransitioning = true;
            EmitSignal(SignalName.TransitionStarted);

            // 步骤8: 设置场景过渡数据
            if (transitionData != null)
            {
                var dataManager = ServiceProvider.Instance?.GetService<DataManager>();
                if (dataManager != null)
                {
                    foreach (var key in transitionData.GetDataKeys())
                    {
                        dataManager.SetTransitionData(key, transitionData.Get<object>(key));
                    }
                }
            }

            // 步骤9: 开始淡出过渡
            await FadeOut();

            // 步骤10: 显示加载画面(如果需要)
            if (UseLoadingScreen && LoadingScreenScene != null)
            {
                ShowLoadingScreen();
            }

            // 步骤11: 卸载当前场景
            if (CurrentScene != null)
            {
                EmitSignal(SignalName.SceneUnloaded, CurrentScenePath);
                CurrentScene.QueueFree();
                CurrentScene = null;
            }

            // 步骤12: 异步加载新场景
            var loadTask = ResourceLoader.LoadThreadedRequest(path);

            // 等待加载完成
            while (ResourceLoader.LoadThreadedGetStatus(path) == ResourceLoader.ThreadLoadStatus.InProgress)
            {
                await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
            }

            var status = ResourceLoader.LoadThreadedGetStatus(path);
            if (status != ResourceLoader.ThreadLoadStatus.Loaded)
            {
                GD.PushError($"[SceneManager] 场景加载失败: {path}");
                IsTransitioning = false;
                return;
            }

            // 步骤13: 实例化并切换到新场景
            var packedScene = ResourceLoader.LoadThreadedGet(path) as PackedScene;
            if (packedScene != null)
            {
                CurrentScene = packedScene.Instantiate();
                GetTree().Root.AddChild(CurrentScene);
                CurrentScenePath = path;

                // 步骤14: 调用新场景的 OnSceneEntered(如果存在)
                CallDeferred(nameof(NotifySceneEntered));
            }

            // 步骤15: 隐藏加载画面
            HideLoadingScreen();

            // 步骤16: 淡入新场景
            await FadeIn();

            EmitSignal(SignalName.SceneLoaded, path);
            EmitSignal(SignalName.TransitionCompleted);

            IsTransitioning = false;
            GD.Print($"[SceneManager] 场景切换完成: {path}");
        }

        /// <summary>
        /// 同步切换场景(简单场景切换)
        /// </summary>
        /// <param name="path">场景资源路径</param>
        public void ChangeScene(string path)
        {
            if (IsTransitioning) return;

            Error error = GetTree().ChangeSceneToFile(path);
            if (error == Error.Ok)
            {
                CurrentScenePath = path;
                UpdateCurrentScene();
                EmitSignal(SignalName.SceneLoaded, path);
            }
            else
            {
                GD.PushError($"[SceneManager] 切换场景失败: {error}");
            }
        }

        /// <summary>
        /// 重新加载当前场景
        /// </summary>
        public async Task ReloadCurrentScene()
        {
            if (!string.IsNullOrEmpty(CurrentScenePath))
            {
                await LoadSceneAsync(CurrentScenePath);
            }
        }

        // 步骤17: 过渡动画方法
        private async Task FadeOut()
        {
            if (_animationPlayer == null) return;

            _transitionLayer.Visible = true;
            _animationPlayer.Play("fade_out");
            await ToSignal(_animationPlayer, AnimationPlayer.SignalName.AnimationFinished);
        }

        private async Task FadeIn()
        {
            if (_animationPlayer == null)
            {
                _transitionLayer.Visible = false;
                return;
            }

            _animationPlayer.Play("fade_in");
            await ToSignal(_animationPlayer, AnimationPlayer.SignalName.AnimationFinished);
            _transitionLayer.Visible = false;
        }

        private void CreateTransitionLayer()
        {
            // 步骤18: 创建过渡层节点
            _transitionLayer = new CanvasLayer
            {
                Name = "SceneTransitionLayer",
                Layer = 100, // 确保在最上层
                Visible = false
            };

            // 创建淡出矩形
            _fadeRect = new ColorRect
            {
                Name = "FadeRect",
                Color = Colors.Black,
                Size = new Vector2(1920, 1080), // 根据需要调整
                MouseFilter = Control.MouseFilterEnum.Ignore
            };
            _fadeRect.SetAnchorsPreset(Control.LayoutPreset.FullRect);
            _transitionLayer.AddChild(_fadeRect);

            // 创建动画播放器
            _animationPlayer = new AnimationPlayer();
            _transitionLayer.AddChild(_animationPlayer);

            // 步骤19: 创建默认过渡动画
            CreateDefaultAnimations();

            AddChild(_transitionLayer);
        }

        private void CreateDefaultAnimations()
        {
            // 创建淡出动画
            var fadeOutAnim = new Animation();
            fadeOutAnim.ResourceName = "fade_out";
            fadeOutAnim.Length = TransitionDuration;

            var trackIdx = fadeOutAnim.AddTrack(Animation.TrackType.Value);
            fadeOutAnim.TrackSetPath(trackIdx, "FadeRect:modulate:a");
            fadeOutAnim.TrackInsertKey(trackIdx, 0.0f, 0.0f);
            fadeOutAnim.TrackInsertKey(trackIdx, TransitionDuration, 1.0f);

            _animationPlayer.AddAnimationLibrary("", new AnimationLibrary());
            _animationPlayer.GetAnimationLibrary("").AddAnimation("fade_out", fadeOutAnim);

            // 创建淡入动画
            var fadeInAnim = new Animation();
            fadeInAnim.ResourceName = "fade_in";
            fadeInAnim.Length = TransitionDuration;

            trackIdx = fadeInAnim.AddTrack(Animation.TrackType.Value);
            fadeInAnim.TrackSetPath(trackIdx, "FadeRect:modulate:a");
            fadeInAnim.TrackInsertKey(trackIdx, 0.0f, 1.0f);
            fadeInAnim.TrackInsertKey(trackIdx, TransitionDuration, 0.0f);

            _animationPlayer.GetAnimationLibrary("").AddAnimation("fade_in", fadeInAnim);
        }

        private void ShowLoadingScreen()
        {
            // 显示加载画面的实现
        }

        private void HideLoadingScreen()
        {
            // 隐藏加载画面的实现
        }

        private void UpdateCurrentScene()
        {
            // 获取当前场景
            CurrentScene = GetTree().CurrentScene;
        }

        private void NotifySceneEntered()
        {
            // 通知新场景已进入
            if (CurrentScene is ISceneEntry entry)
            {
                entry.OnSceneEntered();
            }
        }

        public void Shutdown()
        {
            IsReady = false;
            if (_transitionLayer != null)
            {
                _transitionLayer.QueueFree();
            }
            GD.Print("[SceneManager] 场景管理器已关闭");
        }
    }

    /// <summary>
    /// 场景进入接口 - 场景可以实现此接口以接收进入通知
    /// </summary>
    public interface ISceneEntry
    {
        /// <summary>
        /// 当场景被加载并显示时调用
        /// </summary>
        void OnSceneEntered();
    }
}

3.3.3 依赖注入基础

// 文件路径: Scripts/Core/DependencyInjection.cs
using Godot;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace GameFramework.Core
{
    /// <summary>
    /// 依赖注入容器 - 简化服务获取和依赖管理
       /// </summary>
    public static class DI
    {
        // 步骤1: 创建服务解析器委托缓存
        private static Dictionary<Type, Func<object>> _resolvers = new Dictionary<Type, Func<object>>();

        // 步骤2: 已解析的单例缓存
        private static Dictionary<Type, object> _singletons = new Dictionary<Type, object>();

        /// <summary>
        /// 注册服务类型
        /// </summary>
        /// <typeparam name="TInterface">服务接口</typeparam>
        /// <typeparam name="TImplementation">实现类型</typeparam>
        public static void Register<TInterface, TImplementation>() where TImplementation : TInterface, new()
        {
            Type interfaceType = typeof(TInterface);
            _resolvers[interfaceType] = () => new TImplementation();
        }

        /// <summary>
        /// 注册单例服务
        /// </summary>
        /// <typeparam name="TInterface">服务接口</typeparam>
        /// <param name="instance">单例实例</param>
        public static void RegisterSingleton<TInterface>(TInterface instance)
        {
            Type interfaceType = typeof(TInterface);
            _singletons[interfaceType] = instance;
            _resolvers[interfaceType] = () => instance;
        }

        /// <summary>
        /// 解析服务实例
        /// </summary>
        /// <typeparam name="T">服务类型</typeparam>
        /// <returns>服务实例</returns>
        public static T Resolve<T>()
        {
            Type type = typeof(T);

            // 步骤3: 检查是否是单例
            if (_singletons.TryGetValue(type, out object singleton))
            {
                return (T)singleton;
            }

            // 步骤4: 使用解析器创建实例
            if (_resolvers.TryGetValue(type, out Func<object> resolver))
            {
                return (T)resolver();
            }

            // 步骤5: 尝试从 ServiceProvider 获取
            if (ServiceProvider.Instance != null)
            {
                var service = ServiceProvider.Instance.GetService<T>();
                if (service != null)
                    return service;
            }

            throw new InvalidOperationException($"未注册的服务类型: {type.Name}");
        }

        /// <summary>
        /// 尝试解析服务,如果未注册返回默认值
        /// </summary>
        public static bool TryResolve<T>(out T instance)
        {
            try
            {
                instance = Resolve<T>();
                return true;
            }
            catch
            {
                instance = default;
                return false;
            }
        }

        /// <summary>
        /// 清除所有注册
        /// </summary>
        public static void Clear()
        {
            _resolvers.Clear();
            _singletons.Clear();
        }
    }

    /// <summary>
    /// 自动注入特性 - 标记需要自动注入的字段
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class InjectAttribute : Attribute
    {
        public string ServiceName { get; set; }

        public InjectAttribute() { }

        public InjectAttribute(string serviceName)
        {
            ServiceName = serviceName;
        }
    }

    /// <summary>
    /// 自动注入扩展方法
    /// </summary>
    public static class InjectionExtensions
    {
        /// <summary>
        /// 自动注入标记了 [Inject] 特性的字段
        /// </summary>
        /// <param name="target">目标对象</param>
        public static void InjectDependencies(this object target)
        {
            Type type = target.GetType();

            // 步骤6: 注入字段
            foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                var attr = field.GetCustomAttribute<InjectAttribute>();
                if (attr != null)
                {
                    object service = ResolveService(field.FieldType, attr.ServiceName);
                    if (service != null)
                    {
                        field.SetValue(target, service);
                    }
                    else
                    {
                        GD.PushWarning($"无法注入字段 {field.Name},服务 {field.FieldType.Name} 未注册");
                    }
                }
            }

            // 步骤7: 注入属性
            foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                if (!prop.CanWrite) continue;

                var attr = prop.GetCustomAttribute<InjectAttribute>();
                if (attr != null)
                {
                    object service = ResolveService(prop.PropertyType, attr.ServiceName);
                    if (service != null)
                    {
                        prop.SetValue(target, service);
                    }
                }
            }
        }

        private static object ResolveService(Type serviceType, string serviceName)
        {
            // 步骤8: 尝试从 ServiceProvider 获取
            if (ServiceProvider.Instance != null)
            {
                // 使用反射调用泛型方法 GetService<T>()
                var method = typeof(ServiceProvider).GetMethod("GetService");
                var genericMethod = method.MakeGenericMethod(serviceType);
                return genericMethod.Invoke(ServiceProvider.Instance, null);
            }

            return null;
        }
    }
}
// 文件路径: Scripts/Examples/DependencyInjectionExample.cs
using Godot;
using GameFramework.Core;
using GameFramework.Managers;

namespace GameFramework.Examples
{
    /// <summary>
    /// 依赖注入使用示例
    /// </summary>
    public partial class DependencyInjectionExample : Node
    {
        // 步骤1: 使用 [Inject] 特性自动注入服务
        [Inject]
        private DataManager _dataManager;

        [Inject]
        private ISceneManager _sceneManager;

        public override void _Ready()
        {
            base._Ready();

            // 步骤2: 调用注入方法
            this.InjectDependencies();

            // 步骤3: 现在可以直接使用注入的服务
            if (_dataManager != null)
            {
                GD.Print("[DIExample] DataManager 注入成功");
            }

            if (_sceneManager != null)
            {
                GD.Print("[DIExample] SceneManager 注入成功");
            }
        }

        /// <summary>
        /// 使用 DI 容器直接解析服务
        /// </summary>
        private void ResolveServicesExample()
        {
            // 步骤4: 使用 DI.Resolve 获取服务
            var dataManager = DI.Resolve<DataManager>();
            var sceneManager = DI.Resolve<ISceneManager>();

            // 步骤5: 使用 TryResolve 安全获取服务
            if (DI.TryResolve<SaveManager>(out var saveManager))
            {
                GD.Print("SaveManager 可用");
            }
        }

        /// <summary>
        /// 构造函数注入示例(在 Godot 中较少使用)
        /// </summary>
        public void ConstructorInjectionExample()
        {
            // 步骤6: 手动注入构造函数参数
            var playerManager = new PlayerManager(
                DI.Resolve<DataManager>(),
                DI.Resolve<ISceneManager>()
            );
        }
    }

    /// <summary>
    /// 玩家管理器示例
    /// </summary>
    public class PlayerManager
    {
        private readonly DataManager _dataManager;
        private readonly ISceneManager _sceneManager;

        // 步骤7: 通过构造函数注入依赖
        public PlayerManager(DataManager dataManager, ISceneManager sceneManager)
        {
            _dataManager = dataManager;
            _sceneManager = sceneManager;
        }
    }
}

3.4 完整 GameManager 示例

// 文件路径: Scripts/Managers/GameManager.cs
using Godot;
using System;
using GameFramework.Core;
using GameFramework.Data;

namespace GameFramework.Managers
{
    /// <summary>
    /// 游戏管理器 - 主控制器,协调所有子系统
    /// 这是游戏中最高层的管理器,负责整体游戏流程控制
    /// </summary>
    public partial class GameManager : Node, IManager
    {
        // 步骤1: 单例访问
        public static GameManager Instance { get; private set; }

        // 步骤2: 实现 IManager 接口
        public int Priority => int.MinValue; // 最后初始化,最先关闭
        public bool IsReady { get; private set; }

        // 步骤3: 游戏状态
        public enum GameState
        {
            None,
            Booting,
            MainMenu,
            Playing,
            Paused,
            Loading,
            GameOver
        }

        public GameState CurrentState { get; private set; } = GameState.None;
        public GameState PreviousState { get; private set; } = GameState.None;

        // 步骤4: 运行数据
        public float GameTime { get; private set; }
        public bool IsPaused => CurrentState == GameState.Paused;

        // 步骤5: 依赖服务(通过 ServiceProvider 获取)
        private DataManager DataManager => ServiceProvider.Instance?.GetService<DataManager>();
        private ISceneManager SceneManager => ServiceProvider.Instance?.GetService<ISceneManager>();
        private SaveManager SaveManager => ServiceProvider.Instance?.GetService<SaveManager>();

        // 步骤6: 事件
        [Signal]
        public delegate void GameStateChangedEventHandler(GameState newState, GameState oldState);

        [Signal]
        public delegate void GamePausedEventHandler();

        [Signal]
        public delegate void GameResumedEventHandler();

        public override void _Ready()
        {
            base._Ready();

            // 步骤7: 设置单例
            if (Instance != null)
            {
                QueueFree();
                return;
            }
            Instance = this;

            // 步骤8: 注册到服务容器
            ServiceProvider.Instance?.RegisterService<IGameManager>(this);
        }

        public void Initialize()
        {
            // 步骤9: 初始化游戏
            ChangeState(GameState.Booting);

            IsReady = true;
            GD.Print("[GameManager] 游戏管理器初始化完成");
        }

        public override void _Process(double delta)
        {
            // 步骤10: 更新游戏时间
            if (!IsPaused && CurrentState == GameState.Playing)
            {
                GameTime += (float)delta;
            }
        }

        /// <summary>
        /// 开始新游戏
        /// </summary>
        public void StartNewGame()
        {
            GD.Print("[GameManager] 开始新游戏");

            // 重置游戏数据
            DataManager?.ClearTransitionData();
            GameTime = 0f;

            // 切换到游戏场景
            ChangeState(GameState.Loading);
            SceneManager?.LoadSceneAsync("res://Scenes/GameScene.tscn");
        }

        /// <summary>
        /// 继续游戏
        /// </summary>
        public void ContinueGame()
        {
            if (SaveManager?.SaveExists(0) == true)
            {
                GD.Print("[GameManager] 继续游戏");
                SaveManager.LoadSave(0);
                ChangeState(GameState.Playing);
            }
            else
            {
                GD.Print("[GameManager] 没有找到存档,开始新游戏");
                StartNewGame();
            }
        }

        /// <summary>
        /// 返回主菜单
        /// </summary>
        public void ReturnToMainMenu()
        {
            GD.Print("[GameManager] 返回主菜单");
            ChangeState(GameState.MainMenu);
            SceneManager?.LoadSceneAsync("res://Scenes/MainMenu.tscn");
        }

        /// <summary>
        /// 暂停游戏
        /// </summary>
        public void PauseGame()
        {
            if (CurrentState == GameState.Playing)
            {
                ChangeState(GameState.Paused);
                GetTree().Paused = true;
                EmitSignal(SignalName.GamePaused);
            }
        }

        /// <summary>
        /// 恢复游戏
        /// </summary>
        public void ResumeGame()
        {
            if (CurrentState == GameState.Paused)
            {
                GetTree().Paused = false;
                ChangeState(GameState.Playing);
                EmitSignal(SignalName.GameResumed);
            }
        }

        /// <summary>
        /// 退出游戏
        /// </summary>
        public void QuitGame()
        {
            GD.Print("[GameManager] 退出游戏");

            // 自动保存
            SaveManager?.QuickSave();

            // 关闭服务
            ServiceProvider.Instance?.Shutdown();

            // 退出应用
            GetTree().Quit();
        }

        /// <summary>
        /// 切换游戏状态
        /// </summary>
        private void ChangeState(GameState newState)
        {
            if (CurrentState == newState) return;

            PreviousState = CurrentState;
            CurrentState = newState;

            EmitSignal(SignalName.GameStateChanged, (int)newState, (int)PreviousState);
            GD.Print($"[GameManager] 游戏状态变更: {PreviousState} -> {newState}");
        }

        /// <summary>
        /// 保存游戏
        /// </summary>
        public void SaveGame(int slotIndex, string saveName = null)
        {
            if (SaveManager != null)
            {
                bool success = SaveManager.CreateSave(slotIndex, saveName);
                if (success)
                {
                    GD.Print($"[GameManager] 游戏已保存到槽位 {slotIndex}");
                }
            }
        }

        /// <summary>
        /// 加载游戏
        /// </summary>
        public void LoadGame(int slotIndex)
        {
            if (SaveManager != null)
            {
                bool success = SaveManager.LoadSave(slotIndex);
                if (success)
                {
                    ChangeState(GameState.Playing);
                    GD.Print($"[GameManager] 游戏已从槽位 {slotIndex} 加载");
                }
            }
        }

        public void Shutdown()
        {
            // 清理
            IsReady = false;
            Instance = null;
            GD.Print("[GameManager] 游戏管理器已关闭");
        }
    }

    /// <summary>
    /// 游戏管理器接口
    /// </summary>
    public interface IGameManager : IManager
    {
        GameManager.GameState CurrentState { get; }
        float GameTime { get; }
        bool IsPaused { get; }

        void StartNewGame();
        void ContinueGame();
        void ReturnToMainMenu();
        void PauseGame();
        void ResumeGame();
        void QuitGame();
        void SaveGame(int slotIndex, string saveName = null);
        void LoadGame(int slotIndex);
    }
}

3.5 最佳实践与注意事项

3.5.1 AutoLoad 配置建议

建议在项目设置中按以下顺序配置 AutoLoad 节点:

  1. ServiceProvider - 服务容器(最先加载)
  2. GameBoot - 游戏启动器
  3. DataManager - 数据管理器
  4. SaveManager - 存档管理器
  5. SceneManager - 场景管理器
  6. GameManager - 游戏管理器(最后加载,依赖其他管理器)

3.5.2 循环依赖避免

// 不好的做法 - 直接引用其他管理器
public class BadManager : IManager
{
    private SceneManager _sceneManager; // 直接引用具体类

    public void Initialize()
    {
        _sceneManager = ServiceProvider.Instance.GetService<SceneManager>(); // 可能导致循环
    }
}

// 好的做法 - 使用接口和延迟获取
public class GoodManager : IManager
{
    private ISceneManager SceneManager =>
        ServiceProvider.Instance?.GetService<ISceneManager>(); // 延迟获取

    public void Initialize()
    {
        // 初始化时不立即获取依赖
    }

    public void DoSomething()
    {
        // 使用时才获取
        SceneManager?.ChangeScene("path");
    }
}

3.5.3 测试与调试

// 文件路径: Scripts/Tests/ManagerTests.cs
using Godot;
using GameFramework.Core;
using GameFramework.Managers;

namespace GameFramework.Tests
{
    /// <summary>
    /// 管理器测试工具
    /// </summary>
    public static class ManagerTests
    {
        /// <summary>
        /// 验证所有管理器已正确注册
        /// </summary>
        public static void VerifyManagerRegistrations()
        {
            GD.Print("[ManagerTests] 验证管理器注册...");

            // 检查 ServiceProvider
            if (ServiceProvider.Instance == null)
            {
                GD.PushError("[ManagerTests] ServiceProvider 未注册");
                return;
            }

            // 检查各管理器
            CheckService<DataManager>("DataManager");
            CheckService<ISceneManager>("SceneManager");
            CheckService<SaveManager>("SaveManager");
            CheckService<IGameManager>("GameManager");

            GD.Print("[ManagerTests] 验证完成");
        }

        private static void CheckService<T>(string name) where T : class
        {
            var service = ServiceProvider.Instance.GetService<T>();
            if (service == null)
            {
                GD.PushError($"[ManagerTests] {name} 未注册");
            }
            else
            {
                GD.Print($"[ManagerTests] {name} 已注册");
            }
        }
    }
}

3.5.4 性能考虑

  1. 延迟初始化:管理器应该延迟加载资源,避免启动时加载所有内容
  2. 对象池:频繁创建销毁的对象应该使用对象池
  3. 异步加载:场景切换时使用异步加载避免卡顿
  4. 事件解耦:使用事件而非直接调用来降低耦合度

3.5.5 常见陷阱

  1. 在构造函数中访问 AutoLoad:此时场景树尚未就绪,应使用 _Ready
  2. 循环依赖:A 依赖 B,B 又依赖 A,使用接口和事件解耦
  3. 场景切换时数据丢失:使用 DataManager 保存跨场景数据
  4. 忘记处理游戏退出:实现 NOTIFICATION_WM_CLOSE_REQUEST 处理

本章详细介绍了 Godot C# 游戏开发框架中的全局系统与自动加载机制,包括服务容器设计、数据持久化方案、启动流程控制和依赖注入基础。这些系统是构建稳定、可扩展游戏架构的基石。