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
@ -67,6 +67,12 @@ jobs:
|
||||
working-directory: docs
|
||||
run: bun run build
|
||||
|
||||
# 生成 LLM 索引文件
|
||||
- name: Make docs LLM ready
|
||||
uses: demodrive-ai/llms-txt-action@v1
|
||||
with:
|
||||
docs-path: docs/.vitepress/dist
|
||||
|
||||
# 上传构建产物作为 Pages 部署工件
|
||||
- name: Upload Pages Artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@ riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
GFramework.sln.DotSettings.user
|
||||
.idea/
|
||||
opencode.json
|
||||
.opencode.json
|
||||
.claude/
|
||||
@ -82,7 +82,6 @@ export default defineConfig({
|
||||
{ text: '工具类', link: '/zh-CN/core/utility' },
|
||||
{ text: '模型层', link: '/zh-CN/core/model' },
|
||||
{ text: '系统层', link: '/zh-CN/core/system' },
|
||||
{ text: '控制器', link: '/zh-CN/core/controller' },
|
||||
{ text: '规则系统', link: '/zh-CN/core/rule' },
|
||||
{ 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
|
||||
// 定义一个开始游戏的命令
|
||||
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()
|
||||
{
|
||||
// 获取需要的模型
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
var gameModel = this.GetModel<GameModel>();
|
||||
|
||||
// 执行业务逻辑
|
||||
playerModel.PlayerName.Value = _playerName;
|
||||
gameModel.CurrentLevel.Value = _levelId;
|
||||
gameModel.GameState.Value = GameState.Playing;
|
||||
|
||||
// 发送事件通知其他模块
|
||||
this.SendEvent(new GameStartedEvent());
|
||||
playerModel.Health.Value = playerModel.MaxHealth.Value;
|
||||
this.SendEvent(new PlayerHealthRestoredEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,17 +60,16 @@ public class GameController : IController
|
||||
{
|
||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||
|
||||
public void OnStartButtonClicked()
|
||||
public void OnRestoreHealthButtonClicked()
|
||||
{
|
||||
// 发送命令实例
|
||||
this.SendCommand(new StartGameCommand(1, "Player1"));
|
||||
this.SendCommand(new SimpleCommand());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AbstractCommandWithResult`<TResult>`
|
||||
### AbstractCommand`<TResult>`
|
||||
|
||||
带返回值命令的抽象基类,同样继承自 ContextAwareBase。
|
||||
无输入参数但带返回值的命令基类。
|
||||
|
||||
**核心方法:**
|
||||
|
||||
@ -99,49 +81,28 @@ protected abstract TResult OnExecute(); // 抽象执行方法,由子类实
|
||||
**使用示例:**
|
||||
|
||||
```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()
|
||||
{
|
||||
// 获取游戏配置
|
||||
var config = this.GetModel<GameConfigModel>();
|
||||
|
||||
// 计算最终伤害
|
||||
var baseDamage = _attackerAttackPower - _defenderDefense;
|
||||
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
|
||||
|
||||
return (int)finalDamage;
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
return playerModel.Health.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用带返回值的命令
|
||||
public class CombatSystem : AbstractSystem
|
||||
// 使用命令
|
||||
public class UISystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit() { }
|
||||
|
||||
public void Attack(Character attacker, Character defender)
|
||||
protected override void OnInit()
|
||||
{
|
||||
// 发送命令并获取返回值
|
||||
var damage = this.SendCommand(new CalculateDamageCommand(
|
||||
attacker.AttackPower,
|
||||
defender.Defense
|
||||
));
|
||||
this.RegisterEvent<UpdateUIEvent>(OnUpdateUI);
|
||||
}
|
||||
|
||||
// 应用伤害
|
||||
defender.Health -= damage;
|
||||
|
||||
// 发送伤害事件
|
||||
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
|
||||
private void OnUpdateUI(UpdateUIEvent e)
|
||||
{
|
||||
var health = this.SendCommand(new GetPlayerHealthQuery());
|
||||
Console.WriteLine($"Player health: {health}");
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -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
|
||||
// 异步命令示例
|
||||
public class LoadSaveDataCommand : AbstractAsyncCommandWithResult<SaveData>
|
||||
void ICommand.Execute(); // 实现 ICommand 接口
|
||||
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>();
|
||||
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,7 +90,7 @@ public class DefaultEnvironment : EnvironmentBase
|
||||
|
||||
### 1. 定义自定义环境
|
||||
|
||||
```
|
||||
```csharp
|
||||
public class GameEnvironment : EnvironmentBase
|
||||
{
|
||||
public override string Name { get; } = "Game";
|
||||
@ -112,7 +112,7 @@ public class GameEnvironment : EnvironmentBase
|
||||
|
||||
### 2. 在架构中使用环境
|
||||
|
||||
```
|
||||
```csharp
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
@ -130,7 +130,7 @@ public class GameArchitecture : Architecture
|
||||
|
||||
### 3. 使用环境值
|
||||
|
||||
```
|
||||
```csharp
|
||||
public class NetworkSystem : AbstractSystem
|
||||
{
|
||||
private string _serverAddress;
|
||||
@ -183,7 +183,7 @@ public class NetworkSystem : AbstractSystem
|
||||
|
||||
## 错误示例
|
||||
|
||||
```
|
||||
```csharp
|
||||
// ❌ 错误:获取必需值但不存在
|
||||
public class BadExampleSystem : AbstractSystem
|
||||
{
|
||||
|
||||
@ -353,13 +353,13 @@ public class CombatSystem : AbstractSystem
|
||||
### Controller 中注册事件
|
||||
|
||||
```csharp
|
||||
public partial class GameController : Node, IController
|
||||
public class GameController : IController
|
||||
{
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||
|
||||
public override void _Ready()
|
||||
public void Initialize()
|
||||
{
|
||||
// 注册多个事件
|
||||
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
|
||||
@ -389,7 +389,7 @@ public partial class GameController : Node, IController
|
||||
ShowVictoryScreen(e);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
public void Cleanup()
|
||||
{
|
||||
_unregisterList.UnRegisterAll();
|
||||
}
|
||||
@ -449,9 +449,11 @@ public class EventBridge : AbstractSystem
|
||||
### 4. 临时事件监听
|
||||
|
||||
```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;
|
||||
@ -495,12 +497,12 @@ public class AchievementSystem : AbstractSystem
|
||||
### 使用 UnRegisterList
|
||||
|
||||
```csharp
|
||||
public class MyController : Node, IController
|
||||
public class MyController : IController
|
||||
{
|
||||
// 统一管理所有注销对象
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public override void _Ready()
|
||||
public void Initialize()
|
||||
{
|
||||
// 所有注册都添加到列表
|
||||
this.RegisterEvent<Event1>(OnEvent1)
|
||||
@ -510,7 +512,7 @@ public class MyController : Node, IController
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
public void Cleanup()
|
||||
{
|
||||
// 一次性注销所有
|
||||
_unregisterList.UnRegisterAll();
|
||||
@ -518,17 +520,6 @@ public class MyController : Node, IController
|
||||
}
|
||||
```
|
||||
|
||||
### 使用 Godot 节点生命周期
|
||||
|
||||
```csharp
|
||||
public override void _Ready()
|
||||
{
|
||||
// 当节点退出场景树时自动注销
|
||||
this.RegisterEvent<GameEvent>(OnGameEvent)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **事件命名规范**
|
||||
@ -553,7 +544,7 @@ public override void _Ready()
|
||||
5. **注销管理**
|
||||
- 始终注销事件监听
|
||||
- 使用 `IUnRegisterList` 批量管理
|
||||
- 利用 Godot 节点生命周期
|
||||
- 在适当的生命周期点调用 `Cleanup()`
|
||||
|
||||
6. **性能考虑**
|
||||
- 避免频繁触发的事件(如每帧)
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
┌─────────────────────────────────────────┐
|
||||
│ View / UI │ UI 层:用户界面
|
||||
├─────────────────────────────────────────┤
|
||||
│ Controller │ 控制层:处理用户输入
|
||||
│ Controller │ 控制层:连接 UI 和业务逻辑
|
||||
├─────────────────────────────────────────┤
|
||||
│ System │ 逻辑层:业务逻辑
|
||||
├─────────────────────────────────────────┤
|
||||
|
||||
@ -38,17 +38,15 @@ public class IocContainer : ContextAwareBase, IIocContainer
|
||||
|
||||
```csharp
|
||||
public void Register<T>(T instance)
|
||||
public void Register(Type type, object instance)
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `instance`: 要注册的实例对象
|
||||
- `type`: 要注册的类型(重载方法)
|
||||
|
||||
**特点:**
|
||||
|
||||
- 支持泛型和非泛型注册
|
||||
- 支持泛型注册
|
||||
- 自动注册到实例的所有接口类型
|
||||
- 线程安全操作
|
||||
- 容器冻结后抛出异常
|
||||
@ -62,11 +60,10 @@ var container = new IocContainer();
|
||||
container.Register<IPlayerModel>(new PlayerModel());
|
||||
container.Register<IGameSystem>(new GameSystem());
|
||||
container.Register<IStorageUtility>(new StorageUtility());
|
||||
|
||||
// 非泛型注册
|
||||
container.Register(typeof(ICustomService), new CustomService());
|
||||
```
|
||||
|
||||
**注意:** 非泛型的 `Register(Type, object)` 方法是内部方法 `RegisterInternal`,不作为公开 API 使用。
|
||||
|
||||
### RegisterSingleton`<T>`
|
||||
|
||||
注册单例实例到容器中。一个类型只允许一个实例。
|
||||
@ -136,35 +133,36 @@ var system2 = container.Get<IUpdateable>(); // 返回 gameSystem
|
||||
var system3 = container.Get<GameSystem>(); // 返回 gameSystem
|
||||
```
|
||||
|
||||
### RegisterSystem
|
||||
### RegisterSystem(Architecture 方法)
|
||||
|
||||
注册系统实例,将其绑定到其所有实现的接口上。
|
||||
**注意:** `RegisterSystem` 是 `Architecture` 类的方法,不是 `IocContainer` 的方法。它用于在架构中注册系统实例。
|
||||
|
||||
**方法签名:**
|
||||
**方法签名(在 Architecture 中):**
|
||||
|
||||
```csharp
|
||||
public void RegisterSystem(ISystem system)
|
||||
public TSystem RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `system`: 系统实例对象
|
||||
|
||||
**特点:**
|
||||
|
||||
- 专门为 System 设计的注册方法
|
||||
- 内部调用 `RegisterPlurality`
|
||||
- 内部调用 `IocContainer.RegisterPlurality`
|
||||
- 自动处理 System 的生命周期
|
||||
- 提供语义化的 API
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
var container = new IocContainer();
|
||||
|
||||
// 注册系统
|
||||
container.RegisterSystem(new CombatSystem());
|
||||
container.RegisterSystem(new InventorySystem());
|
||||
container.RegisterSystem(new QuestSystem());
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
// 注册系统
|
||||
RegisterSystem(new CombatSystem());
|
||||
RegisterSystem(new InventorySystem());
|
||||
RegisterSystem(new QuestSystem());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get`<T>` 和 GetAll`<T>`
|
||||
|
||||
@ -61,13 +61,18 @@ public class PlayerModel : AbstractModel
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case ArchitecturePhase.Initializing:
|
||||
// 架构初始化阶段的处理
|
||||
case ArchitecturePhase.BeforeModelInit:
|
||||
// 模型初始化前的处理
|
||||
break;
|
||||
case ArchitecturePhase.AfterModelInit:
|
||||
// 模型初始化后的处理
|
||||
break;
|
||||
case ArchitecturePhase.Ready:
|
||||
// 架构就绪阶段的处理
|
||||
break;
|
||||
// ... 其他阶段处理
|
||||
case ArchitecturePhase.Destroying:
|
||||
// 架构销毁阶段的处理
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,7 +162,7 @@ public class GameModel : AbstractModel
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case ArchitecturePhase.ShuttingDown:
|
||||
case ArchitecturePhase.Destroying:
|
||||
// 游戏模型清理工作
|
||||
break;
|
||||
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 存储需要监听的数据**
|
||||
@ -174,6 +221,7 @@ public class GameModel : AbstractModel
|
||||
3. **复杂计算和业务逻辑放在 System 中**
|
||||
4. **使用事件通知数据的重要变化**
|
||||
5. **保持 Model 简单纯粹,只做数据管理**
|
||||
6. **对于需要异步加载的数据,实现 IAsyncInitializable 接口**
|
||||
|
||||
## 相关包
|
||||
|
||||
|
||||
@ -21,27 +21,40 @@ TResult Do(); // 执行查询并返回结果
|
||||
|
||||
## 核心类
|
||||
|
||||
### AbstractQuery`<TResult>`
|
||||
### AbstractQuery`<TInput, TResult>`
|
||||
|
||||
抽象查询基类,提供了查询的基础实现。
|
||||
抽象查询基类,提供了查询的基础实现。通过 `IQueryInput` 接口传递参数。
|
||||
|
||||
**核心方法:**
|
||||
|
||||
```csharp
|
||||
TResult IQuery<TResult>.Do(); // 实现 IQuery 接口
|
||||
protected abstract TResult OnDo(); // 抽象查询方法,由子类实现
|
||||
protected abstract TResult OnDo(TInput input); // 抽象查询方法,接收输入参数
|
||||
```
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```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(); // 执行查询
|
||||
protected abstract TResult OnDo(); // 子类实现查询逻辑
|
||||
public TResult Do() => OnDo(Input); // 执行查询
|
||||
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
|
||||
|
||||
空查询输入类,用于表示不需要任何输入参数的查询操作。
|
||||
@ -83,54 +96,62 @@ public sealed class QueryBus : IQueryBus
|
||||
### 1. 定义查询
|
||||
|
||||
```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;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询玩家是否死亡
|
||||
public class IsPlayerDeadQuery : AbstractQuery<bool>
|
||||
// 定义查询输入
|
||||
public class GetItemCountInput : IQueryInput
|
||||
{
|
||||
protected override bool OnDo()
|
||||
{
|
||||
return this.GetModel<PlayerModel>().Health.Value <= 0;
|
||||
}
|
||||
public string ItemId { get; set; }
|
||||
}
|
||||
|
||||
// 查询背包中指定物品的数量
|
||||
public class GetItemCountQuery : AbstractQuery<int>
|
||||
public class GetItemCountQuery : AbstractQuery<GetItemCountInput, int>
|
||||
{
|
||||
private readonly string _itemId;
|
||||
|
||||
public GetItemCountQuery(string itemId)
|
||||
{
|
||||
_itemId = itemId;
|
||||
}
|
||||
|
||||
protected override int OnDo()
|
||||
protected override int OnDo(GetItemCountInput input)
|
||||
{
|
||||
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. 发送查询
|
||||
|
||||
```csharp
|
||||
public partial class ShopUI : Control, IController
|
||||
public class ShopUI : IController
|
||||
{
|
||||
[Export] private Button _buyButton;
|
||||
[Export] private int _itemPrice = 100;
|
||||
|
||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||
|
||||
public override void _Ready()
|
||||
public void OnReady()
|
||||
{
|
||||
_buyButton.Pressed += OnBuyButtonPressed;
|
||||
}
|
||||
@ -138,12 +159,13 @@ public partial class ShopUI : Control, IController
|
||||
private void OnBuyButtonPressed()
|
||||
{
|
||||
// 查询玩家金币
|
||||
int playerGold = this.SendQuery(new GetPlayerGoldQuery());
|
||||
var query = new GetPlayerGoldQuery { Input = new GetPlayerGoldInput() };
|
||||
int playerGold = this.SendQuery(query);
|
||||
|
||||
if (playerGold >= _itemPrice)
|
||||
{
|
||||
// 发送购买命令
|
||||
this.SendCommand(new BuyItemCommand("sword_01"));
|
||||
this.SendCommand(new BuyItemCommand { Input = new BuyItemInput { ItemId = "sword_01" } });
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -167,15 +189,24 @@ public class CombatSystem : AbstractSystem
|
||||
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)
|
||||
{
|
||||
// 执行伤害逻辑
|
||||
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,111 +215,105 @@ public class CombatSystem : AbstractSystem
|
||||
### 1. 带参数的复杂查询
|
||||
|
||||
```csharp
|
||||
// 查询指定范围内的敌人列表
|
||||
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
|
||||
// 定义查询输入
|
||||
public class GetEnemiesInRangeInput : IQueryInput
|
||||
{
|
||||
private readonly Vector3 _center;
|
||||
private readonly float _radius;
|
||||
public Vector3 Center { get; set; }
|
||||
public float Radius { get; set; }
|
||||
}
|
||||
|
||||
public GetEnemiesInRangeQuery(Vector3 center, float radius)
|
||||
{
|
||||
_center = center;
|
||||
_radius = radius;
|
||||
}
|
||||
|
||||
protected override List<Enemy> OnDo()
|
||||
// 查询指定范围内的敌人列表
|
||||
public class GetEnemiesInRangeQuery : AbstractQuery<GetEnemiesInRangeInput, List<Enemy>>
|
||||
{
|
||||
protected override List<Enemy> OnDo(GetEnemiesInRangeInput input)
|
||||
{
|
||||
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. 组合查询
|
||||
|
||||
```csharp
|
||||
// 查询玩家是否可以使用技能
|
||||
public class CanUseSkillQuery : AbstractQuery<bool>
|
||||
// 定义查询输入
|
||||
public class CanUseSkillInput : IQueryInput
|
||||
{
|
||||
private readonly string _skillId;
|
||||
public string SkillId { get; set; }
|
||||
}
|
||||
|
||||
public CanUseSkillQuery(string skillId)
|
||||
{
|
||||
_skillId = skillId;
|
||||
}
|
||||
|
||||
protected override bool OnDo()
|
||||
// 查询玩家是否可以使用技能
|
||||
public class CanUseSkillQuery : AbstractQuery<CanUseSkillInput, bool>
|
||||
{
|
||||
protected override bool OnDo(CanUseSkillInput input)
|
||||
{
|
||||
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
|
||||
&& !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 GetSkillCostQuery(string skillId)
|
||||
{
|
||||
_skillId = skillId;
|
||||
}
|
||||
|
||||
protected override SkillCost OnDo()
|
||||
{
|
||||
return this.GetModel<SkillModel>().GetSkillCost(_skillId);
|
||||
}
|
||||
public string SkillId { get; set; }
|
||||
}
|
||||
|
||||
public class IsSkillOnCooldownQuery : AbstractQuery<bool>
|
||||
public class GetSkillCostQuery : AbstractQuery<GetSkillCostInput, SkillCost>
|
||||
{
|
||||
private readonly string _skillId;
|
||||
|
||||
public IsSkillOnCooldownQuery(string skillId)
|
||||
protected override SkillCost OnDo(GetSkillCostInput input)
|
||||
{
|
||||
_skillId = skillId;
|
||||
}
|
||||
|
||||
protected override bool OnDo()
|
||||
{
|
||||
return this.GetModel<SkillModel>().IsOnCooldown(_skillId);
|
||||
return this.GetModel<SkillModel>().GetSkillCost(input.SkillId);
|
||||
}
|
||||
}
|
||||
|
||||
public class IsSkillOnCooldownInput : IQueryInput
|
||||
{
|
||||
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. 聚合数据查询
|
||||
|
||||
```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 equipmentModel = this.GetModel<EquipmentModel>();
|
||||
|
||||
int basePower = playerModel.Level.Value * 10;
|
||||
int equipmentPower = equipmentModel.GetTotalPower();
|
||||
int buffPower = this.SendQuery(new GetBuffPowerQuery());
|
||||
int buffPower = this.SendQuery(new GetBuffPowerQuery { Input = new EmptyQueryInput() });
|
||||
|
||||
return basePower + equipmentPower + buffPower;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询玩家详细信息(用于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>();
|
||||
|
||||
@ -298,8 +323,8 @@ public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
|
||||
Level = playerModel.Level.Value,
|
||||
Health = playerModel.Health.Value,
|
||||
MaxHealth = playerModel.MaxHealth.Value,
|
||||
Gold = this.SendQuery(new GetPlayerGoldQuery()),
|
||||
Power = this.SendQuery(new GetPlayerPowerQuery())
|
||||
Gold = this.SendQuery(new GetPlayerGoldQuery { Input = new GetPlayerGoldInput() }),
|
||||
Power = this.SendQuery(new GetPlayerPowerQuery { Input = new EmptyQueryInput() })
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -311,37 +336,31 @@ public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
|
||||
// 在 AI System 中查询玩家状态
|
||||
public class EnemyAISystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit() { }
|
||||
protected override void OnInit() { }
|
||||
|
||||
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
|
||||
{
|
||||
Position = enemy.Position,
|
||||
Range = enemy.AttackRange
|
||||
});
|
||||
var inRangeInput = new IsPlayerInRangeInput { Position = enemy.Position, Range = enemy.AttackRange };
|
||||
bool inRange = this.SendQuery(new IsPlayerInRangeQuery { Input = inRangeInput });
|
||||
|
||||
if (inRange)
|
||||
{
|
||||
// 查询是否可以攻击
|
||||
bool canAttack = this.SendQuery(new CanEnemyAttackQuery
|
||||
{
|
||||
EnemyId = enemy.Id
|
||||
});
|
||||
var canAttackInput = new CanEnemyAttackInput { EnemyId = enemy.Id };
|
||||
bool canAttack = this.SendQuery(new CanEnemyAttackQuery { Input = canAttackInput });
|
||||
|
||||
if (canAttack)
|
||||
{
|
||||
this.SendCommand(new EnemyAttackCommand { EnemyId = enemy.Id });
|
||||
this.SendCommand(new EnemyAttackCommand { Input = new EnemyAttackInput { EnemyId = enemy.Id } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Query 的执行机制
|
||||
|
||||
@ -185,11 +185,11 @@ public class GameArchitecture : Architecture
|
||||
|
||||
```csharp
|
||||
// 在 Controller 中
|
||||
public partial class GameController : Node, IController
|
||||
public class GameController : IController
|
||||
{
|
||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||
|
||||
public override void _Ready()
|
||||
public void Start()
|
||||
{
|
||||
// 获取 System
|
||||
var combatSystem = this.GetSystem<CombatSystem>();
|
||||
@ -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
|
||||
|
||||
```
|
||||
|
||||
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