453 lines
11 KiB
Markdown
453 lines
11 KiB
Markdown
# 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;
|
||
}
|
||
```
|