mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
docs(core): 更新命令系统文档并移除控制器独立文档
- 移除 controller.md 文件,将控制器相关内容整合到其他文档中 - 重构 command.md 文档,更新命令基类的类型参数设计 - 添加新的命令基类 AbstractCommand<TInput> 和 AbstractCommand<TInput, TResult> - 更新命令使用示例,采用输入参数对象替代构造函数参数 - 优化事件注册相关代码示例,移除 Godot 特定的生命周期方法 - 更新依赖注入容器文档,明确 Register 方法的泛型特性 - 添加模型异步初始化功能说明和相关接口介绍 - 重构查询系统文档,统一采用输入参数对象的设计模式 - 更新架构生命周期枚举值,使用更准确的阶段名称 - 添加 GitHub Actions 工作流配置,集成 LLM 准备的文档索引功能
This commit is contained in:
parent
aa0a9f7ab6
commit
93b25a19f7
6
.github/workflows/publish-docs.yml
vendored
6
.github/workflows/publish-docs.yml
vendored
@ -66,6 +66,12 @@ jobs:
|
|||||||
- name: Build VitePress
|
- name: Build VitePress
|
||||||
working-directory: docs
|
working-directory: docs
|
||||||
run: bun run build
|
run: bun run build
|
||||||
|
|
||||||
|
# 生成 LLM 索引文件
|
||||||
|
- name: Make docs LLM ready
|
||||||
|
uses: demodrive-ai/llms-txt-action@v1
|
||||||
|
with:
|
||||||
|
docs-path: docs/.vitepress/dist
|
||||||
|
|
||||||
# 上传构建产物作为 Pages 部署工件
|
# 上传构建产物作为 Pages 部署工件
|
||||||
- name: Upload Pages Artifact
|
- name: Upload Pages Artifact
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@ riderModule.iml
|
|||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
GFramework.sln.DotSettings.user
|
GFramework.sln.DotSettings.user
|
||||||
.idea/
|
.idea/
|
||||||
opencode.json
|
.opencode.json
|
||||||
|
.claude/
|
||||||
@ -82,7 +82,6 @@ export default defineConfig({
|
|||||||
{ text: '工具类', link: '/zh-CN/core/utility' },
|
{ text: '工具类', link: '/zh-CN/core/utility' },
|
||||||
{ text: '模型层', link: '/zh-CN/core/model' },
|
{ text: '模型层', link: '/zh-CN/core/model' },
|
||||||
{ text: '系统层', link: '/zh-CN/core/system' },
|
{ text: '系统层', link: '/zh-CN/core/system' },
|
||||||
{ text: '控制器', link: '/zh-CN/core/controller' },
|
|
||||||
{ text: '规则系统', link: '/zh-CN/core/rule' },
|
{ text: '规则系统', link: '/zh-CN/core/rule' },
|
||||||
{ text: '环境接口', link: '/zh-CN/core/environment' }
|
{ text: '环境接口', link: '/zh-CN/core/environment' }
|
||||||
]
|
]
|
||||||
|
|||||||
411
docs/zh-CN/api-reference/index.md
Normal file
411
docs/zh-CN/api-reference/index.md
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
# API 参考文档
|
||||||
|
|
||||||
|
本文档提供 GFramework 各模块的完整 API 参考。
|
||||||
|
|
||||||
|
## 核心命名空间
|
||||||
|
|
||||||
|
### GFramework.Core.architecture
|
||||||
|
|
||||||
|
核心架构命名空间,包含所有基础组件。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|--------------------|--------|
|
||||||
|
| `Architecture` | 应用架构基类 |
|
||||||
|
| `AbstractModel` | 数据模型基类 |
|
||||||
|
| `AbstractSystem` | 业务系统基类 |
|
||||||
|
| `AbstractCommand` | 命令基类 |
|
||||||
|
| `AbstractQuery<T>` | 查询基类 |
|
||||||
|
| `IController` | 控制器接口 |
|
||||||
|
| `IUtility` | 工具类接口 |
|
||||||
|
|
||||||
|
### GFramework.Core.events
|
||||||
|
|
||||||
|
事件系统命名空间。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|-------------------|----------|
|
||||||
|
| `IEvent` | 事件接口 |
|
||||||
|
| `IEventSystem` | 事件系统接口 |
|
||||||
|
| `TypeEventSystem` | 类型安全事件系统 |
|
||||||
|
|
||||||
|
### GFramework.Core.property
|
||||||
|
|
||||||
|
属性系统命名空间。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|-----------------------|--------|
|
||||||
|
| `BindableProperty<T>` | 可绑定属性 |
|
||||||
|
| `IUnRegister` | 注销接口 |
|
||||||
|
| `IUnRegisterList` | 注销列表接口 |
|
||||||
|
|
||||||
|
### GFramework.Core.ioc
|
||||||
|
|
||||||
|
IoC 容器命名空间。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|--------------|------|
|
||||||
|
| `IContainer` | 容器接口 |
|
||||||
|
| `Container` | 容器实现 |
|
||||||
|
|
||||||
|
### GFramework.Core.pool
|
||||||
|
|
||||||
|
对象池命名空间。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|------------------|-------|
|
||||||
|
| `IObjectPool<T>` | 对象池接口 |
|
||||||
|
| `ObjectPool<T>` | 对象池实现 |
|
||||||
|
|
||||||
|
## 常用 API
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class Architecture : IBelongToArchitecture
|
||||||
|
{
|
||||||
|
// 初始化架构
|
||||||
|
public void Initialize();
|
||||||
|
|
||||||
|
// 销毁架构
|
||||||
|
public void Destroy();
|
||||||
|
|
||||||
|
// 注册模型
|
||||||
|
public void RegisterModel<T>(T model) where T : IModel;
|
||||||
|
|
||||||
|
// 获取模型
|
||||||
|
public T GetModel<T>() where T : IModel;
|
||||||
|
|
||||||
|
// 注册系统
|
||||||
|
public void RegisterSystem<T>(T system) where T : ISystem;
|
||||||
|
|
||||||
|
// 获取系统
|
||||||
|
public T GetSystem<T>() where T : ISystem;
|
||||||
|
|
||||||
|
// 注册工具
|
||||||
|
public void RegisterUtility<T>(T utility) where T : IUtility;
|
||||||
|
|
||||||
|
// 获取工具
|
||||||
|
public T GetUt>() where T : IUtility;
|
||||||
|
|
||||||
|
// 发送命令
|
||||||
|
public void SendCommand<T>(T command) where T : ICommand;
|
||||||
|
|
||||||
|
// 发送查询
|
||||||
|
public TResult SendQuery<TQuery, TResult>(TQuery query)
|
||||||
|
where TQuery : IQuery<TResult>;
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
public void SendEvent<T>(T e) where T : IEvent;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractModel
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class AbstractModel : IBelongToArchitecture
|
||||||
|
{
|
||||||
|
// 初始化模型
|
||||||
|
protected abstract void OnInit();
|
||||||
|
|
||||||
|
// 销毁模型
|
||||||
|
protected virtual void OnDestroy();
|
||||||
|
|
||||||
|
// 获取架构
|
||||||
|
public IArchitecture GetArchitecture();
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
protected void SendEvent<T>(T e) where T : IEvent;
|
||||||
|
|
||||||
|
// 获取模型
|
||||||
|
protected T GetModel<T>() where T : IModel;
|
||||||
|
|
||||||
|
// 获取系统
|
||||||
|
protected T GetSystem<T>() where T : ISystem;
|
||||||
|
|
||||||
|
// 获取工具
|
||||||
|
protected T GetUtility<T>() where T : IUtility;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractSystem
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class AbstractSystem : IBelongToArchitecture
|
||||||
|
{
|
||||||
|
// 初始化系统
|
||||||
|
protected abstract void OnInit();
|
||||||
|
|
||||||
|
// 销毁系统
|
||||||
|
protected virtual void OnDestroy();
|
||||||
|
|
||||||
|
// 获取架构
|
||||||
|
public IArchitecture GetArchitecture();
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
protected void SendEvent<T>(T e) where T : IEvent;
|
||||||
|
|
||||||
|
// 注册事件
|
||||||
|
protected IUnRegister RegisterEvent<T>(Action<T> onEvent)
|
||||||
|
where T : IEvent;
|
||||||
|
|
||||||
|
// 获取模型
|
||||||
|
protected T GetModel<T>() where T : IModel;
|
||||||
|
|
||||||
|
// 获取系统
|
||||||
|
protected T GetSystem<T>() where T : ISystem;
|
||||||
|
|
||||||
|
// 获取工具
|
||||||
|
protected T GetUtility<T>() where T : IUtility;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractCommand
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class AbstractCommand : IBelongToArchitecture
|
||||||
|
{
|
||||||
|
// 执行命令
|
||||||
|
public void Execute();
|
||||||
|
|
||||||
|
// 命令实现
|
||||||
|
protected abstract void OnDo();
|
||||||
|
|
||||||
|
// 获取架构
|
||||||
|
public IArchitecture GetArchitecture();
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
protected void SendEvent<T>(T e) where T : IEvent;
|
||||||
|
|
||||||
|
// 获取模型
|
||||||
|
protected T GetModel<T>() where T : IModel;
|
||||||
|
|
||||||
|
// 获取系统
|
||||||
|
protected T GetSystem<T>() where T : ISystem;
|
||||||
|
|
||||||
|
// 获取工具
|
||||||
|
protected T GetUtility<T>() where T : IUtility;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractQuery<T>
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class AbstractQuery<T> : IBelongToArchitecture
|
||||||
|
{
|
||||||
|
// 执行查询
|
||||||
|
public T Do();
|
||||||
|
|
||||||
|
// 查询实现
|
||||||
|
protected abstract T OnDo();
|
||||||
|
|
||||||
|
// 获取架构
|
||||||
|
public IArchitecture GetArchitecture();
|
||||||
|
|
||||||
|
// 获取模型
|
||||||
|
protected T GetModel<T>() where T : IModel;
|
||||||
|
|
||||||
|
// 获取系统
|
||||||
|
protected T GetSystem<T>() where T : ISystem;
|
||||||
|
|
||||||
|
// 获取工具
|
||||||
|
protected T GetUtility<T>() where T : IUtility;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### BindableProperty<T>
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class BindableProperty<T>
|
||||||
|
{
|
||||||
|
// 构造函数
|
||||||
|
public BindableProperty(T initialValue = default);
|
||||||
|
|
||||||
|
// 获取或设置值
|
||||||
|
public T Value { get; set; }
|
||||||
|
|
||||||
|
// 注册监听器
|
||||||
|
public IUnRegister Register(Action<T> onValueChanged);
|
||||||
|
|
||||||
|
// 注册监听器(包含初始值)
|
||||||
|
public IUnRegister RegisterWithInitValue(Action<T> onValueChanged);
|
||||||
|
|
||||||
|
// 获取当前值
|
||||||
|
public T GetValue();
|
||||||
|
|
||||||
|
// 设置值
|
||||||
|
public void SetValue(T newValue);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 扩展方法
|
||||||
|
|
||||||
|
### 架构扩展
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 发送命令
|
||||||
|
public static void SendCommand<T>(this IBelongToArchitecture self, T command)
|
||||||
|
where T : ICommand;
|
||||||
|
|
||||||
|
// 发送查询
|
||||||
|
public static TResult SendQuery<TQuery, TResult>(
|
||||||
|
this IBelongToArchitecture self, TQuery query)
|
||||||
|
where TQuery : IQuery<TResult>;
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
public static void SendEvent<T>(this IBelongToArchitecture self, T e)
|
||||||
|
where T : IEvent;
|
||||||
|
|
||||||
|
// 获取模型
|
||||||
|
public static T GetModel<T>(this IBelongToArchitecture self)
|
||||||
|
where T : IModel;
|
||||||
|
|
||||||
|
// 获取系统
|
||||||
|
public static T GetSystem<T>(this IBelongToArchitecture self)
|
||||||
|
where T : ISystem;
|
||||||
|
|
||||||
|
// 获取工具
|
||||||
|
public static T GetUtility<T>(this IBelongToArchitecture self)
|
||||||
|
where T : IUtility;
|
||||||
|
|
||||||
|
// 注册事件
|
||||||
|
public static IUnRegister RegisterEvent<T>(
|
||||||
|
this IBelongToArchitecture self, Action<T> onEvent)
|
||||||
|
where T : IEvent;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 属性扩展
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 添加到注销列表
|
||||||
|
public static IUnRegister AddToUnregisterList(
|
||||||
|
this IUnRegister self, IUnRegisterList list);
|
||||||
|
|
||||||
|
// 注销所有
|
||||||
|
public static void UnRegisterAll(this IUnRegisterList self);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 游戏模块 API
|
||||||
|
|
||||||
|
### GFramework.Game
|
||||||
|
|
||||||
|
游戏业务扩展模块。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|---------------|--------|
|
||||||
|
| `GameSetting` | 游戏设置 |
|
||||||
|
| `GameState` | 游戏状态 |
|
||||||
|
| `IGameModule` | 游戏模块接口 |
|
||||||
|
|
||||||
|
## Godot 集成 API
|
||||||
|
|
||||||
|
### GFramework.Godot
|
||||||
|
|
||||||
|
Godot 引擎集成模块。
|
||||||
|
|
||||||
|
#### 主要类型
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|------------------|------------|
|
||||||
|
| `GodotNode` | Godot 节点扩展 |
|
||||||
|
| `GodotCoroutine` | Godot 协程 |
|
||||||
|
| `GodotSignal` | Godot 信号 |
|
||||||
|
|
||||||
|
## 源码生成器
|
||||||
|
|
||||||
|
### GFramework.SourceGenerators
|
||||||
|
|
||||||
|
自动代码生成工具。
|
||||||
|
|
||||||
|
#### 支持的生成器
|
||||||
|
|
||||||
|
| 生成器 | 说明 |
|
||||||
|
|--------------------|---------|
|
||||||
|
| `LoggingGenerator` | 日志生成器 |
|
||||||
|
| `EnumGenerator` | 枚举扩展生成器 |
|
||||||
|
| `RuleGenerator` | 规则生成器 |
|
||||||
|
|
||||||
|
## 常见用法示例
|
||||||
|
|
||||||
|
### 创建架构
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class MyArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
RegisterSystem(new PlayerSystem());
|
||||||
|
RegisterUtility(new StorageUtility());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var arch = new MyArchitecture();
|
||||||
|
arch.Initialize();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发送命令
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class AttackCommand : AbstractCommand
|
||||||
|
{
|
||||||
|
public int Damage { get; set; }
|
||||||
|
|
||||||
|
protected override void OnDo()
|
||||||
|
{
|
||||||
|
var player = this.GetModel<PlayerModel>();
|
||||||
|
this.SendEvent(new AttackEvent { Damage = Damage });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
arch.SendCommand(new AttackCommand { Damage = 10 });
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发送查询
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GetPlayerHealthQuery : AbstractQuery<int>
|
||||||
|
{
|
||||||
|
protected override int OnDo()
|
||||||
|
{
|
||||||
|
return this.GetModel<PlayerModel>().Health.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var health = arch.SendQuery(new GetPlayerHealthQuery());
|
||||||
|
```
|
||||||
|
|
||||||
|
### 监听事件
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PlayerSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerDied(PlayerDiedEvent e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Player died!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
更多详情请查看各模块的详细文档。
|
||||||
483
docs/zh-CN/best-practices/index.md
Normal file
483
docs/zh-CN/best-practices/index.md
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
# 最佳实践
|
||||||
|
|
||||||
|
本文档总结了使用 GFramework 的最佳实践和设计模式。
|
||||||
|
|
||||||
|
## 架构设计
|
||||||
|
|
||||||
|
### 1. 清晰的职责分离
|
||||||
|
|
||||||
|
**原则**:每一层都有明确的职责,不要混淆。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 正确的职责分离
|
||||||
|
public class PlayerModel : AbstractModel
|
||||||
|
{
|
||||||
|
// Model:只存储数据
|
||||||
|
public BindableProperty<int> Health { get; } = new(100);
|
||||||
|
public BindableProperty<int> Score { get; } = new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CombatSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
// System:处理业务逻辑
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<AttackEvent>(OnAttack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttack(AttackEvent e)
|
||||||
|
{
|
||||||
|
var player = this.GetModel<PlayerModel>();
|
||||||
|
player.Health.Value -= e.Damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlayerController : IController
|
||||||
|
{
|
||||||
|
// Controller:连接 UI 和逻辑
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
var player = _architecture.GetModel<PlayerModel>();
|
||||||
|
player.Health.RegisterWithInitValue(OnHealthChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHealthChanged(int health)
|
||||||
|
{
|
||||||
|
UpdateHealthDisplay(health);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 事件驱动设计
|
||||||
|
|
||||||
|
**原则**:使用事件解耦组件,避免直接调用。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 紧耦合
|
||||||
|
public class SystemA : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnEvent(EventA e)
|
||||||
|
{
|
||||||
|
var systemB = this.GetSystem<SystemB>();
|
||||||
|
systemB.DoSomething(); // 直接调用
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 松耦合
|
||||||
|
public class SystemA : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnEvent(EventA e)
|
||||||
|
{
|
||||||
|
this.SendEvent(new EventB()); // 发送事件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SystemB : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<EventB>(OnEventB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 命令查询分离
|
||||||
|
|
||||||
|
**原则**:明确区分修改状态(Command)和查询状态(Query)。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 正确的 CQRS
|
||||||
|
public class MovePlayerCommand : AbstractCommand
|
||||||
|
{
|
||||||
|
public Vector2 Direction { get; set; }
|
||||||
|
|
||||||
|
protected override void OnDo()
|
||||||
|
{
|
||||||
|
// 修改状态
|
||||||
|
this.SendEvent(new PlayerMovedEvent { Direction = Direction });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetPlayerPositionQuery : AbstractQuery<Vector2>
|
||||||
|
{
|
||||||
|
protected override Vector2 OnDo()
|
||||||
|
{
|
||||||
|
// 只查询,不修改
|
||||||
|
return this.GetModel<PlayerModel>().Position.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 代码组织
|
||||||
|
|
||||||
|
### 1. 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
GameProject/
|
||||||
|
├── Models/
|
||||||
|
│ ├── PlayerModel.cs
|
||||||
|
│ ├── GameStateModel.cs
|
||||||
|
│ └── InventoryModel.cs
|
||||||
|
├── Systems/
|
||||||
|
│ ├── CombatSystem.cs
|
||||||
|
│ ├── InventorySystem.cs
|
||||||
|
│ └── GameLogicSystem.cs
|
||||||
|
├── Commands/
|
||||||
|
│ ├── AttackCommand.cs
|
||||||
|
│ ├── MoveCommand.cs
|
||||||
|
│ └── UseItemCommand.cs
|
||||||
|
├── Queries/
|
||||||
|
│ ├── GetPlayerHealthQuery.cs
|
||||||
|
│ └── GetInventoryItemsQuery.cs
|
||||||
|
├── Events/
|
||||||
|
│ ├── PlayerDiedEvent.cs
|
||||||
|
│ ├── ItemUsedEvent.cs
|
||||||
|
│ └── EnemyDamagedEvent.cs
|
||||||
|
├── Controllers/
|
||||||
|
│ ├── PlayerController.cs
|
||||||
|
│ └── UIController.cs
|
||||||
|
├── Utilities/
|
||||||
|
│ ├── StorageUtility.cs
|
||||||
|
│ └── MathUtility.cs
|
||||||
|
└── GameArchitecture.cs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 命名规范
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Models:使用 Model 后缀
|
||||||
|
public class PlayerModel : AbstractModel { }
|
||||||
|
public class GameStateModel : AbstractModel { }
|
||||||
|
|
||||||
|
// Systems:使用 System 后缀
|
||||||
|
public class CombatSystem : AbstractSystem { }
|
||||||
|
public class InventorySystem : AbstractSystem { }
|
||||||
|
|
||||||
|
// Commands:使用 Command 后缀
|
||||||
|
public class AttackCommand : AbstractCommand { }
|
||||||
|
public class MoveCommand : AbstractCommand { }
|
||||||
|
|
||||||
|
// Queries:使用 Query 后缀
|
||||||
|
public class GetPlayerHealthQuery : AbstractQuery<int> { }
|
||||||
|
public class GetInventoryItemsQuery : AbstractQuery<List<Item>> { }
|
||||||
|
|
||||||
|
// Events:使用 Event 后缀
|
||||||
|
public class PlayerDiedEvent : IEvent { }
|
||||||
|
public class ItemUsedEvent : IEvent { }
|
||||||
|
|
||||||
|
// Controllers:使用 Controller 后缀
|
||||||
|
public class PlayerController : IController { }
|
||||||
|
|
||||||
|
// Utilities:使用 Utility 后缀
|
||||||
|
public class StorageUtility : IUtility { }
|
||||||
|
```
|
||||||
|
|
||||||
|
## 内存管理
|
||||||
|
|
||||||
|
### 1. 正确的注销管理
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class MyController : IController
|
||||||
|
{
|
||||||
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
var model = _architecture.GetModel<PlayerModel>();
|
||||||
|
|
||||||
|
// 注册事件并添加到注销列表
|
||||||
|
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
|
||||||
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
|
||||||
|
// 注册属性监听并添加到注销列表
|
||||||
|
model.Health.Register(OnHealthChanged)
|
||||||
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
// 统一注销所有监听器
|
||||||
|
_unregisterList.UnRegisterAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerDied(PlayerDiedEvent e) { }
|
||||||
|
private void OnHealthChanged(int health) { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 生命周期管理
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameManager
|
||||||
|
{
|
||||||
|
private GameArchitecture _architecture;
|
||||||
|
|
||||||
|
public void StartGame()
|
||||||
|
{
|
||||||
|
_architecture = new GameArchitecture();
|
||||||
|
_architecture.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndGame()
|
||||||
|
{
|
||||||
|
// 销毁架构,自动清理所有组件
|
||||||
|
_architecture.Destroy();
|
||||||
|
_architecture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 性能优化
|
||||||
|
|
||||||
|
### 1. 缓存组件引用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 低效:每次都查询
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
var model = _architecture.GetModel<PlayerModel>();
|
||||||
|
model.Health.Value -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 高效:缓存引用
|
||||||
|
private PlayerModel _playerModel;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_playerModel = _architecture.GetModel<PlayerModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
_playerModel.Health.Value -= 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 避免频繁的事件创建
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 低效:每帧创建新事件
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
this.SendEvent(new UpdateEvent()); // 频繁分配内存
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 高效:复用事件或使用对象池
|
||||||
|
private UpdateEvent _updateEvent = new UpdateEvent();
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
this.SendEvent(_updateEvent);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 异步处理重操作
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class LoadDataCommand : AbstractCommand
|
||||||
|
{
|
||||||
|
protected override async void OnDo()
|
||||||
|
{
|
||||||
|
// 异步加载数据,不阻塞主线程
|
||||||
|
var data = await LoadDataAsync();
|
||||||
|
this.SendEvent(new DataLoadedEvent { Data = data });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Data> LoadDataAsync()
|
||||||
|
{
|
||||||
|
return await Task.Run(() =>
|
||||||
|
{
|
||||||
|
// 耗时操作
|
||||||
|
return new Data();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
### 1. 单元测试
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[TestFixture]
|
||||||
|
public class CombatSystemTests
|
||||||
|
{
|
||||||
|
private GameArchitecture _architecture;
|
||||||
|
private PlayerModel _playerModel;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_architecture = new TestArchitecture();
|
||||||
|
_architecture.Initialize();
|
||||||
|
_playerModel = _architecture.GetModel<PlayerModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void Teardown()
|
||||||
|
{
|
||||||
|
_architecture.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerTakeDamage_ReducesHealth()
|
||||||
|
{
|
||||||
|
_playerModel.Health.Value = 100;
|
||||||
|
_architecture.SendEvent(new DamageEvent { Amount = 10 });
|
||||||
|
Assert.AreEqual(90, _playerModel.Health.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerDies_WhenHealthReachesZero()
|
||||||
|
{
|
||||||
|
_playerModel.Health.Value = 10;
|
||||||
|
_architecture.SendEvent(new DamageEvent { Amount = 10 });
|
||||||
|
Assert.AreEqual(0, _playerModel.Health.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 集成测试
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[TestFixture]
|
||||||
|
public class GameFlowTests
|
||||||
|
{
|
||||||
|
private GameArchitecture _architecture;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_architecture = new GameArchitecture();
|
||||||
|
_architecture.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CompleteGameFlow()
|
||||||
|
{
|
||||||
|
// 初始化
|
||||||
|
var player = _architecture.GetModel<PlayerModel>();
|
||||||
|
Assert.AreEqual(100, player.Health.Value);
|
||||||
|
|
||||||
|
// 执行操作
|
||||||
|
_architecture.SendCommand(new AttackCommand { Damage = 20 });
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
Assert.AreEqual(80, player.Health.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
### 1. 代码注释
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家模型,存储玩家的所有状态数据
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerModel : AbstractModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家的生命值,使用 BindableProperty 实现响应式更新
|
||||||
|
/// </summary>
|
||||||
|
public BindableProperty<int> Health { get; } = new(100);
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 监听生命值变化,当生命值为 0 时发送死亡事件
|
||||||
|
Health.Register(hp =>
|
||||||
|
{
|
||||||
|
if (hp <= 0)
|
||||||
|
this.SendEvent(new PlayerDiedEvent());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 架构文档
|
||||||
|
|
||||||
|
为你的项目编写架构文档,说明:
|
||||||
|
|
||||||
|
- 主要的 Model、System、Command、Query
|
||||||
|
- 关键事件流
|
||||||
|
- 组件间的通信方式
|
||||||
|
- 扩展点和插件机制
|
||||||
|
|
||||||
|
## 常见陷阱
|
||||||
|
|
||||||
|
### 1. 在 Model 中包含业务逻辑
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误
|
||||||
|
public class PlayerModel : AbstractModel
|
||||||
|
{
|
||||||
|
public void TakeDamage(int damage)
|
||||||
|
{
|
||||||
|
Health.Value -= damage;
|
||||||
|
if (Health.Value <= 0)
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 正确
|
||||||
|
public class CombatSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnDamage(DamageEvent e)
|
||||||
|
{
|
||||||
|
var player = this.GetModel<PlayerModel>();
|
||||||
|
player.Health.Value -= e.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 忘记注销监听器
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误:可能导致内存泄漏
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<Event1>(OnEvent1); // 未注销
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 正确
|
||||||
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<Event1>(OnEvent1)
|
||||||
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
_unregisterList.UnRegisterAll();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 直接调用其他系统
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误:紧耦合
|
||||||
|
public class SystemA : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnEvent(EventA e)
|
||||||
|
{
|
||||||
|
var systemB = this.GetSystem<SystemB>();
|
||||||
|
systemB.DoSomething();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 正确:使用事件解耦
|
||||||
|
public class SystemA : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnEvent(EventA e)
|
||||||
|
{
|
||||||
|
this.SendEvent(new EventB());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。
|
||||||
443
docs/zh-CN/core/async-initialization.md
Normal file
443
docs/zh-CN/core/async-initialization.md
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
# 异步初始化指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
异步初始化是 GFramework 中的一个重要特性,允许 Architecture、Model、System 等组件在初始化时执行异步操作。这对于需要加载大量资源、进行网络请求或其他耗时操作的场景特别有用。
|
||||||
|
|
||||||
|
## 核心接口
|
||||||
|
|
||||||
|
### IAsyncInitializable
|
||||||
|
|
||||||
|
异步初始化接口,定义了异步初始化的契约。
|
||||||
|
|
||||||
|
**核心方法:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Task InitializeAsync(); // 异步初始化方法
|
||||||
|
```
|
||||||
|
|
||||||
|
**实现者:**
|
||||||
|
|
||||||
|
- `Architecture` - 架构支持异步初始化
|
||||||
|
- `AbstractModel` - Model 可以实现异步初始化
|
||||||
|
- `AbstractSystem` - System 可以实现异步初始化
|
||||||
|
- `AbstractUtility` - Utility 可以实现异步初始化
|
||||||
|
|
||||||
|
## 异步 Architecture
|
||||||
|
|
||||||
|
### 基本使用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
RegisterSystem(new CombatSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步初始化架构
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
|
||||||
|
// 等待架构就绪
|
||||||
|
await architecture.WaitUntilReadyAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 初始化流程
|
||||||
|
|
||||||
|
异步初始化遵循以下流程:
|
||||||
|
|
||||||
|
1. 创建架构实例
|
||||||
|
2. 调用 `InitializeAsync()` 方法
|
||||||
|
3. 执行同步初始化(`Init()` 方法)
|
||||||
|
4. 按顺序异步初始化各个阶段:
|
||||||
|
- 工具异步初始化(BeforeUtilityInit → AfterUtilityInit)
|
||||||
|
- 模型异步初始化(BeforeModelInit → AfterModelInit)
|
||||||
|
- 系统异步初始化(BeforeSystemInit → AfterSystemInit)
|
||||||
|
5. 进入 Ready 阶段
|
||||||
|
6. 返回 Task 完成
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
### 定义异步 Model
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ConfigModel : AbstractModel, IAsyncInitializable
|
||||||
|
{
|
||||||
|
public BindableProperty<GameConfig> Config { get; } = new(null);
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
Console.WriteLine("ConfigModel sync init");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步加载配置
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
var config = await storage.LoadConfigAsync();
|
||||||
|
Config.Value = config;
|
||||||
|
|
||||||
|
Console.WriteLine("ConfigModel async init completed");
|
||||||
|
this.SendEvent(new ConfigLoadedEvent { Config = config });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlayerDataModel : AbstractModel, IAsyncInitializable
|
||||||
|
{
|
||||||
|
public BindableProperty<PlayerData> PlayerData { get; } = new(null);
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步加载玩家数据
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
var playerId = this.GetContext().Environment.Get<string>("PlayerId");
|
||||||
|
var playerData = await storage.LoadPlayerDataAsync(playerId);
|
||||||
|
PlayerData.Value = playerData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在架构中使用异步 Model
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
// 注册异步 Model
|
||||||
|
RegisterModel(new ConfigModel());
|
||||||
|
RegisterModel(new PlayerDataModel());
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
|
||||||
|
// 注册 System
|
||||||
|
RegisterSystem(new CombatSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 异步 System
|
||||||
|
|
||||||
|
### 定义异步 System
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class DataLoadSystem : AbstractSystem, IAsyncInitializable
|
||||||
|
{
|
||||||
|
private GameData _gameData;
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
this.RegisterEvent<GameStartedEvent>(OnGameStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步加载游戏数据
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
_gameData = await storage.LoadGameDataAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Game data loaded");
|
||||||
|
this.SendEvent(new GameDataLoadedEvent { Data = _gameData });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGameStarted(GameStartedEvent e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Game started with data version: {_gameData.Version}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResourceLoadSystem : AbstractSystem, IAsyncInitializable
|
||||||
|
{
|
||||||
|
private Dictionary<string, Resource> _resources = new();
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步加载资源
|
||||||
|
var resourceManager = this.GetUtility<IResourceManager>();
|
||||||
|
_resources = await resourceManager.LoadAllResourcesAsync();
|
||||||
|
|
||||||
|
Console.WriteLine($"Loaded {_resources.Count} resources");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 异步 Utility
|
||||||
|
|
||||||
|
### 定义异步 Utility
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class DatabaseUtility : IUtility, IAsyncInitializable
|
||||||
|
{
|
||||||
|
private Database _database;
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步连接数据库
|
||||||
|
_database = new Database();
|
||||||
|
await _database.ConnectAsync("connection_string");
|
||||||
|
|
||||||
|
Console.WriteLine("Database connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> QueryAsync<T>(string sql)
|
||||||
|
{
|
||||||
|
return await _database.QueryAsync<T>(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完整示例
|
||||||
|
|
||||||
|
### 场景:游戏启动流程
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 1. 定义各个异步组件
|
||||||
|
public class ConfigModel : AbstractModel, IAsyncInitializable
|
||||||
|
{
|
||||||
|
public BindableProperty<GameConfig> Config { get; } = new(null);
|
||||||
|
|
||||||
|
protected override void OnInit() { }
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
Config.Value = await storage.LoadConfigAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlayerModel : AbstractModel, IAsyncInitializable
|
||||||
|
{
|
||||||
|
public BindableProperty<PlayerData> PlayerData { get; } = new(null);
|
||||||
|
|
||||||
|
protected override void OnInit() { }
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
var playerId = this.GetContext().Environment.Get<string>("PlayerId");
|
||||||
|
PlayerData.Value = await storage.LoadPlayerDataAsync(playerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResourceLoadSystem : AbstractSystem, IAsyncInitializable
|
||||||
|
{
|
||||||
|
private Dictionary<string, Resource> _resources = new();
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<GameStartedEvent>(OnGameStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
var resourceManager = this.GetUtility<IResourceManager>();
|
||||||
|
_resources = await resourceManager.LoadAllResourcesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGameStarted(GameStartedEvent e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Game started with all resources loaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 定义架构
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterModel(new ConfigModel());
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
RegisterSystem(new ResourceLoadSystem());
|
||||||
|
RegisterSystem(new CombatSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 使用架构
|
||||||
|
public class GameBootstrapper
|
||||||
|
{
|
||||||
|
public async Task StartGameAsync()
|
||||||
|
{
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
|
||||||
|
// 异步初始化架构
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
|
||||||
|
// 等待架构就绪
|
||||||
|
await architecture.WaitUntilReadyAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Game is ready!");
|
||||||
|
|
||||||
|
// 发送游戏启动事件
|
||||||
|
architecture.SendEvent(new GameStartedEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 同步 vs 异步初始化
|
||||||
|
|
||||||
|
### 同步初始化
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
architecture.Initialize(); // 阻塞等待初始化完成
|
||||||
|
|
||||||
|
// 立即可以使用架构
|
||||||
|
var playerModel = architecture.GetModel<PlayerModel>();
|
||||||
|
```
|
||||||
|
|
||||||
|
**特点:**
|
||||||
|
|
||||||
|
- 阻塞式,等待初始化完成
|
||||||
|
- 适用于简单场景或控制台应用
|
||||||
|
- 初始化失败时抛出异常
|
||||||
|
|
||||||
|
### 异步初始化
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync(); // 非阻塞等待
|
||||||
|
|
||||||
|
// 初始化完成后可以使用架构
|
||||||
|
var playerModel = architecture.GetModel<PlayerModel>();
|
||||||
|
```
|
||||||
|
|
||||||
|
**特点:**
|
||||||
|
|
||||||
|
- 非阻塞式,不会阻塞主线程
|
||||||
|
- 支持异步组件初始化
|
||||||
|
- 适用于需要加载大量资源的场景
|
||||||
|
- 初始化失败时抛出异常
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 1. 合理划分同步和异步初始化
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class MyModel : AbstractModel, IAsyncInitializable
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化:快速的初始化逻辑
|
||||||
|
// - 注册事件监听
|
||||||
|
// - 初始化本地数据结构
|
||||||
|
// - 设置默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步初始化:耗时的初始化逻辑
|
||||||
|
// - 加载文件
|
||||||
|
// - 网络请求
|
||||||
|
// - 数据库查询
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 处理异步初始化异常
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public async Task StartGameAsync()
|
||||||
|
{
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Initialization failed: {ex.Message}");
|
||||||
|
// 处理初始化失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 显示加载进度
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ProgressTrackingArchitecture : Architecture
|
||||||
|
{
|
||||||
|
public event Action<float> OnProgressChanged;
|
||||||
|
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterModel(new ConfigModel());
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
RegisterSystem(new ResourceLoadSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsyncWithProgress()
|
||||||
|
{
|
||||||
|
OnProgressChanged?.Invoke(0.0f);
|
||||||
|
|
||||||
|
// 初始化各个阶段
|
||||||
|
await InitializeAsync();
|
||||||
|
|
||||||
|
OnProgressChanged?.Invoke(1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var architecture = new ProgressTrackingArchitecture();
|
||||||
|
architecture.OnProgressChanged += progress =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Loading: {progress * 100}%");
|
||||||
|
};
|
||||||
|
|
||||||
|
await architecture.InitializeAsyncWithProgress();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 超时控制
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public async Task StartGameWithTimeoutAsync()
|
||||||
|
{
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var initTask = architecture.InitializeAsync();
|
||||||
|
await initTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Initialization timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关包
|
||||||
|
|
||||||
|
- [`architecture`](./architecture.md) - 架构核心,支持异步初始化
|
||||||
|
- [`model`](./model.md) - Model 可以实现异步初始化
|
||||||
|
- [`system`](./system.md) - System 可以实现异步初始化
|
||||||
|
- [`utility`](./utility.md) - Utility 可以实现异步初始化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**许可证**:Apache 2.0
|
||||||
@ -44,31 +44,14 @@ protected abstract void OnExecute(); // 抽象执行方法,由子类实
|
|||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 定义一个开始游戏的命令
|
// 定义一个无返回值的基础命令
|
||||||
public class StartGameCommand : AbstractCommand
|
public class SimpleCommand : AbstractCommand
|
||||||
{
|
{
|
||||||
private readonly int _levelId;
|
|
||||||
private readonly string _playerName;
|
|
||||||
|
|
||||||
public StartGameCommand(int levelId, string playerName)
|
|
||||||
{
|
|
||||||
_levelId = levelId;
|
|
||||||
_playerName = playerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnExecute()
|
protected override void OnExecute()
|
||||||
{
|
{
|
||||||
// 获取需要的模型
|
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
var gameModel = this.GetModel<GameModel>();
|
playerModel.Health.Value = playerModel.MaxHealth.Value;
|
||||||
|
this.SendEvent(new PlayerHealthRestoredEvent());
|
||||||
// 执行业务逻辑
|
|
||||||
playerModel.PlayerName.Value = _playerName;
|
|
||||||
gameModel.CurrentLevel.Value = _levelId;
|
|
||||||
gameModel.GameState.Value = GameState.Playing;
|
|
||||||
|
|
||||||
// 发送事件通知其他模块
|
|
||||||
this.SendEvent(new GameStartedEvent());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,18 +59,17 @@ public class StartGameCommand : AbstractCommand
|
|||||||
public class GameController : IController
|
public class GameController : IController
|
||||||
{
|
{
|
||||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||||
|
|
||||||
public void OnStartButtonClicked()
|
public void OnRestoreHealthButtonClicked()
|
||||||
{
|
{
|
||||||
// 发送命令实例
|
this.SendCommand(new SimpleCommand());
|
||||||
this.SendCommand(new StartGameCommand(1, "Player1"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### AbstractCommandWithResult`<TResult>`
|
### AbstractCommand`<TResult>`
|
||||||
|
|
||||||
带返回值命令的抽象基类,同样继承自 ContextAwareBase。
|
无输入参数但带返回值的命令基类。
|
||||||
|
|
||||||
**核心方法:**
|
**核心方法:**
|
||||||
|
|
||||||
@ -99,49 +81,28 @@ protected abstract TResult OnExecute(); // 抽象执行方法,由子类实
|
|||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 定义一个计算伤害的命令
|
// 定义一个无输入但有返回值的命令
|
||||||
public class CalculateDamageCommand : AbstractCommandWithResult<int>
|
public class GetPlayerHealthQuery : AbstractCommand<int>
|
||||||
{
|
{
|
||||||
private readonly int _attackerAttackPower;
|
|
||||||
private readonly int _defenderDefense;
|
|
||||||
|
|
||||||
public CalculateDamageCommand(int attackerAttackPower, int defenderDefense)
|
|
||||||
{
|
|
||||||
_attackerAttackPower = attackerAttackPower;
|
|
||||||
_defenderDefense = defenderDefense;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override int OnExecute()
|
protected override int OnExecute()
|
||||||
{
|
{
|
||||||
// 获取游戏配置
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
var config = this.GetModel<GameConfigModel>();
|
return playerModel.Health.Value;
|
||||||
|
|
||||||
// 计算最终伤害
|
|
||||||
var baseDamage = _attackerAttackPower - _defenderDefense;
|
|
||||||
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
|
|
||||||
|
|
||||||
return (int)finalDamage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用带返回值的命令
|
// 使用命令
|
||||||
public class CombatSystem : AbstractSystem
|
public class UISystem : AbstractSystem
|
||||||
{
|
{
|
||||||
protected override void OnInit() { }
|
protected override void OnInit()
|
||||||
|
|
||||||
public void Attack(Character attacker, Character defender)
|
|
||||||
{
|
{
|
||||||
// 发送命令并获取返回值
|
this.RegisterEvent<UpdateUIEvent>(OnUpdateUI);
|
||||||
var damage = this.SendCommand(new CalculateDamageCommand(
|
}
|
||||||
attacker.AttackPower,
|
|
||||||
defender.Defense
|
private void OnUpdateUI(UpdateUIEvent e)
|
||||||
));
|
{
|
||||||
|
var health = this.SendCommand(new GetPlayerHealthQuery());
|
||||||
// 应用伤害
|
Console.WriteLine($"Player health: {health}");
|
||||||
defender.Health -= damage;
|
|
||||||
|
|
||||||
// 发送伤害事件
|
|
||||||
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -195,45 +156,167 @@ var damage = commandBus.Send(new CalculateDamageCommand(100, 50));
|
|||||||
|
|
||||||
框架提供了多种命令基类以满足不同需求:
|
框架提供了多种命令基类以满足不同需求:
|
||||||
|
|
||||||
### AbstractCommand
|
### AbstractCommand`<TInput>`
|
||||||
|
|
||||||
最基础的无返回值命令类
|
带输入参数的无返回值命令类。通过 `ICommandInput` 接口传递参数。
|
||||||
|
|
||||||
### AbstractCommandWithResult`<TResult>`
|
**核心方法:**
|
||||||
|
|
||||||
带返回值的命令基类
|
|
||||||
|
|
||||||
### AbstractCommandWithInput`<TInput>`
|
|
||||||
|
|
||||||
带输入参数的无返回值命令类
|
|
||||||
|
|
||||||
### AbstractCommandWithInputAndResult`<TInput, TResult>`
|
|
||||||
|
|
||||||
既带输入参数又带返回值的命令类
|
|
||||||
|
|
||||||
### AbstractAsyncCommand
|
|
||||||
|
|
||||||
支持异步执行的命令基类
|
|
||||||
|
|
||||||
### AbstractAsyncCommandWithResult`<TResult>`
|
|
||||||
|
|
||||||
支持异步执行的带返回值命令基类
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 异步命令示例
|
void ICommand.Execute(); // 实现 ICommand 接口
|
||||||
public class LoadSaveDataCommand : AbstractAsyncCommandWithResult<SaveData>
|
protected abstract void OnExecute(TInput input); // 抽象执行方法,接收输入参数
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 定义输入对象
|
||||||
|
public class StartGameInput : ICommandInput
|
||||||
{
|
{
|
||||||
private readonly string _saveSlot;
|
public int LevelId { get; set; }
|
||||||
|
public string PlayerName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public LoadSaveDataCommand(string saveSlot)
|
// 定义命令
|
||||||
|
public class StartGameCommand : AbstractCommand<StartGameInput>
|
||||||
|
{
|
||||||
|
protected override void OnExecute(StartGameInput input)
|
||||||
{
|
{
|
||||||
_saveSlot = saveSlot;
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
}
|
var gameModel = this.GetModel<GameModel>();
|
||||||
|
|
||||||
protected override async Task<SaveData> OnExecuteAsync()
|
playerModel.PlayerName.Value = input.PlayerName;
|
||||||
|
gameModel.CurrentLevel.Value = input.LevelId;
|
||||||
|
gameModel.GameState.Value = GameState.Playing;
|
||||||
|
|
||||||
|
this.SendEvent(new GameStartedEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用命令
|
||||||
|
public class GameController : IController
|
||||||
|
{
|
||||||
|
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||||
|
|
||||||
|
public void OnStartButtonClicked()
|
||||||
|
{
|
||||||
|
var input = new StartGameInput { LevelId = 1, PlayerName = "Player1" };
|
||||||
|
this.SendCommand(new StartGameCommand { Input = input });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractCommand`<TInput, TResult>`
|
||||||
|
|
||||||
|
既带输入参数又带返回值的命令类。
|
||||||
|
|
||||||
|
**核心方法:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
TResult ICommand<TResult>.Execute(); // 实现 ICommand<TResult> 接口
|
||||||
|
protected abstract TResult OnExecute(TInput input); // 抽象执行方法,接收输入参数
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 定义输入对象
|
||||||
|
public class CalculateDamageInput : ICommandInput
|
||||||
|
{
|
||||||
|
public int AttackerAttackPower { get; set; }
|
||||||
|
public int DefenderDefense { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义命令
|
||||||
|
public class CalculateDamageCommand : AbstractCommand<CalculateDamageInput, int>
|
||||||
|
{
|
||||||
|
protected override int OnExecute(CalculateDamageInput input)
|
||||||
|
{
|
||||||
|
var config = this.GetModel<GameConfigModel>();
|
||||||
|
var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
|
||||||
|
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
|
||||||
|
return (int)finalDamage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用命令
|
||||||
|
public class CombatSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit() { }
|
||||||
|
|
||||||
|
public void Attack(Character attacker, Character defender)
|
||||||
|
{
|
||||||
|
var input = new CalculateDamageInput
|
||||||
|
{
|
||||||
|
AttackerAttackPower = attacker.AttackPower,
|
||||||
|
DefenderDefense = defender.Defense
|
||||||
|
};
|
||||||
|
|
||||||
|
var damage = this.SendCommand(new CalculateDamageCommand { Input = input });
|
||||||
|
defender.Health -= damage;
|
||||||
|
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractAsyncCommand`<TInput>`
|
||||||
|
|
||||||
|
支持异步执行的带输入参数的无返回值命令基类。
|
||||||
|
|
||||||
|
**核心方法:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口
|
||||||
|
protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法
|
||||||
|
```
|
||||||
|
|
||||||
|
### AbstractAsyncCommand`<TInput, TResult>`
|
||||||
|
|
||||||
|
支持异步执行的既带输入参数又带返回值的命令基类。
|
||||||
|
|
||||||
|
**核心方法:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Task<TResult> IAsyncCommand<TResult>.ExecuteAsync(); // 实现异步命令接口
|
||||||
|
protected abstract Task<TResult> OnExecuteAsync(TInput input); // 抽象异步执行方法
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 定义输入对象
|
||||||
|
public class LoadSaveDataInput : ICommandInput
|
||||||
|
{
|
||||||
|
public string SaveSlot { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义异步命令
|
||||||
|
public class LoadSaveDataCommand : AbstractAsyncCommand<LoadSaveDataInput, SaveData>
|
||||||
|
{
|
||||||
|
protected override async Task<SaveData> OnExecuteAsync(LoadSaveDataInput input)
|
||||||
{
|
{
|
||||||
var storage = this.GetUtility<IStorageUtility>();
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
return await storage.LoadSaveDataAsync(_saveSlot);
|
return await storage.LoadSaveDataAsync(input.SaveSlot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用异步命令
|
||||||
|
public class SaveSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<LoadGameRequestEvent>(OnLoadGameRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnLoadGameRequest(LoadGameRequestEvent e)
|
||||||
|
{
|
||||||
|
var input = new LoadSaveDataInput { SaveSlot = e.SaveSlot };
|
||||||
|
var saveData = await this.SendCommandAsync(new LoadSaveDataCommand { Input = input });
|
||||||
|
|
||||||
|
if (saveData != null)
|
||||||
|
{
|
||||||
|
this.SendEvent(new GameLoadedEvent { SaveData = saveData });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
490
docs/zh-CN/core/context.md
Normal file
490
docs/zh-CN/core/context.md
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
# Context 上下文指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Context(上下文)是 GFramework 中的核心概念,提供了对架构服务的统一访问入口。通过 Context,组件可以访问事件总线、命令总线、查询总线、IoC
|
||||||
|
容器等核心服务。
|
||||||
|
|
||||||
|
## 核心接口
|
||||||
|
|
||||||
|
### IArchitectureContext
|
||||||
|
|
||||||
|
架构上下文接口,定义了对架构服务的访问契约。
|
||||||
|
|
||||||
|
**核心属性:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
IEventBus EventBus { get; } // 事件总线
|
||||||
|
ICommandBus CommandBus { get; } // 命令总线
|
||||||
|
IQueryBus QueryBus { get; } // 查询总线
|
||||||
|
IIocContainer Container { get; } // IoC 容器
|
||||||
|
IEnvironment Environment { get; } // 环境配置
|
||||||
|
IArchitectureConfiguration Configuration { get; } // 架构配置
|
||||||
|
ILogger Logger { get; } // 日志系统
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心类
|
||||||
|
|
||||||
|
### ArchitectureContext
|
||||||
|
|
||||||
|
架构上下文的完整实现。
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 通过架构获取上下文
|
||||||
|
var context = architecture.Context;
|
||||||
|
|
||||||
|
// 访问各个服务
|
||||||
|
var eventBus = context.EventBus;
|
||||||
|
var commandBus = context.CommandBus;
|
||||||
|
var queryBus = context.QueryBus;
|
||||||
|
var container = context.Container;
|
||||||
|
var environment = context.Environment;
|
||||||
|
var logger = context.Logger;
|
||||||
|
```
|
||||||
|
|
||||||
|
### GameContext
|
||||||
|
|
||||||
|
游戏上下文类,管理架构类型与上下文实例的映射关系。
|
||||||
|
|
||||||
|
**核心方法:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 绑定架构类型到上下文
|
||||||
|
static void Bind<TArchitecture>(IArchitectureContext context)
|
||||||
|
where TArchitecture : IArchitecture;
|
||||||
|
|
||||||
|
// 获取架构类型对应的上下文
|
||||||
|
static IArchitectureContext GetContext<TArchitecture>()
|
||||||
|
where TArchitecture : IArchitecture;
|
||||||
|
|
||||||
|
// 解绑架构类型
|
||||||
|
static void Unbind<TArchitecture>()
|
||||||
|
where TArchitecture : IArchitecture;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 在组件中使用 Context
|
||||||
|
|
||||||
|
### 在 Model 中使用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PlayerModel : AbstractModel
|
||||||
|
{
|
||||||
|
public BindableProperty<int> Health { get; } = new(100);
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 通过 Context 访问事件总线
|
||||||
|
var context = this.GetContext();
|
||||||
|
var eventBus = context.EventBus;
|
||||||
|
|
||||||
|
// 监听生命值变化
|
||||||
|
Health.Register(hp =>
|
||||||
|
{
|
||||||
|
if (hp <= 0)
|
||||||
|
{
|
||||||
|
// 发送事件
|
||||||
|
eventBus.Send(new PlayerDiedEvent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在 System 中使用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class CombatSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 通过 Context 访问各个服务
|
||||||
|
var context = this.GetContext();
|
||||||
|
var eventBus = context.EventBus;
|
||||||
|
var commandBus = context.CommandBus;
|
||||||
|
var container = context.Container;
|
||||||
|
|
||||||
|
// 注册事件监听
|
||||||
|
eventBus.Register<EnemyAttackEvent>(OnEnemyAttack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnemyAttack(EnemyAttackEvent e)
|
||||||
|
{
|
||||||
|
var context = this.GetContext();
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
|
||||||
|
// 处理伤害
|
||||||
|
playerModel.Health.Value -= e.Damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在 Command 中使用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class StartGameCommand : AbstractCommand
|
||||||
|
{
|
||||||
|
protected override void OnExecute()
|
||||||
|
{
|
||||||
|
// 通过 Context 访问服务
|
||||||
|
var context = this.GetContext();
|
||||||
|
var container = context.Container;
|
||||||
|
var eventBus = context.EventBus;
|
||||||
|
|
||||||
|
var playerModel = container.Get<PlayerModel>();
|
||||||
|
playerModel.Health.Value = playerModel.MaxHealth.Value;
|
||||||
|
|
||||||
|
eventBus.Send(new GameStartedEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在 Query 中使用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GetPlayerHealthQuery : AbstractQuery<int>
|
||||||
|
{
|
||||||
|
protected override int OnDo()
|
||||||
|
{
|
||||||
|
// 通过 Context 访问容器
|
||||||
|
var context = this.GetContext();
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
|
||||||
|
return playerModel.Health.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## GameContext 的使用
|
||||||
|
|
||||||
|
### 绑定架构到 GameContext
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
// 注册组件
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
RegisterSystem(new CombatSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在应用启动时绑定
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
|
||||||
|
// 绑定架构到 GameContext
|
||||||
|
GameContext.Bind<GameArchitecture>(architecture.Context);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 从 GameContext 获取上下文
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 在任何地方获取架构上下文
|
||||||
|
var context = GameContext.GetContext<GameArchitecture>();
|
||||||
|
|
||||||
|
// 访问服务
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
var eventBus = context.EventBus;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 GameContext 的扩展方法
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 通过扩展方法简化访问
|
||||||
|
public static class GameContextExtensions
|
||||||
|
{
|
||||||
|
public static T GetModel<T>(this IArchitectureContext context)
|
||||||
|
where T : class, IModel
|
||||||
|
{
|
||||||
|
return context.Container.Get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetSystem<T>(this IArchitectureContext context)
|
||||||
|
where T : class, ISystem
|
||||||
|
{
|
||||||
|
return context.Container.Get<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var context = GameContext.GetContext<GameArchitecture>();
|
||||||
|
var playerModel = context.GetModel<PlayerModel>();
|
||||||
|
var combatSystem = context.GetSystem<CombatSystem>();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context 中的服务
|
||||||
|
|
||||||
|
### EventBus - 事件总线
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var context = architecture.Context;
|
||||||
|
var eventBus = context.EventBus;
|
||||||
|
|
||||||
|
// 注册事件
|
||||||
|
eventBus.Register<PlayerDiedEvent>(e =>
|
||||||
|
{
|
||||||
|
Console.WriteLine("Player died!");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
eventBus.Send(new PlayerDiedEvent());
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommandBus - 命令总线
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var context = architecture.Context;
|
||||||
|
var commandBus = context.CommandBus;
|
||||||
|
|
||||||
|
// 发送命令
|
||||||
|
commandBus.Send(new StartGameCommand());
|
||||||
|
|
||||||
|
// 发送带返回值的命令
|
||||||
|
var damage = commandBus.Send(new CalculateDamageCommand { Input = input });
|
||||||
|
```
|
||||||
|
|
||||||
|
### QueryBus - 查询总线
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var context = architecture.Context;
|
||||||
|
var queryBus = context.QueryBus;
|
||||||
|
|
||||||
|
// 发送查询
|
||||||
|
var health = queryBus.Send(new GetPlayerHealthQuery { Input = new EmptyQueryInput() });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container - IoC 容器
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var context = architecture.Context;
|
||||||
|
var container = context.Container;
|
||||||
|
|
||||||
|
// 获取已注册的组件
|
||||||
|
var playerModel = container.Get<PlayerModel>();
|
||||||
|
var combatSystem = container.Get<CombatSystem>();
|
||||||
|
|
||||||
|
// 获取所有实现某接口的组件
|
||||||
|
var allSystems = container.GetAll<ISystem>();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment - 环境配置
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var context = architecture.Context;
|
||||||
|
var environment = context.Environment;
|
||||||
|
|
||||||
|
// 获取环境值
|
||||||
|
var gameMode = environment.Get<string>("GameMode");
|
||||||
|
var maxPlayers = environment.Get<int>("MaxPlayers");
|
||||||
|
|
||||||
|
// 安全获取值
|
||||||
|
if (environment.TryGet<string>("ServerAddress", out var address))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Server: {address}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logger - 日志系统
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var context = architecture.Context;
|
||||||
|
var logger = context.Logger;
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
logger.Log("Game started");
|
||||||
|
logger.LogWarning("Low memory");
|
||||||
|
logger.LogError("Failed to load resource");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context 的生命周期
|
||||||
|
|
||||||
|
### 创建
|
||||||
|
|
||||||
|
Context 在架构初始化时自动创建:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
// Context 在这里被创建
|
||||||
|
var context = architecture.Context;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
|
||||||
|
Context 在架构的整个生命周期中可用:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 初始化期间
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
|
||||||
|
// Ready 阶段
|
||||||
|
var context = architecture.Context;
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
|
||||||
|
// 销毁前
|
||||||
|
architecture.Destroy();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 销毁
|
||||||
|
|
||||||
|
Context 随着架构的销毁而销毁:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
architecture.Destroy();
|
||||||
|
// Context 不再可用
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 1. 通过扩展方法简化访问
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public static class ContextExtensions
|
||||||
|
{
|
||||||
|
public static T GetModel<T>(this IArchitectureContext context)
|
||||||
|
where T : class, IModel
|
||||||
|
{
|
||||||
|
return context.Container.Get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetSystem<T>(this IArchitectureContext context)
|
||||||
|
where T : class, ISystem
|
||||||
|
{
|
||||||
|
return context.Container.Get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendCommand(this IArchitectureContext context, ICommand command)
|
||||||
|
{
|
||||||
|
context.CommandBus.Send(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TResult SendQuery<TResult>(this IArchitectureContext context, IQuery<TResult> query)
|
||||||
|
{
|
||||||
|
return context.QueryBus.Send(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var context = architecture.Context;
|
||||||
|
var playerModel = context.GetModel<PlayerModel>();
|
||||||
|
context.SendCommand(new StartGameCommand());
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 缓存 Context 引用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class GameSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
private IArchitectureContext _context;
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 缓存 Context 引用
|
||||||
|
_context = this.GetContext();
|
||||||
|
|
||||||
|
// 后续使用缓存的引用
|
||||||
|
_context.EventBus.Register<GameStartedEvent>(OnGameStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGameStarted(GameStartedEvent e)
|
||||||
|
{
|
||||||
|
var playerModel = _context.Container.Get<PlayerModel>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 使用 GameContext 实现全局访问
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 在应用启动时绑定
|
||||||
|
public class GameBootstrapper
|
||||||
|
{
|
||||||
|
public async Task StartAsync()
|
||||||
|
{
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
|
||||||
|
// 绑定到 GameContext
|
||||||
|
GameContext.Bind<GameArchitecture>(architecture.Context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在任何地方访问
|
||||||
|
public class UIController
|
||||||
|
{
|
||||||
|
public void UpdateHealthDisplay()
|
||||||
|
{
|
||||||
|
var context = GameContext.GetContext<GameArchitecture>();
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
|
||||||
|
// 更新 UI
|
||||||
|
healthText.text = playerModel.Health.Value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 处理 Context 不可用的情况
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class SafeGameSystem : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var context = this.GetContext();
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Context not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error accessing context: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context vs Architecture
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
- **职责**:管理组件的生命周期
|
||||||
|
- **作用**:注册、初始化、销毁组件
|
||||||
|
- **访问**:通过 `GetArchitecture()` 获取
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
- **职责**:提供对架构服务的访问
|
||||||
|
- **作用**:访问事件总线、命令总线、查询总线等
|
||||||
|
- **访问**:通过 `GetContext()` 获取
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Architecture 用于管理
|
||||||
|
var architecture = GameArchitecture.Interface;
|
||||||
|
architecture.RegisterModel(new PlayerModel());
|
||||||
|
|
||||||
|
// Context 用于访问服务
|
||||||
|
var context = architecture.Context;
|
||||||
|
var playerModel = context.Container.Get<PlayerModel>();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关包
|
||||||
|
|
||||||
|
- [`architecture`](./architecture.md) - 架构核心,创建和管理 Context
|
||||||
|
- [`ioc`](./ioc.md) - IoC 容器,通过 Context 访问
|
||||||
|
- [`events`](./events.md) - 事件总线,通过 Context 访问
|
||||||
|
- [`command`](./command.md) - 命令总线,通过 Context 访问
|
||||||
|
- [`query`](./query.md) - 查询总线,通过 Context 访问
|
||||||
|
- [`environment`](./environment.md) - 环境配置,通过 Context 访问
|
||||||
|
- [`logging`](./logging.md) - 日志系统,通过 Context 访问
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**许可证**:Apache 2.0
|
||||||
@ -1,413 +0,0 @@
|
|||||||
# Controller 包使用说明
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
Controller 包定义了控制器(Controller)的接口规范。控制器是 MVC 架构中的 C 层,负责处理用户交互、协调视图和模型,是连接表现层和业务层的桥梁。
|
|
||||||
|
|
||||||
**注意**:本框架使用依赖注入模式,Controller 通过构造函数或属性注入获取架构实例,而非使用全局单例。
|
|
||||||
|
|
||||||
## 核心接口
|
|
||||||
|
|
||||||
### [`IController`](./controller.md)
|
|
||||||
|
|
||||||
控制器接口,定义了控制器需要实现的所有功能契约。
|
|
||||||
|
|
||||||
**继承的能力接口:**
|
|
||||||
|
|
||||||
- [`ICanSendCommand`](./command.md) - 可发送命令
|
|
||||||
- [`ICanGetSystem`](./system.md) - 可获取系统
|
|
||||||
- [`ICanGetModel`](./model.md) - 可获取模型
|
|
||||||
- [`ICanRegisterEvent`](./events.md) - 可注册事件
|
|
||||||
- [`ICanSendQuery`](./query.md) - 可发送查询
|
|
||||||
- [`ICanGetUtility`](./utility.md) - 可获取工具
|
|
||||||
|
|
||||||
**能力说明:**
|
|
||||||
|
|
||||||
控制器拥有框架中最全面的能力集合,可以:
|
|
||||||
|
|
||||||
1. 发送命令执行业务逻辑
|
|
||||||
2. 获取系统调用服务
|
|
||||||
3. 获取模型读写数据
|
|
||||||
4. 注册事件监听变化
|
|
||||||
5. 发送查询获取信息
|
|
||||||
6. 获取工具使用辅助功能
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 基础控制器实现(依赖注入模式)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using GFramework.Core.architecture;
|
|
||||||
|
|
||||||
// 通过依赖注入获取架构
|
|
||||||
public class PlayerController : IController
|
|
||||||
{
|
|
||||||
private readonly IArchitecture _architecture;
|
|
||||||
private readonly IUnRegisterList _unregisterList = new UnRegisterList();
|
|
||||||
|
|
||||||
// 通过构造函数注入架构
|
|
||||||
public PlayerController(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
_architecture = architecture;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
// 获取模型
|
|
||||||
var playerModel = _architecture.GetModel<PlayerModel>();
|
|
||||||
|
|
||||||
// 监听模型变化
|
|
||||||
playerModel.Health.RegisterWithInitValue(OnHealthChanged)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
|
|
||||||
// 注册事件
|
|
||||||
_architecture.RegisterEvent<PlayerLevelUpEvent>(OnPlayerLevelUp)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理用户输入
|
|
||||||
public void ProcessInput(double delta)
|
|
||||||
{
|
|
||||||
if (Input.IsActionJustPressed("attack"))
|
|
||||||
{
|
|
||||||
// 发送命令
|
|
||||||
_architecture.SendCommand(new AttackCommand());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Input.IsActionJustPressed("use_item"))
|
|
||||||
{
|
|
||||||
// 发送查询
|
|
||||||
var inventory = _architecture.SendQuery(new GetInventoryQuery());
|
|
||||||
if (inventory.HasItem("potion"))
|
|
||||||
{
|
|
||||||
_architecture.SendCommand(new UseItemCommand("potion"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHealthChanged(int newHealth)
|
|
||||||
{
|
|
||||||
// 更新 UI 显示
|
|
||||||
UpdateHealthBar(newHealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPlayerLevelUp(PlayerLevelUpEvent e)
|
|
||||||
{
|
|
||||||
// 显示升级特效
|
|
||||||
ShowLevelUpEffect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cleanup()
|
|
||||||
{
|
|
||||||
// 清理事件注册
|
|
||||||
_unregisterList.UnRegisterAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateHealthBar(int health) { /* UI 更新逻辑 */ }
|
|
||||||
private void ShowLevelUpEffect() { /* 特效逻辑 */ }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### UI 控制器示例
|
|
||||||
|
|
||||||
``csharp
|
|
||||||
// UI 面板控制器
|
|
||||||
public class MainMenuController : IController
|
|
||||||
{
|
|
||||||
[Inject] private IArchitecture _architecture;
|
|
||||||
[Inject] private IUISystem _uiSystem;
|
|
||||||
|
|
||||||
[Export] private Button _startButton;
|
|
||||||
[Export] private Button _settingsButton;
|
|
||||||
[Export] private Button _quitButton;
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
// 绑定按钮事件
|
|
||||||
_startButton.Pressed += OnStartButtonPressed;
|
|
||||||
_settingsButton.Pressed += OnSettingsButtonPressed;
|
|
||||||
_quitButton.Pressed += OnQuitButtonPressed;
|
|
||||||
|
|
||||||
// 获取模型更新 UI
|
|
||||||
var gameModel = _architecture.GetModel<GameModel>();
|
|
||||||
UpdateUI(gameModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStartButtonPressed()
|
|
||||||
{
|
|
||||||
// 通过命令启动游戏
|
|
||||||
_architecture.SendCommand<StartGameCommand>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSettingsButtonPressed()
|
|
||||||
{
|
|
||||||
// 查询当前设置
|
|
||||||
var settings = _architecture.SendQuery(new GetSettingsQuery());
|
|
||||||
|
|
||||||
// 打开设置面板
|
|
||||||
_uiSystem.OpenSettingsPanel(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnQuitButtonPressed()
|
|
||||||
{
|
|
||||||
// 发送退出命令
|
|
||||||
_architecture.SendCommand<QuitGameCommand>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateUI(GameModel model) { /* UI 更新逻辑 */ }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 复杂交互控制器
|
|
||||||
|
|
||||||
``csharp
|
|
||||||
// 战斗控制器
|
|
||||||
public class CombatController : IController
|
|
||||||
{
|
|
||||||
[Inject] protected IArchitecture _architecture;
|
|
||||||
|
|
||||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
|
||||||
private PlayerModel _playerModel;
|
|
||||||
private CombatSystem _combatSystem;
|
|
||||||
|
|
||||||
[PostConstruct]
|
|
||||||
public void Init()
|
|
||||||
{
|
|
||||||
// 缓存常用引用
|
|
||||||
_playerModel = _architecture.GetModel<PlayerModel>();
|
|
||||||
_combatSystem = _architecture.GetSystem<CombatSystem>();
|
|
||||||
|
|
||||||
// 注册多个事件
|
|
||||||
_architecture.RegisterEvent<EnemySpawnedEvent>(OnEnemySpawned)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
|
|
||||||
_architecture.RegisterEvent<CombatEndedEvent>(OnCombatEnded)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
|
|
||||||
// 监听模型状态
|
|
||||||
_playerModel.CombatState.Register(OnCombatStateChanged)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnemySpawned(EnemySpawnedEvent e)
|
|
||||||
{
|
|
||||||
// 进入战斗状态
|
|
||||||
_architecture.SendCommand(new EnterCombatCommand(e.Enemy));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCombatEnded(CombatEndedEvent e)
|
|
||||||
{
|
|
||||||
if (e.Victory)
|
|
||||||
{
|
|
||||||
// 查询奖励
|
|
||||||
var rewards = _architecture.SendQuery(new CalculateRewardsQuery(e.Enemy));
|
|
||||||
|
|
||||||
// 发放奖励
|
|
||||||
_architecture.SendCommand(new GiveRewardsCommand(rewards));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 处理失败
|
|
||||||
_architecture.SendCommand<GameOverCommand>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCombatStateChanged(CombatState state)
|
|
||||||
{
|
|
||||||
// 根据战斗状态更新 UI
|
|
||||||
_architecture.GetSystem<UISystem>().UpdateCombatUI(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cleanup()
|
|
||||||
{
|
|
||||||
_unregisterList.UnRegisterAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 控制器职责
|
|
||||||
|
|
||||||
### ✅ 应该做的事
|
|
||||||
|
|
||||||
1. **处理用户输入**
|
|
||||||
- 键盘、鼠标、触摸输入
|
|
||||||
- UI 按钮点击
|
|
||||||
- 手势识别
|
|
||||||
|
|
||||||
2. **协调视图和模型**
|
|
||||||
- 监听模型变化更新视图
|
|
||||||
- 将用户操作转换为命令
|
|
||||||
|
|
||||||
3. **管理界面逻辑**
|
|
||||||
- UI 元素的显示/隐藏
|
|
||||||
- 动画播放控制
|
|
||||||
- 视觉反馈
|
|
||||||
|
|
||||||
4. **事件监听**
|
|
||||||
- 注册关心的事件
|
|
||||||
- 响应事件更新界面
|
|
||||||
|
|
||||||
### ❌ 不应该做的事
|
|
||||||
|
|
||||||
1. **不包含业务逻辑**
|
|
||||||
- 业务逻辑应该在 System 中
|
|
||||||
- 控制器只负责调用和协调
|
|
||||||
|
|
||||||
2. **不直接修改模型**
|
|
||||||
- 应该通过 Command 修改模型
|
|
||||||
- 避免直接访问 Model 的 setter
|
|
||||||
|
|
||||||
3. **不处理复杂计算**
|
|
||||||
- 复杂计算应该在 Query 或 System 中
|
|
||||||
- 控制器保持简洁
|
|
||||||
|
|
||||||
4. **不保存核心状态**
|
|
||||||
- 核心状态应该在 Model 中
|
|
||||||
- 控制器可以保存临时 UI 状态
|
|
||||||
|
|
||||||
## 生命周期管理
|
|
||||||
|
|
||||||
### 事件注销
|
|
||||||
|
|
||||||
``csharp
|
|
||||||
public class MyController : IController
|
|
||||||
{
|
|
||||||
[Inject] private IArchitecture _architecture;
|
|
||||||
|
|
||||||
// 使用 UnRegisterList 统一管理
|
|
||||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
// 所有事件注册都添加到列表
|
|
||||||
_architecture.RegisterEvent<GameEvent>(OnGameEvent)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
|
|
||||||
_architecture.GetModel<PlayerModel>().Health.Register(OnHealthChanged)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cleanup()
|
|
||||||
{
|
|
||||||
// 统一注销所有事件
|
|
||||||
_unregisterList.UnRegisterAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 最佳实践
|
|
||||||
|
|
||||||
1. **使用依赖注入获取依赖**
|
|
||||||
- 通过构造函数注入 `IArchitecture`
|
|
||||||
- 使用 `[Inject]` 属性标记注入字段
|
|
||||||
|
|
||||||
2. **保持控制器轻量**
|
|
||||||
- 复杂逻辑放在 Command、Query、System 中
|
|
||||||
- 控制器只做协调和转发
|
|
||||||
|
|
||||||
3. **合理使用缓存**
|
|
||||||
- 频繁使用的 Model、System 可以缓存引用
|
|
||||||
- 平衡性能和内存占用
|
|
||||||
|
|
||||||
4. **统一管理事件注销**
|
|
||||||
- 使用 `IUnRegisterList` 统一管理
|
|
||||||
- 在 `Cleanup()` 中统一注销
|
|
||||||
|
|
||||||
5. **命名规范**
|
|
||||||
- 控制器类名:`XxxController`
|
|
||||||
- 使用 `[Inject]` 或构造函数注入获取架构
|
|
||||||
|
|
||||||
## 常见模式
|
|
||||||
|
|
||||||
### 数据绑定模式
|
|
||||||
|
|
||||||
``csharp
|
|
||||||
public class ScoreController : IController
|
|
||||||
{
|
|
||||||
[Inject] private IArchitecture _architecture;
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
// 绑定模型数据到 UI
|
|
||||||
_architecture.GetModel<GameModel>()
|
|
||||||
.Score
|
|
||||||
.RegisterWithInitValue(score => UpdateDisplay(score))
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDisplay(int score)
|
|
||||||
{
|
|
||||||
// 更新分数显示
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 状态机模式
|
|
||||||
|
|
||||||
``csharp
|
|
||||||
public class PlayerStateController : IController
|
|
||||||
{
|
|
||||||
[Inject] private IArchitecture _architecture;
|
|
||||||
private Dictionary<PlayerState, Action> _stateHandlers;
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
_stateHandlers = new Dictionary<PlayerState, Action>
|
|
||||||
{
|
|
||||||
{ PlayerState.Idle, HandleIdleState },
|
|
||||||
{ PlayerState.Moving, HandleMovingState },
|
|
||||||
{ PlayerState.Attacking, HandleAttackingState }
|
|
||||||
};
|
|
||||||
|
|
||||||
_architecture.GetModel<PlayerModel>()
|
|
||||||
.State
|
|
||||||
.Register(OnStateChanged)
|
|
||||||
.AddToUnregisterList(_unregisterList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStateChanged(PlayerState state)
|
|
||||||
{
|
|
||||||
_stateHandlers[state]?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 与 Godot 集成
|
|
||||||
|
|
||||||
在 Godot 项目中,可以使用 GFramework.Godot 提供的扩展:
|
|
||||||
|
|
||||||
``csharp
|
|
||||||
using GFramework.Godot;
|
|
||||||
|
|
||||||
public partial class GodotPlayerController : Node, IController
|
|
||||||
{
|
|
||||||
[Inject] private IArchitecture _architecture;
|
|
||||||
|
|
||||||
public override void _Ready()
|
|
||||||
{
|
|
||||||
// 使用 Godot 特定的自动注销扩展
|
|
||||||
_architecture.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
|
|
||||||
.UnRegisterWhenNodeExitTree(this);
|
|
||||||
|
|
||||||
_architecture.GetModel<PlayerModel>()
|
|
||||||
.Health
|
|
||||||
.RegisterWithInitValue(OnHealthChanged)
|
|
||||||
.UnRegisterWhenNodeExitTree(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 相关包
|
|
||||||
|
|
||||||
- [`architecture`](./architecture.md) - 提供架构访问能力
|
|
||||||
- [`command`](./command.md) - 控制器发送命令执行业务逻辑
|
|
||||||
- [`query`](./query.md) - 控制器发送查询获取数据
|
|
||||||
- [`events`](./events.md) - 控制器注册事件监听变化
|
|
||||||
- [`model`](./model.md) - 控制器读取模型数据
|
|
||||||
- [`system`](./system.md) - 控制器调用系统服务
|
|
||||||
- [`extensions`](./extensions.md) - 提供便捷的扩展方法
|
|
||||||
- **GFramework.Godot** - Godot 特定的控制器扩展
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**许可证**: Apache 2.0
|
|
||||||
@ -90,11 +90,11 @@ public class DefaultEnvironment : EnvironmentBase
|
|||||||
|
|
||||||
### 1. 定义自定义环境
|
### 1. 定义自定义环境
|
||||||
|
|
||||||
```
|
```csharp
|
||||||
public class GameEnvironment : EnvironmentBase
|
public class GameEnvironment : EnvironmentBase
|
||||||
{
|
{
|
||||||
public override string Name { get; } = "Game";
|
public override string Name { get; } = "Game";
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
// 注册一些环境特定的值
|
// 注册一些环境特定的值
|
||||||
@ -102,7 +102,7 @@ public class GameEnvironment : EnvironmentBase
|
|||||||
Register("MaxPlayers", 4);
|
Register("MaxPlayers", 4);
|
||||||
Register("ServerAddress", "localhost");
|
Register("ServerAddress", "localhost");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 便捷方法
|
// 便捷方法
|
||||||
public string GameMode => Get<string>("GameMode") ?? "Default";
|
public string GameMode => Get<string>("GameMode") ?? "Default";
|
||||||
public int MaxPlayers => Get<int>("MaxPlayers") ?? 1;
|
public int MaxPlayers => Get<int>("MaxPlayers") ?? 1;
|
||||||
@ -112,7 +112,7 @@ public class GameEnvironment : EnvironmentBase
|
|||||||
|
|
||||||
### 2. 在架构中使用环境
|
### 2. 在架构中使用环境
|
||||||
|
|
||||||
```
|
```csharp
|
||||||
public class GameArchitecture : Architecture
|
public class GameArchitecture : Architecture
|
||||||
{
|
{
|
||||||
protected override void Init()
|
protected override void Init()
|
||||||
@ -120,7 +120,7 @@ public class GameArchitecture : Architecture
|
|||||||
// 环境已经在架构初始化过程中自动初始化
|
// 环境已经在架构初始化过程中自动初始化
|
||||||
// 但是我们可以在需要的时候获取环境值
|
// 但是我们可以在需要的时候获取环境值
|
||||||
var gameMode = this.Context.Environment.Get<string>("GameMode");
|
var gameMode = this.Context.Environment.Get<string>("GameMode");
|
||||||
|
|
||||||
// 注册模型和系统
|
// 注册模型和系统
|
||||||
RegisterModel(new PlayerModel());
|
RegisterModel(new PlayerModel());
|
||||||
RegisterSystem(new GameplaySystem());
|
RegisterSystem(new GameplaySystem());
|
||||||
@ -130,26 +130,26 @@ public class GameArchitecture : Architecture
|
|||||||
|
|
||||||
### 3. 使用环境值
|
### 3. 使用环境值
|
||||||
|
|
||||||
```
|
```csharp
|
||||||
public class NetworkSystem : AbstractSystem
|
public class NetworkSystem : AbstractSystem
|
||||||
{
|
{
|
||||||
private string _serverAddress;
|
private string _serverAddress;
|
||||||
|
|
||||||
protected override void OnInit()
|
protected override void OnInit()
|
||||||
{
|
{
|
||||||
// 从环境中获取服务器地址
|
// 从环境中获取服务器地址
|
||||||
_serverAddress = this.GetContext().Environment.Get<string>("ServerAddress") ?? "localhost";
|
_serverAddress = this.GetContext().Environment.Get<string>("ServerAddress") ?? "localhost";
|
||||||
|
|
||||||
// 注册事件
|
// 注册事件
|
||||||
this.RegisterEvent<ConnectToServerEvent>(OnConnectToServer);
|
this.RegisterEvent<ConnectToServerEvent>(OnConnectToServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnectToServer(ConnectToServerEvent e)
|
private void OnConnectToServer(ConnectToServerEvent e)
|
||||||
{
|
{
|
||||||
// 使用环境中的服务器地址
|
// 使用环境中的服务器地址
|
||||||
Connect(_serverAddress, e.Port);
|
Connect(_serverAddress, e.Port);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Connect(string address, int port)
|
private void Connect(string address, int port)
|
||||||
{
|
{
|
||||||
// 连接逻辑
|
// 连接逻辑
|
||||||
@ -183,7 +183,7 @@ public class NetworkSystem : AbstractSystem
|
|||||||
|
|
||||||
## 错误示例
|
## 错误示例
|
||||||
|
|
||||||
```
|
```csharp
|
||||||
// ❌ 错误:获取必需值但不存在
|
// ❌ 错误:获取必需值但不存在
|
||||||
public class BadExampleSystem : AbstractSystem
|
public class BadExampleSystem : AbstractSystem
|
||||||
{
|
{
|
||||||
@ -204,7 +204,7 @@ public class GoodExampleSystem : AbstractSystem
|
|||||||
{
|
{
|
||||||
// 处理值
|
// 处理值
|
||||||
}
|
}
|
||||||
|
|
||||||
// 或者提供默认值
|
// 或者提供默认值
|
||||||
var gameMode = this.GetContext().Environment.Get<string>("GameMode") ?? "Default";
|
var gameMode = this.GetContext().Environment.Get<string>("GameMode") ?? "Default";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -353,43 +353,43 @@ public class CombatSystem : AbstractSystem
|
|||||||
### Controller 中注册事件
|
### Controller 中注册事件
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public partial class GameController : Node, IController
|
public class GameController : IController
|
||||||
{
|
{
|
||||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||||
|
|
||||||
public override void _Ready()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// 注册多个事件
|
// 注册多个事件
|
||||||
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
|
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
|
||||||
.AddToUnregisterList(_unregisterList);
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
|
||||||
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
|
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
|
||||||
.AddToUnregisterList(_unregisterList);
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
|
||||||
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted)
|
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted)
|
||||||
.AddToUnregisterList(_unregisterList);
|
.AddToUnregisterList(_unregisterList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGameStarted(GameStartedEvent e)
|
private void OnGameStarted(GameStartedEvent e)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Game started!");
|
Console.WriteLine("Game started!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlayerDied(PlayerDiedEvent e)
|
private void OnPlayerDied(PlayerDiedEvent e)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Player died at {e.Position}: {e.Cause}");
|
Console.WriteLine($"Player died at {e.Position}: {e.Cause}");
|
||||||
ShowGameOverScreen();
|
ShowGameOverScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLevelCompleted(LevelCompletedEvent e)
|
private void OnLevelCompleted(LevelCompletedEvent e)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Level {e.LevelId} completed! Score: {e.Score}");
|
Console.WriteLine($"Level {e.LevelId} completed! Score: {e.Score}");
|
||||||
ShowVictoryScreen(e);
|
ShowVictoryScreen(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _ExitTree()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
_unregisterList.UnRegisterAll();
|
_unregisterList.UnRegisterAll();
|
||||||
}
|
}
|
||||||
@ -449,9 +449,11 @@ public class EventBridge : AbstractSystem
|
|||||||
### 4. 临时事件监听
|
### 4. 临时事件监听
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class TutorialController : Node, IController
|
public class TutorialController : IController
|
||||||
{
|
{
|
||||||
public override void _Ready()
|
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// 只监听一次
|
// 只监听一次
|
||||||
IUnRegister unregister = null;
|
IUnRegister unregister = null;
|
||||||
@ -495,22 +497,22 @@ public class AchievementSystem : AbstractSystem
|
|||||||
### 使用 UnRegisterList
|
### 使用 UnRegisterList
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class MyController : Node, IController
|
public class MyController : IController
|
||||||
{
|
{
|
||||||
// 统一管理所有注销对象
|
// 统一管理所有注销对象
|
||||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
public override void _Ready()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// 所有注册都添加到列表
|
// 所有注册都添加到列表
|
||||||
this.RegisterEvent<Event1>(OnEvent1)
|
this.RegisterEvent<Event1>(OnEvent1)
|
||||||
.AddToUnregisterList(_unregisterList);
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
|
||||||
this.RegisterEvent<Event2>(OnEvent2)
|
this.RegisterEvent<Event2>(OnEvent2)
|
||||||
.AddToUnregisterList(_unregisterList);
|
.AddToUnregisterList(_unregisterList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _ExitTree()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
// 一次性注销所有
|
// 一次性注销所有
|
||||||
_unregisterList.UnRegisterAll();
|
_unregisterList.UnRegisterAll();
|
||||||
@ -518,17 +520,6 @@ public class MyController : Node, IController
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 使用 Godot 节点生命周期
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public override void _Ready()
|
|
||||||
{
|
|
||||||
// 当节点退出场景树时自动注销
|
|
||||||
this.RegisterEvent<GameEvent>(OnGameEvent)
|
|
||||||
.UnRegisterWhenNodeExitTree(this);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 最佳实践
|
## 最佳实践
|
||||||
|
|
||||||
1. **事件命名规范**
|
1. **事件命名规范**
|
||||||
@ -553,7 +544,7 @@ public override void _Ready()
|
|||||||
5. **注销管理**
|
5. **注销管理**
|
||||||
- 始终注销事件监听
|
- 始终注销事件监听
|
||||||
- 使用 `IUnRegisterList` 批量管理
|
- 使用 `IUnRegisterList` 批量管理
|
||||||
- 利用 Godot 节点生命周期
|
- 在适当的生命周期点调用 `Cleanup()`
|
||||||
|
|
||||||
6. **性能考虑**
|
6. **性能考虑**
|
||||||
- 避免频繁触发的事件(如每帧)
|
- 避免频繁触发的事件(如每帧)
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
┌─────────────────────────────────────────┐
|
┌─────────────────────────────────────────┐
|
||||||
│ View / UI │ UI 层:用户界面
|
│ View / UI │ UI 层:用户界面
|
||||||
├─────────────────────────────────────────┤
|
├─────────────────────────────────────────┤
|
||||||
│ Controller │ 控制层:处理用户输入
|
│ Controller │ 控制层:连接 UI 和业务逻辑
|
||||||
├─────────────────────────────────────────┤
|
├─────────────────────────────────────────┤
|
||||||
│ System │ 逻辑层:业务逻辑
|
│ System │ 逻辑层:业务逻辑
|
||||||
├─────────────────────────────────────────┤
|
├─────────────────────────────────────────┤
|
||||||
|
|||||||
@ -38,17 +38,15 @@ public class IocContainer : ContextAwareBase, IIocContainer
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public void Register<T>(T instance)
|
public void Register<T>(T instance)
|
||||||
public void Register(Type type, object instance)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**参数:**
|
**参数:**
|
||||||
|
|
||||||
- `instance`: 要注册的实例对象
|
- `instance`: 要注册的实例对象
|
||||||
- `type`: 要注册的类型(重载方法)
|
|
||||||
|
|
||||||
**特点:**
|
**特点:**
|
||||||
|
|
||||||
- 支持泛型和非泛型注册
|
- 支持泛型注册
|
||||||
- 自动注册到实例的所有接口类型
|
- 自动注册到实例的所有接口类型
|
||||||
- 线程安全操作
|
- 线程安全操作
|
||||||
- 容器冻结后抛出异常
|
- 容器冻结后抛出异常
|
||||||
@ -62,11 +60,10 @@ var container = new IocContainer();
|
|||||||
container.Register<IPlayerModel>(new PlayerModel());
|
container.Register<IPlayerModel>(new PlayerModel());
|
||||||
container.Register<IGameSystem>(new GameSystem());
|
container.Register<IGameSystem>(new GameSystem());
|
||||||
container.Register<IStorageUtility>(new StorageUtility());
|
container.Register<IStorageUtility>(new StorageUtility());
|
||||||
|
|
||||||
// 非泛型注册
|
|
||||||
container.Register(typeof(ICustomService), new CustomService());
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**注意:** 非泛型的 `Register(Type, object)` 方法是内部方法 `RegisterInternal`,不作为公开 API 使用。
|
||||||
|
|
||||||
### RegisterSingleton`<T>`
|
### RegisterSingleton`<T>`
|
||||||
|
|
||||||
注册单例实例到容器中。一个类型只允许一个实例。
|
注册单例实例到容器中。一个类型只允许一个实例。
|
||||||
@ -136,35 +133,36 @@ var system2 = container.Get<IUpdateable>(); // 返回 gameSystem
|
|||||||
var system3 = container.Get<GameSystem>(); // 返回 gameSystem
|
var system3 = container.Get<GameSystem>(); // 返回 gameSystem
|
||||||
```
|
```
|
||||||
|
|
||||||
### RegisterSystem
|
### RegisterSystem(Architecture 方法)
|
||||||
|
|
||||||
注册系统实例,将其绑定到其所有实现的接口上。
|
**注意:** `RegisterSystem` 是 `Architecture` 类的方法,不是 `IocContainer` 的方法。它用于在架构中注册系统实例。
|
||||||
|
|
||||||
**方法签名:**
|
**方法签名(在 Architecture 中):**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public void RegisterSystem(ISystem system)
|
public TSystem RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
|
||||||
```
|
```
|
||||||
|
|
||||||
**参数:**
|
|
||||||
|
|
||||||
- `system`: 系统实例对象
|
|
||||||
|
|
||||||
**特点:**
|
**特点:**
|
||||||
|
|
||||||
- 专门为 System 设计的注册方法
|
- 专门为 System 设计的注册方法
|
||||||
- 内部调用 `RegisterPlurality`
|
- 内部调用 `IocContainer.RegisterPlurality`
|
||||||
|
- 自动处理 System 的生命周期
|
||||||
- 提供语义化的 API
|
- 提供语义化的 API
|
||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var container = new IocContainer();
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
// 注册系统
|
protected override void Init()
|
||||||
container.RegisterSystem(new CombatSystem());
|
{
|
||||||
container.RegisterSystem(new InventorySystem());
|
// 注册系统
|
||||||
container.RegisterSystem(new QuestSystem());
|
RegisterSystem(new CombatSystem());
|
||||||
|
RegisterSystem(new InventorySystem());
|
||||||
|
RegisterSystem(new QuestSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get`<T>` 和 GetAll`<T>`
|
### Get`<T>` 和 GetAll`<T>`
|
||||||
|
|||||||
@ -61,13 +61,18 @@ public class PlayerModel : AbstractModel
|
|||||||
{
|
{
|
||||||
switch (phase)
|
switch (phase)
|
||||||
{
|
{
|
||||||
case ArchitecturePhase.Initializing:
|
case ArchitecturePhase.BeforeModelInit:
|
||||||
// 架构初始化阶段的处理
|
// 模型初始化前的处理
|
||||||
|
break;
|
||||||
|
case ArchitecturePhase.AfterModelInit:
|
||||||
|
// 模型初始化后的处理
|
||||||
break;
|
break;
|
||||||
case ArchitecturePhase.Ready:
|
case ArchitecturePhase.Ready:
|
||||||
// 架构就绪阶段的处理
|
// 架构就绪阶段的处理
|
||||||
break;
|
break;
|
||||||
// ... 其他阶段处理
|
case ArchitecturePhase.Destroying:
|
||||||
|
// 架构销毁阶段的处理
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +162,7 @@ public class GameModel : AbstractModel
|
|||||||
{
|
{
|
||||||
switch (phase)
|
switch (phase)
|
||||||
{
|
{
|
||||||
case ArchitecturePhase.ShuttingDown:
|
case ArchitecturePhase.Destroying:
|
||||||
// 游戏模型清理工作
|
// 游戏模型清理工作
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -167,6 +172,48 @@ public class GameModel : AbstractModel
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 异步 Model
|
||||||
|
|
||||||
|
Model 支持异步初始化,通过实现 `IAsyncInitializable` 接口可以在初始化时执行异步操作。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 异步模型示例
|
||||||
|
public class ConfigModel : AbstractModel, IAsyncInitializable
|
||||||
|
{
|
||||||
|
public BindableProperty<GameConfig> Config { get; } = new(null);
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步加载配置
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
var config = await storage.LoadConfigAsync();
|
||||||
|
Config.Value = config;
|
||||||
|
|
||||||
|
// 配置加载完成后发送事件
|
||||||
|
this.SendEvent(new ConfigLoadedEvent { Config = config });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在架构中使用异步 Model
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterModel(new ConfigModel());
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步初始化架构
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
```
|
||||||
|
|
||||||
## 最佳实践
|
## 最佳实践
|
||||||
|
|
||||||
1. **使用 BindableProperty 存储需要监听的数据**
|
1. **使用 BindableProperty 存储需要监听的数据**
|
||||||
@ -174,6 +221,7 @@ public class GameModel : AbstractModel
|
|||||||
3. **复杂计算和业务逻辑放在 System 中**
|
3. **复杂计算和业务逻辑放在 System 中**
|
||||||
4. **使用事件通知数据的重要变化**
|
4. **使用事件通知数据的重要变化**
|
||||||
5. **保持 Model 简单纯粹,只做数据管理**
|
5. **保持 Model 简单纯粹,只做数据管理**
|
||||||
|
6. **对于需要异步加载的数据,实现 IAsyncInitializable 接口**
|
||||||
|
|
||||||
## 相关包
|
## 相关包
|
||||||
|
|
||||||
|
|||||||
@ -21,27 +21,40 @@ TResult Do(); // 执行查询并返回结果
|
|||||||
|
|
||||||
## 核心类
|
## 核心类
|
||||||
|
|
||||||
### AbstractQuery`<TResult>`
|
### AbstractQuery`<TInput, TResult>`
|
||||||
|
|
||||||
抽象查询基类,提供了查询的基础实现。
|
抽象查询基类,提供了查询的基础实现。通过 `IQueryInput` 接口传递参数。
|
||||||
|
|
||||||
**核心方法:**
|
**核心方法:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
TResult IQuery<TResult>.Do(); // 实现 IQuery 接口
|
TResult IQuery<TResult>.Do(); // 实现 IQuery 接口
|
||||||
protected abstract TResult OnDo(); // 抽象查询方法,由子类实现
|
protected abstract TResult OnDo(TInput input); // 抽象查询方法,接收输入参数
|
||||||
```
|
```
|
||||||
|
|
||||||
**使用方式:**
|
**使用方式:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public abstract class AbstractQuery<TResult> : ContextAwareBase, IQuery<TResult>
|
public abstract class AbstractQuery<TInput, TResult> : ContextAwareBase, IQuery<TResult>
|
||||||
|
where TInput : IQueryInput
|
||||||
{
|
{
|
||||||
public TResult Do() => OnDo(); // 执行查询
|
public TResult Do() => OnDo(Input); // 执行查询
|
||||||
protected abstract TResult OnDo(); // 子类实现查询逻辑
|
public TInput Input { get; set; } // 输入参数
|
||||||
|
protected abstract TResult OnDo(TInput input); // 子类实现查询逻辑
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### AbstractAsyncQuery`<TInput, TResult>`
|
||||||
|
|
||||||
|
支持异步执行的查询基类。
|
||||||
|
|
||||||
|
**核心方法:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Task<TResult> IAsyncQuery<TResult>.DoAsync(); // 实现异步查询接口
|
||||||
|
protected abstract Task<TResult> OnDoAsync(TInput input); // 抽象异步查询方法
|
||||||
|
```
|
||||||
|
|
||||||
### EmptyQueryInput
|
### EmptyQueryInput
|
||||||
|
|
||||||
空查询输入类,用于表示不需要任何输入参数的查询操作。
|
空查询输入类,用于表示不需要任何输入参数的查询操作。
|
||||||
@ -83,67 +96,76 @@ public sealed class QueryBus : IQueryBus
|
|||||||
### 1. 定义查询
|
### 1. 定义查询
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
// 定义查询输入
|
||||||
|
public class GetPlayerGoldInput : IQueryInput { }
|
||||||
|
|
||||||
// 查询玩家金币数量
|
// 查询玩家金币数量
|
||||||
public class GetPlayerGoldQuery : AbstractQuery<int>
|
public class GetPlayerGoldQuery : AbstractQuery<GetPlayerGoldInput, int>
|
||||||
{
|
{
|
||||||
protected override int OnDo()
|
protected override int OnDo(GetPlayerGoldInput input)
|
||||||
{
|
{
|
||||||
return this.GetModel<PlayerModel>().Gold.Value;
|
return this.GetModel<PlayerModel>().Gold.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询玩家是否死亡
|
// 定义查询输入
|
||||||
public class IsPlayerDeadQuery : AbstractQuery<bool>
|
public class GetItemCountInput : IQueryInput
|
||||||
{
|
{
|
||||||
protected override bool OnDo()
|
public string ItemId { get; set; }
|
||||||
{
|
|
||||||
return this.GetModel<PlayerModel>().Health.Value <= 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询背包中指定物品的数量
|
// 查询背包中指定物品的数量
|
||||||
public class GetItemCountQuery : AbstractQuery<int>
|
public class GetItemCountQuery : AbstractQuery<GetItemCountInput, int>
|
||||||
{
|
{
|
||||||
private readonly string _itemId;
|
protected override int OnDo(GetItemCountInput input)
|
||||||
|
|
||||||
public GetItemCountQuery(string itemId)
|
|
||||||
{
|
|
||||||
_itemId = itemId;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override int OnDo()
|
|
||||||
{
|
{
|
||||||
var inventory = this.GetModel<InventoryModel>();
|
var inventory = this.GetModel<InventoryModel>();
|
||||||
return inventory.GetItemCount(_itemId);
|
return inventory.GetItemCount(input.ItemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义异步查询输入
|
||||||
|
public class LoadPlayerDataInput : IQueryInput
|
||||||
|
{
|
||||||
|
public string PlayerId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步查询玩家数据
|
||||||
|
public class LoadPlayerDataQuery : AbstractAsyncQuery<LoadPlayerDataInput, PlayerData>
|
||||||
|
{
|
||||||
|
protected override async Task<PlayerData> OnDoAsync(LoadPlayerDataInput input)
|
||||||
|
{
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
return await storage.LoadPlayerDataAsync(input.PlayerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 发送查询
|
### 2. 发送查询
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public partial class ShopUI : Control, IController
|
public class ShopUI : IController
|
||||||
{
|
{
|
||||||
[Export] private Button _buyButton;
|
[Export] private Button _buyButton;
|
||||||
[Export] private int _itemPrice = 100;
|
[Export] private int _itemPrice = 100;
|
||||||
|
|
||||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||||
|
|
||||||
public override void _Ready()
|
public void OnReady()
|
||||||
{
|
{
|
||||||
_buyButton.Pressed += OnBuyButtonPressed;
|
_buyButton.Pressed += OnBuyButtonPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBuyButtonPressed()
|
private void OnBuyButtonPressed()
|
||||||
{
|
{
|
||||||
// 查询玩家金币
|
// 查询玩家金币
|
||||||
int playerGold = this.SendQuery(new GetPlayerGoldQuery());
|
var query = new GetPlayerGoldQuery { Input = new GetPlayerGoldInput() };
|
||||||
|
int playerGold = this.SendQuery(query);
|
||||||
|
|
||||||
if (playerGold >= _itemPrice)
|
if (playerGold >= _itemPrice)
|
||||||
{
|
{
|
||||||
// 发送购买命令
|
// 发送购买命令
|
||||||
this.SendCommand(new BuyItemCommand("sword_01"));
|
this.SendCommand(new BuyItemCommand { Input = new BuyItemInput { ItemId = "sword_01" } });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -167,15 +189,24 @@ public class CombatSystem : AbstractSystem
|
|||||||
private void OnEnemyAttack(EnemyAttackEvent e)
|
private void OnEnemyAttack(EnemyAttackEvent e)
|
||||||
{
|
{
|
||||||
// 查询玩家是否已经死亡
|
// 查询玩家是否已经死亡
|
||||||
bool isDead = this.SendQuery(new IsPlayerDeadQuery());
|
var query = new IsPlayerDeadQuery { Input = new EmptyQueryInput() };
|
||||||
|
bool isDead = this.SendQuery(query);
|
||||||
|
|
||||||
if (!isDead)
|
if (!isDead)
|
||||||
{
|
{
|
||||||
// 执行伤害逻辑
|
// 执行伤害逻辑
|
||||||
this.SendCommand(new TakeDamageCommand(e.Damage));
|
this.SendCommand(new TakeDamageCommand { Input = new TakeDamageInput { Damage = e.Damage } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class IsPlayerDeadQuery : AbstractQuery<EmptyQueryInput, bool>
|
||||||
|
{
|
||||||
|
protected override bool OnDo(EmptyQueryInput input)
|
||||||
|
{
|
||||||
|
return this.GetModel<PlayerModel>().Health.Value <= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -184,122 +215,116 @@ public class CombatSystem : AbstractSystem
|
|||||||
### 1. 带参数的复杂查询
|
### 1. 带参数的复杂查询
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 查询指定范围内的敌人列表
|
// 定义查询输入
|
||||||
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
|
public class GetEnemiesInRangeInput : IQueryInput
|
||||||
{
|
{
|
||||||
private readonly Vector3 _center;
|
public Vector3 Center { get; set; }
|
||||||
private readonly float _radius;
|
public float Radius { get; set; }
|
||||||
|
}
|
||||||
public GetEnemiesInRangeQuery(Vector3 center, float radius)
|
|
||||||
{
|
// 查询指定范围内的敌人列表
|
||||||
_center = center;
|
public class GetEnemiesInRangeQuery : AbstractQuery<GetEnemiesInRangeInput, List<Enemy>>
|
||||||
_radius = radius;
|
{
|
||||||
}
|
protected override List<Enemy> OnDo(GetEnemiesInRangeInput input)
|
||||||
|
|
||||||
protected override List<Enemy> OnDo()
|
|
||||||
{
|
{
|
||||||
var enemySystem = this.GetSystem<EnemySpawnSystem>();
|
var enemySystem = this.GetSystem<EnemySpawnSystem>();
|
||||||
return enemySystem.GetEnemiesInRange(_center, _radius);
|
return enemySystem.GetEnemiesInRange(input.Center, input.Radius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用
|
// 使用
|
||||||
var enemies = this.SendQuery(new GetEnemiesInRangeQuery(playerPosition, 10.0f));
|
var input = new GetEnemiesInRangeInput { Center = playerPosition, Radius = 10.0f };
|
||||||
|
var query = new GetEnemiesInRangeQuery { Input = input };
|
||||||
|
var enemies = this.SendQuery(query);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 组合查询
|
### 2. 组合查询
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 查询玩家是否可以使用技能
|
// 定义查询输入
|
||||||
public class CanUseSkillQuery : AbstractQuery<bool>
|
public class CanUseSkillInput : IQueryInput
|
||||||
{
|
{
|
||||||
private readonly string _skillId;
|
public string SkillId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public CanUseSkillQuery(string skillId)
|
// 查询玩家是否可以使用技能
|
||||||
{
|
public class CanUseSkillQuery : AbstractQuery<CanUseSkillInput, bool>
|
||||||
_skillId = skillId;
|
{
|
||||||
}
|
protected override bool OnDo(CanUseSkillInput input)
|
||||||
|
|
||||||
protected override bool OnDo()
|
|
||||||
{
|
{
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
var skillModel = this.GetModel<SkillModel>();
|
|
||||||
|
|
||||||
// 查询技能消耗
|
// 查询技能消耗
|
||||||
var skillCost = this.SendQuery(new GetSkillCostQuery(_skillId));
|
var skillCostQuery = new GetSkillCostQuery { Input = new GetSkillCostInput { SkillId = input.SkillId } };
|
||||||
|
var skillCost = this.SendQuery(skillCostQuery);
|
||||||
|
|
||||||
// 检查是否满足条件
|
// 检查是否满足条件
|
||||||
return playerModel.Mana.Value >= skillCost.ManaCost
|
return playerModel.Mana.Value >= skillCost.ManaCost
|
||||||
&& !this.SendQuery(new IsSkillOnCooldownQuery(_skillId));
|
&& !this.SendQuery(new IsSkillOnCooldownQuery { Input = new IsSkillOnCooldownInput { SkillId = input.SkillId } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GetSkillCostQuery : AbstractQuery<SkillCost>
|
public class GetSkillCostInput : IQueryInput
|
||||||
{
|
{
|
||||||
private readonly string _skillId;
|
public string SkillId { get; set; }
|
||||||
|
|
||||||
public GetSkillCostQuery(string skillId)
|
|
||||||
{
|
|
||||||
_skillId = skillId;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override SkillCost OnDo()
|
|
||||||
{
|
|
||||||
return this.GetModel<SkillModel>().GetSkillCost(_skillId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IsSkillOnCooldownQuery : AbstractQuery<bool>
|
public class GetSkillCostQuery : AbstractQuery<GetSkillCostInput, SkillCost>
|
||||||
{
|
{
|
||||||
private readonly string _skillId;
|
protected override SkillCost OnDo(GetSkillCostInput input)
|
||||||
|
|
||||||
public IsSkillOnCooldownQuery(string skillId)
|
|
||||||
{
|
{
|
||||||
_skillId = skillId;
|
return this.GetModel<SkillModel>().GetSkillCost(input.SkillId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
protected override bool OnDo()
|
|
||||||
{
|
public class IsSkillOnCooldownInput : IQueryInput
|
||||||
return this.GetModel<SkillModel>().IsOnCooldown(_skillId);
|
{
|
||||||
|
public string SkillId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IsSkillOnCooldownQuery : AbstractQuery<IsSkillOnCooldownInput, bool>
|
||||||
|
{
|
||||||
|
protected override bool OnDo(IsSkillOnCooldownInput input)
|
||||||
|
{
|
||||||
|
return this.GetModel<SkillModel>().IsOnCooldown(input.SkillId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 聚合数据查询
|
### 3. 聚合数据查询
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 查询玩家战斗力
|
// 查询玩家战斗力
|
||||||
public class GetPlayerPowerQuery : AbstractQuery<int>
|
public class GetPlayerPowerQuery : AbstractQuery<EmptyQueryInput, int>
|
||||||
{
|
{
|
||||||
protected override int OnDo()
|
protected override int OnDo(EmptyQueryInput input)
|
||||||
{
|
{
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
var equipmentModel = this.GetModel<EquipmentModel>();
|
var equipmentModel = this.GetModel<EquipmentModel>();
|
||||||
|
|
||||||
int basePower = playerModel.Level.Value * 10;
|
int basePower = playerModel.Level.Value * 10;
|
||||||
int equipmentPower = equipmentModel.GetTotalPower();
|
int equipmentPower = equipmentModel.GetTotalPower();
|
||||||
int buffPower = this.SendQuery(new GetBuffPowerQuery());
|
int buffPower = this.SendQuery(new GetBuffPowerQuery { Input = new EmptyQueryInput() });
|
||||||
|
|
||||||
return basePower + equipmentPower + buffPower;
|
return basePower + equipmentPower + buffPower;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询玩家详细信息(用于UI显示)
|
// 查询玩家详细信息(用于UI显示)
|
||||||
public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
|
public class GetPlayerInfoQuery : AbstractQuery<EmptyQueryInput, PlayerInfo>
|
||||||
{
|
{
|
||||||
protected override PlayerInfo OnDo()
|
protected override PlayerInfo OnDo(EmptyQueryInput input)
|
||||||
{
|
{
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
|
|
||||||
return new PlayerInfo
|
return new PlayerInfo
|
||||||
{
|
{
|
||||||
Name = playerModel.Name.Value,
|
Name = playerModel.Name.Value,
|
||||||
Level = playerModel.Level.Value,
|
Level = playerModel.Level.Value,
|
||||||
Health = playerModel.Health.Value,
|
Health = playerModel.Health.Value,
|
||||||
MaxHealth = playerModel.MaxHealth.Value,
|
MaxHealth = playerModel.MaxHealth.Value,
|
||||||
Gold = this.SendQuery(new GetPlayerGoldQuery()),
|
Gold = this.SendQuery(new GetPlayerGoldQuery { Input = new GetPlayerGoldInput() }),
|
||||||
Power = this.SendQuery(new GetPlayerPowerQuery())
|
Power = this.SendQuery(new GetPlayerPowerQuery { Input = new EmptyQueryInput() })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,37 +336,31 @@ public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
|
|||||||
// 在 AI System 中查询玩家状态
|
// 在 AI System 中查询玩家状态
|
||||||
public class EnemyAISystem : AbstractSystem
|
public class EnemyAISystem : AbstractSystem
|
||||||
{
|
{
|
||||||
protected override void OnInit() { }
|
protected override void OnInit() { }
|
||||||
|
|
||||||
public void UpdateEnemyBehavior(Enemy enemy)
|
public void UpdateEnemyBehavior(Enemy enemy)
|
||||||
{
|
{
|
||||||
// 查询玩家位置
|
// 查询玩家位置
|
||||||
var playerPos = this.SendQuery(new GetPlayerPositionQuery());
|
var playerPosQuery = new GetPlayerPositionQuery { Input = new EmptyQueryInput() };
|
||||||
|
var playerPos = this.SendQuery(playerPosQuery);
|
||||||
|
|
||||||
// 查询玩家是否在攻击范围内
|
// 查询玩家是否在攻击范围内
|
||||||
bool inRange = this.SendQuery(new IsPlayerInRangeQuery
|
var inRangeInput = new IsPlayerInRangeInput { Position = enemy.Position, Range = enemy.AttackRange };
|
||||||
{
|
bool inRange = this.SendQuery(new IsPlayerInRangeQuery { Input = inRangeInput });
|
||||||
Position = enemy.Position,
|
|
||||||
Range = enemy.AttackRange
|
|
||||||
});
|
|
||||||
|
|
||||||
if (inRange)
|
if (inRange)
|
||||||
{
|
{
|
||||||
// 查询是否可以攻击
|
// 查询是否可以攻击
|
||||||
bool canAttack = this.SendQuery(new CanEnemyAttackQuery
|
var canAttackInput = new CanEnemyAttackInput { EnemyId = enemy.Id };
|
||||||
{
|
bool canAttack = this.SendQuery(new CanEnemyAttackQuery { Input = canAttackInput });
|
||||||
EnemyId = enemy.Id
|
|
||||||
});
|
|
||||||
|
|
||||||
if (canAttack)
|
if (canAttack)
|
||||||
{
|
{
|
||||||
this.SendCommand(new EnemyAttackCommand { EnemyId = enemy.Id });
|
this.SendCommand(new EnemyAttackCommand { Input = new EnemyAttackInput { EnemyId = enemy.Id } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Query 的执行机制
|
## Query 的执行机制
|
||||||
|
|||||||
@ -185,16 +185,16 @@ public class GameArchitecture : Architecture
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 在 Controller 中
|
// 在 Controller 中
|
||||||
public partial class GameController : Node, IController
|
public class GameController : IController
|
||||||
{
|
{
|
||||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||||
|
|
||||||
public override void _Ready()
|
public void Start()
|
||||||
{
|
{
|
||||||
// 获取 System
|
// 获取 System
|
||||||
var combatSystem = this.GetSystem<CombatSystem>();
|
var combatSystem = this.GetSystem<CombatSystem>();
|
||||||
var questSystem = this.GetSystem<QuestSystem>();
|
var questSystem = this.GetSystem<QuestSystem>();
|
||||||
|
|
||||||
// 使用 System
|
// 使用 System
|
||||||
combatSystem.StartBattle();
|
combatSystem.StartBattle();
|
||||||
}
|
}
|
||||||
@ -223,6 +223,61 @@ public class AISystem : AbstractSystem
|
|||||||
|
|
||||||
## 常见使用模式
|
## 常见使用模式
|
||||||
|
|
||||||
|
### 异步 System
|
||||||
|
|
||||||
|
System 支持异步初始化,通过实现 `IAsyncInitializable` 接口可以在初始化时执行异步操作。
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 异步系统示例
|
||||||
|
public class DataLoadSystem : AbstractSystem, IAsyncInitializable
|
||||||
|
{
|
||||||
|
private GameData _gameData;
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 同步初始化逻辑
|
||||||
|
this.RegisterEvent<GameStartedEvent>(OnGameStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
// 异步加载游戏数据
|
||||||
|
var storage = this.GetUtility<IStorageUtility>();
|
||||||
|
_gameData = await storage.LoadGameDataAsync();
|
||||||
|
|
||||||
|
// 数据加载完成后发送事件
|
||||||
|
this.SendEvent(new GameDataLoadedEvent { Data = _gameData });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGameStarted(GameStartedEvent e)
|
||||||
|
{
|
||||||
|
// 使用已加载的数据
|
||||||
|
Console.WriteLine($"Game data loaded: {_gameData.Version}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDestroy()
|
||||||
|
{
|
||||||
|
// 清理资源
|
||||||
|
_gameData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在架构中使用异步 System
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterModel(new PlayerModel());
|
||||||
|
RegisterSystem(new DataLoadSystem());
|
||||||
|
RegisterSystem(new CombatSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步初始化架构
|
||||||
|
var architecture = new GameArchitecture();
|
||||||
|
await architecture.InitializeAsync();
|
||||||
|
```
|
||||||
|
|
||||||
### 1. 事件驱动的 System
|
### 1. 事件驱动的 System
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
107
docs/zh-CN/faq.md
Normal file
107
docs/zh-CN/faq.md
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# 常见问题(FAQ)
|
||||||
|
|
||||||
|
## 安装与配置
|
||||||
|
|
||||||
|
### Q: 如何安装 GFramework?
|
||||||
|
|
||||||
|
A: 使用 NuGet 包管理器:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package GeWuYou.GFramework.Core
|
||||||
|
```
|
||||||
|
|
||||||
|
详见 [安装配置](./getting-started/installation.md)。
|
||||||
|
|
||||||
|
### Q: 支持哪些 .NET 版本?
|
||||||
|
|
||||||
|
A: 支持 .NET 6.0 及更高版本(包括 .NET 7.0、8.0、9.0、10.0)。
|
||||||
|
|
||||||
|
### Q: 可以在 Unity 中使用吗?
|
||||||
|
|
||||||
|
A: Core 模块可以在 Unity 中使用。Godot 集成模块仅支持 Godot。
|
||||||
|
|
||||||
|
## 架构设计
|
||||||
|
|
||||||
|
### Q: 什么时候应该使用 Command?
|
||||||
|
|
||||||
|
A: 当需要封装用户操作、支持撤销/重做或记录操作历史时。
|
||||||
|
|
||||||
|
### Q: 什么时候应该使用 Query?
|
||||||
|
|
||||||
|
A: 当需要查询数据、检查条件或明确只读意图时。
|
||||||
|
|
||||||
|
### Q: 什么时候应该使用 Event?
|
||||||
|
|
||||||
|
A: 当需要通知其他组件、实现松耦合通信或支持多个监听者时。
|
||||||
|
|
||||||
|
### Q: Model 中可以包含业务逻辑吗?
|
||||||
|
|
||||||
|
A: 不建议。Model 应该只存储数据。业务逻辑应该在 System 中实现。
|
||||||
|
|
||||||
|
## 性能优化
|
||||||
|
|
||||||
|
### Q: 频繁调用 GetModel 会有性能问题吗?
|
||||||
|
|
||||||
|
A: 不会,但建议缓存引用以提高效率。
|
||||||
|
|
||||||
|
### Q: 事件系统的性能如何?
|
||||||
|
|
||||||
|
A: 事件系统使用类型安全的泛型,性能优异。
|
||||||
|
|
||||||
|
### Q: BindableProperty 有性能开销吗?
|
||||||
|
|
||||||
|
A: 开销极小。仅在值变化时触发回调。
|
||||||
|
|
||||||
|
## 内存管理
|
||||||
|
|
||||||
|
### Q: 如何避免内存泄漏?
|
||||||
|
|
||||||
|
A: 使用 `UnRegisterList` 统一管理注销。
|
||||||
|
|
||||||
|
### Q: 销毁 Architecture 时会自动清理吗?
|
||||||
|
|
||||||
|
A: 是的。调用 `architecture.Destroy()` 会自动销毁所有组件。
|
||||||
|
|
||||||
|
## Godot 集成
|
||||||
|
|
||||||
|
### Q: 如何在 Godot 中使用 GFramework?
|
||||||
|
|
||||||
|
A: 安装 Godot 集成包:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package GeWuYou.GFramework.Godot
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 支持哪个版本的 Godot?
|
||||||
|
|
||||||
|
A: Godot 4.6 及更高版本。
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
### Q: 如何测试使用 GFramework 的代码?
|
||||||
|
|
||||||
|
A: 通过依赖注入模拟架构进行单元测试。
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### Q: 如何组织大型项目?
|
||||||
|
|
||||||
|
A: 按功能模块组织(Models、Systems、Commands、Events 等)。
|
||||||
|
|
||||||
|
### Q: 如何处理异步操作?
|
||||||
|
|
||||||
|
A: 使用 async/await 结合事件系统。
|
||||||
|
|
||||||
|
### Q: 如何处理错误?
|
||||||
|
|
||||||
|
A: 使用事件传递错误信息。
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
### Q: GFramework 采用什么许可证?
|
||||||
|
|
||||||
|
A: Apache License 2.0。可用于商业项目。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**没有找到答案?** 在 [GitHub](https://github.com/GeWuYou/GFramework) 提交 Issue。
|
||||||
306
docs/zh-CN/troubleshooting.md
Normal file
306
docs/zh-CN/troubleshooting.md
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
# 故障排除与调试
|
||||||
|
|
||||||
|
本指南帮助你诊断和解决 GFramework 使用中的常见问题。
|
||||||
|
|
||||||
|
## 常见错误
|
||||||
|
|
||||||
|
### 1. 组件未注册错误
|
||||||
|
|
||||||
|
**错误信息**:`KeyNotFoundException: 未找到类型为 'XXX' 的组件`
|
||||||
|
|
||||||
|
**原因**:尝试获取未注册的组件。
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误:未注册 PlayerModel
|
||||||
|
var arch = new GameArchitecture();
|
||||||
|
arch.Initialize();
|
||||||
|
var model = arch.GetModel<PlayerModel>(); // 抛出异常
|
||||||
|
|
||||||
|
// ✅ 正确:先注册再获取
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterModel(new PlayerModel()); // 注册模型
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 事件监听器未触发
|
||||||
|
|
||||||
|
**问题**:注册了事件监听器但没有被调用。
|
||||||
|
|
||||||
|
**原因**:
|
||||||
|
|
||||||
|
- 事件类型不匹配
|
||||||
|
- 监听器在事件发送前注销
|
||||||
|
- 事件发送时使用了错误的类型
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误:事件类型不匹配
|
||||||
|
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
|
||||||
|
this.SendEvent(new PlayerAttackedEvent()); // 不会触发
|
||||||
|
|
||||||
|
// ✅ 正确:事件类型匹配
|
||||||
|
this.RegisterEvent<PlayerAttackedEvent>(OnPlayerAttacked);
|
||||||
|
this.SendEvent(new PlayerAttackedEvent()); // 正确触发
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 内存泄漏
|
||||||
|
|
||||||
|
**问题**:应用内存持续增长。
|
||||||
|
|
||||||
|
**原因**:
|
||||||
|
|
||||||
|
- 未注销事件监听器
|
||||||
|
- 未注销属性监听器
|
||||||
|
- 未销毁 Architecture
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 正确:使用 UnRegisterList 管理注销
|
||||||
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<Event1>(OnEvent1)
|
||||||
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
|
||||||
|
model.Property.Register(OnPropertyChanged)
|
||||||
|
.AddToUnregisterList(_unregisterList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
_unregisterList.UnRegisterAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 销毁架构
|
||||||
|
architecture.Destroy();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 循环依赖
|
||||||
|
|
||||||
|
**问题**:两个系统相互依赖导致死循环。
|
||||||
|
|
||||||
|
**原因**:系统间直接调用而不是通过事件通信。
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 错误:直接调用导致循环依赖
|
||||||
|
public class SystemA : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnEvent(EventA e)
|
||||||
|
{
|
||||||
|
var systemB = this.GetSystem<SystemB>();
|
||||||
|
systemB.DoSomething(); // 可能导致循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 正确:使用事件解耦
|
||||||
|
public class SystemA : AbstractSystem
|
||||||
|
{
|
||||||
|
private void OnEvent(EventA e)
|
||||||
|
{
|
||||||
|
this.SendEvent(new EventB()); // 发送事件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SystemB : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
this.RegisterEvent<EventB>(OnEventB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调试技巧
|
||||||
|
|
||||||
|
### 1. 启用日志
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 创建自定义日志系统
|
||||||
|
public class DebugLogger : ILogger
|
||||||
|
{
|
||||||
|
public void Log(string message)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[GFramework] {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在架构中注册
|
||||||
|
public class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
RegisterUtility(new DebugLogger());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 追踪事件流
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 创建事件追踪系统
|
||||||
|
public class EventTracer : AbstractSystem
|
||||||
|
{
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// 监听所有事件(需要手动注册)
|
||||||
|
this.RegisterEvent<PlayerDiedEvent>(e =>
|
||||||
|
Console.WriteLine("Event: PlayerDiedEvent"));
|
||||||
|
this.RegisterEvent<PlayerAttackedEvent>(e =>
|
||||||
|
Console.WriteLine("Event: PlayerAttackedEvent"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 检查组件状态
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 创建调试工具
|
||||||
|
public class ArchitectureDebugger
|
||||||
|
{
|
||||||
|
public static void PrintArchitectureState(IArchitecture arch)
|
||||||
|
{
|
||||||
|
Console.WriteLine("=== Architecture State ===");
|
||||||
|
|
||||||
|
// 打印已注册的模型
|
||||||
|
Console.WriteLine("Models:");
|
||||||
|
// 需要通过反射或其他方式访问
|
||||||
|
|
||||||
|
// 打印已注册的系统
|
||||||
|
Console.WriteLine("Systems:");
|
||||||
|
// 需要通过反射或其他方式访问
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 单元测试调试
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Test]
|
||||||
|
public void DebugPlayerDamage()
|
||||||
|
{
|
||||||
|
var arch = new TestArchitecture();
|
||||||
|
arch.Initialize();
|
||||||
|
|
||||||
|
var player = arch.GetModel<PlayerModel>();
|
||||||
|
|
||||||
|
// 打印初始状态
|
||||||
|
Console.WriteLine($"Initial Health: {player.Health.Value}");
|
||||||
|
|
||||||
|
// 发送伤害事件
|
||||||
|
arch.SendEvent(new DamageEvent { Amount = 10 });
|
||||||
|
|
||||||
|
// 打印最终状态
|
||||||
|
Console.WriteLine($"Final Health: {player.Health.Value}");
|
||||||
|
|
||||||
|
// 验证
|
||||||
|
Assert.AreEqual(90, player.Health.Value);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 性能问题
|
||||||
|
|
||||||
|
### 1. 事件处理缓慢
|
||||||
|
|
||||||
|
**问题**:事件处理耗时过长。
|
||||||
|
|
||||||
|
**诊断**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 测量事件处理时间
|
||||||
|
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
arch.SendEvent(new HeavyEvent());
|
||||||
|
stopwatch.Stop();
|
||||||
|
Console.WriteLine($"Event processing time: {stopwatch.ElapsedMilliseconds}ms");
|
||||||
|
```
|
||||||
|
|
||||||
|
**优化**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 低效:在事件处理中进行复杂计算
|
||||||
|
private void OnEvent(HeavyEvent e)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 1000000; i++)
|
||||||
|
{
|
||||||
|
// 复杂计算
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 高效:异步处理
|
||||||
|
private async void OnEvent(HeavyEvent e)
|
||||||
|
{
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 1000000; i++)
|
||||||
|
{
|
||||||
|
// 复杂计算
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 频繁的 GetModel 调用
|
||||||
|
|
||||||
|
**问题**:每帧都调用 GetModel 导致性能下降。
|
||||||
|
|
||||||
|
**优化**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ❌ 低效
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
var model = _architecture.GetModel<PlayerModel>(); // 每帧调用
|
||||||
|
model.Health.Value -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 高效
|
||||||
|
private PlayerModel _playerModel;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_playerModel = _architecture.GetModel<PlayerModel>(); // 只调用一次
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
_playerModel.Health.Value -= 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题排查清单
|
||||||
|
|
||||||
|
- [ ] 所有组件都已注册?
|
||||||
|
- [ ] 事件类型是否匹配?
|
||||||
|
- [ ] 是否正确注销了监听器?
|
||||||
|
- [ ] Architecture 是否已初始化?
|
||||||
|
- [ ] 是否有循环依赖?
|
||||||
|
- [ ] 内存使用是否持续增长?
|
||||||
|
- [ ] 事件处理是否过于复杂?
|
||||||
|
- [ ] 是否缓存了频繁访问的组件?
|
||||||
|
|
||||||
|
## 获取帮助
|
||||||
|
|
||||||
|
如果问题仍未解决:
|
||||||
|
|
||||||
|
1. 查看 [常见问题](../faq.md)
|
||||||
|
2. 查看 [API 参考](../api-reference/index.md)
|
||||||
|
3. 在 [GitHub Issues](https://github.com/GeWuYou/GFramework/issues) 提交问题
|
||||||
|
4. 查看 [教程](../tutorials/index.md) 中的示例代码
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**提示**:在提交 Issue 时,请提供:
|
||||||
|
|
||||||
|
- 错误信息和堆栈跟踪
|
||||||
|
- 最小化的复现代码
|
||||||
|
- 你的环境信息(.NET 版本、IDE 等)
|
||||||
Loading…
x
Reference in New Issue
Block a user