Files
FlowScope/docs/superpowers/specs/2026-05-14-gamecore-design.md
2026-05-15 15:38:40 +08:00

453 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GameCore 游戏框架设计文档
## 概述
通用 Unity 游戏框架,以 Unity Package 形式分发,基于自研轻量 DI 容器,采用核心扩展式架构。目标:跨项目可复用 + 开箱即用的常用系统。
- **目标平台:** Unity 2022 LTS
- **架构模式:** 依赖注入 (DI)
- **分发形式:** Unity Package
- **DI 容器:** 自研轻量实现(反射,预留 Source Generator 优化接口)
---
## 架构:核心扩展式
```
GameCore (核心 asmdef)
├── DI 容器
├── 所有模块接口定义
└── 核心模块:事件系统、对象池、游戏流程控制、计时器
扩展包 (各自独立 asmdef只依赖核心)
├── GameCore.UI
├── GameCore.Audio
├── GameCore.Resource
├── GameCore.Data
├── GameCore.Save
└── GameCore.Network
可替换实现 (各自独立 asmdef)
├── GameCore.Defaults — 默认 EventBus + Timer
├── GameCore.R3 — R3 实现(可选替换 Defaults
├── GameCore.Resource.Resources
├── GameCore.Resource.Addressables
├── GameCore.Save.FileStorage
└── GameCore.Save.PlayerPrefsStorage
```
**设计原则:**
- 核心包定义接口不包含具体实现DI、Pool、Lifecycle 除外)
- 扩展包只依赖核心,互不依赖
- 可替换实现通过 DI Installer 注册,换实现不改上层代码
---
## DI 容器
### 能力
- 接口→实现绑定 (`Bind<IAudioManager>().To<AudioManager>()`)
- 实例绑定 (`ToValue(instance)`)
- 生命周期:`AsSingleton()` / `AsTransient()`
- 子容器(场景级作用域,子容器销毁时自动释放其单例)
- 构造函数注入(普通 C# 类)
- 属性注入 `[Inject]`MonoBehaviour
- 方法注入 `[Inject]`(初始化/PostConstruct
- 自动递归解析依赖链
- 反射实现,预留 Source Generator 优化接口
### 子容器
```csharp
// 全局容器(跨场景)
var root = new GameContainer();
// 场景容器(继承全局,场景销毁时一起释放)
var sceneContainer = root.CreateChild();
// 子容器可访问父容器绑定
sceneContainer.Resolve<IAudioManager>(); // 从父容器拿
// 子容器同名绑定覆盖父容器
sceneContainer.Bind<IGameManager>().To<GameManager>().AsSingleton();
```
### 注入方式
```csharp
// 构造函数注入 — 普通 C# 类
public class AudioManager : IAudioManager
{
private readonly IEventBus _eventBus;
public AudioManager(IEventBus eventBus) { _eventBus = eventBus; }
}
// 属性注入 — MonoBehaviour
public class GameCoreEntry : MonoBehaviour
{
[Inject] private IEventBus _eventBus;
}
// 方法注入 — 初始化/PostConstruct
public class GameManager : IGameManager
{
[Inject]
public void Init(IEventBus eventBus, IDataManager data)
{
// 所有依赖就绪后执行
}
}
```
---
## 核心模块
### 事件总线 (IEventBus)
接口定义在核心包,实现可替换。
```csharp
public interface IEventBus
{
void Subscribe<T>(Action<T> handler) where T : IEvent;
void Unsubscribe<T>(Action<T> handler) where T : IEvent;
void Publish<T>(T evt) where T : IEvent;
}
```
- 事件用 `struct`,避免 GC 压力
- 泛型事件分发,每个事件类型独立维护订阅者列表
默认实现:`GameCore.Defaults.DefaultEventBus`
R3 实现:`GameCore.R3.R3EventBus`
### 计时器/调度器 (ITimerManager)
接口定义在核心包,实现可替换。
```csharp
public interface ITimerManager
{
ITimerHandle Delay(float seconds, Action callback);
ITimerHandle Repeat(float interval, Action callback);
void Tick(float deltaTime);
}
public interface ITimerHandle
{
void Cancel();
bool IsDone { get; }
}
```
- 不依赖 MonoBehaviour Coroutine纯 Tick 驱动
- 内部用对象池管理 Timer 对象
默认实现:`GameCore.Defaults.DefaultTimerManager`
R3 实现:`GameCore.R3.R3TimerManager`
### 对象池 (IPoolManager)
```csharp
public interface IPoolManager
{
IPool<T> CreatePool<T>(Func<T> factory, int prewarm = 0);
IGameObjectPool CreateGameObjectPool(string prefabPath, int prewarm = 0);
}
public interface IPool<T>
{
T Get();
void Release(T item);
}
```
- 泛型对象池,支持任意类型
- 预加载容量,支持预热
- GameObject 池的创建/销毁由 Resource 扩展包实现,核心只定义接口
### 游戏流程控制 (GameLifecycle)
```csharp
public static class GameCore
{
public static void Launch(StartupConfig config);
public static void Tick();
public static void Shutdown();
}
public class StartupConfig
{
public IInstaller[] Installers;
public IStartupTask[] StartupTasks;
}
public interface IInstaller
{
void Install(IGameContainer container);
}
public interface IStartupTask
{
Task Execute();
}
```
生命周期阶段:
```
Launch → Installers 注册绑定 → ResolveAll 创建单例 → StartupTasks 按序执行 → Running
场景切换时
子容器销毁
Shutdown
```
- `IInstaller` 统一模块注册入口
- `IStartupTask` 控制启动顺序(支持异步)
- 场景切换时自动销毁场景级子容器
- Shutdown 时按反序释放所有模块
---
## 扩展包
### GameCore.Resource — 资源加载
```csharp
public interface IResourceLoader
{
T Load<T>(string path) where T : Object;
Task<T> LoadAsync<T>(string address) where T : Object;
void Release<T>(T asset);
void ReleaseAll();
}
```
- 统一封装,支持引用计数,同一资源多次加载不重复
- 存储实现可替换Resources / Addressables / AssetBundle / 自定义
### GameCore.UI — UI 管理
```csharp
public interface IUIManager
{
T Open<T>(string layer = null) where T : UIPanelBase;
void Close<T>() where T : UIPanelBase;
void CloseAll();
void CloseLayer(string layer);
void RegisterLayer(string name, Canvas canvas, int sortOrder);
}
public abstract class UIPanelBase : MonoBehaviour
{
[Inject] protected IEventBus _eventBus;
[Inject] protected IResourceLoader _loader;
public virtual void OnOpen() { }
public virtual void OnClose() { }
}
```
- 层级由用户自定义注册,不预设固定层级
- 面板栈管理,支持返回上一级
- 面板复用(配合对象池)
### GameCore.Audio — 音频管理
```csharp
public interface IAudioManager
{
IAudioHandle PlayBGM(string name, bool loop = true, float fadeIn = 0f);
void StopBGM(float fadeOut = 0f);
IAudioHandle PlaySFX(string name);
void Stop(IAudioHandle handle);
void Pause(IAudioHandle handle);
void Resume(IAudioHandle handle);
void SetBGMVolume(float volume);
void SetSFXVolume(float volume);
void MuteAll();
void UnmuteAll();
}
public interface IAudioHandle
{
float Volume { get; set; }
bool IsPlaying { get; }
void Stop(float fadeOut = 0f);
}
```
- fadeIn/fadeOut 处理音频过渡,具体曲线在实现层
- IAudioHandle 可停止单个音频
- SFX 用对象池管理 AudioSource
### GameCore.Data — 数据/配置管理
```csharp
public interface IDataManager
{
void RegisterParser<TParser>(string format) where TParser : IDataParser;
void LoadModule(string moduleName);
void UnloadModule(string moduleName);
bool IsModuleLoaded(string moduleName);
T GetConfig<T>(string key) where T : IConfig;
IReadOnlyList<T> GetConfigs<T>() where T : IConfig;
}
public interface IDataParser
{
IEnumerable<IConfig> Parse(string content);
}
```
- 格式自定义:注册 IDataParser 实现JSON/CSV/ScriptableObject/自定义)
- 分模块按需加载和卸载
### GameCore.Save — 存档系统
```csharp
public interface ISaveManager
{
Task Save(string slotName, object data);
Task<T> Load<T>(string slotName);
void Delete(string slotName);
bool Exists(string slotName);
string[] ListSaves();
}
public interface ISaveStorage
{
Task Write(string key, byte[] data);
Task<byte[]> Read(string key);
void Delete(string key);
bool Exists(string key);
}
```
- ISaveManager 负责序列化和业务逻辑
- ISaveStorage 负责底层读写可替换File / PlayerPrefs / 云存档 / 自定义
- 支持多存档位
### GameCore.Network — 网络层
```csharp
public interface INetworkService
{
Task<T> Request<T>(string url, object body);
Task Connect(string url);
void Disconnect();
void Send<T>(T message);
void OnReceive<T>(Action<T> handler);
}
```
- HTTP 请求封装GET/POST
- WebSocket 长连接
- 断线重连、超时处理、心跳机制
---
## 包结构
```
com.yourname.gamecore/
├── package.json
├── README.md
├── CHANGELOG.md
├── LICENSE
├── Runtime/
│ ├── GameCore/ # 核心 asmdef
│ │ ├── DI/
│ │ ├── Events/IEventBus.cs
│ │ ├── Timer/ITimerManager.cs
│ │ ├── Pool/
│ │ ├── Lifecycle/
│ │ └── Interfaces/
│ │
│ ├── GameCore.Defaults/ # 默认 EventBus + Timer
│ ├── GameCore.R3/ # R3 实现(可选)
│ │
│ ├── GameCore.UI/
│ ├── GameCore.Audio/
│ │
│ ├── GameCore.Resource/ # 接口
│ ├── GameCore.Resource.Resources/ # Resources 实现
│ ├── GameCore.Resource.Addressables/ # Addressables 实现
│ │
│ ├── GameCore.Data/
│ │
│ ├── GameCore.Save/ # 接口 + 序列化
│ ├── GameCore.Save.FileStorage/
│ ├── GameCore.Save.PlayerPrefsStorage/
│ │
│ └── GameCore.Network/
├── Editor/
│ └── GameCore.Editor/
└── Samples~/
```
---
## 完整使用示例
```csharp
// 入口
public class GameBootstrap : MonoBehaviour
{
void Awake()
{
GameCore.Launch(new StartupConfig
{
Installers = new IInstaller[]
{
new CoreInstaller(),
new TimerInstaller(),
new ResourceInstaller<AddressablesResourceLoader>(),
new AudioInstaller(),
new UIInstaller(),
new DataInstaller(),
new SaveInstaller<FileSaveStorage>(),
},
StartupTasks = new IStartupTask[]
{
new LoadConfigTask(),
new ShowMainMenuTask(),
}
});
}
void Update() => GameCore.Tick();
void OnDestroy() => GameCore.Shutdown();
}
// 使用模块
public class MainMenuPanel : UIPanelBase
{
[Inject] private IAudioManager _audio;
[Inject] private IEventBus _eventBus;
[Inject] private IDataManager _data;
public override void OnOpen()
{
_audio.PlayBGM("main_menu", fadeIn: 1f);
var weapons = _data.GetConfigs<WeaponConfig>();
RenderWeaponList(weapons);
}
public void OnStartGameClick()
{
_audio.PlaySFX("click");
_eventBus.Publish(new StartGameEvent { Level = 1 });
}
}
// 事件定义
public struct StartGameEvent : IEvent
{
public int Level;
}
```