From 93b25a19f793c6b1d6a62991b04e5f26e5f37c8b Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:13:07 +0800 Subject: [PATCH] =?UTF-8?q?docs(core):=20=E6=9B=B4=E6=96=B0=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E7=B3=BB=E7=BB=9F=E6=96=87=E6=A1=A3=E5=B9=B6=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E6=8E=A7=E5=88=B6=E5=99=A8=E7=8B=AC=E7=AB=8B=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 controller.md 文件,将控制器相关内容整合到其他文档中 - 重构 command.md 文档,更新命令基类的类型参数设计 - 添加新的命令基类 AbstractCommand 和 AbstractCommand - 更新命令使用示例,采用输入参数对象替代构造函数参数 - 优化事件注册相关代码示例,移除 Godot 特定的生命周期方法 - 更新依赖注入容器文档,明确 Register 方法的泛型特性 - 添加模型异步初始化功能说明和相关接口介绍 - 重构查询系统文档,统一采用输入参数对象的设计模式 - 更新架构生命周期枚举值,使用更准确的阶段名称 - 添加 GitHub Actions 工作流配置,集成 LLM 准备的文档索引功能 --- .github/workflows/publish-docs.yml | 6 + .gitignore | 3 +- docs/.vitepress/config.mts | 1 - docs/zh-CN/api-reference/index.md | 411 ++++++++++++++++++++ docs/zh-CN/best-practices/index.md | 483 +++++++++++++++++++++++ docs/zh-CN/core/async-initialization.md | 443 +++++++++++++++++++++ docs/zh-CN/core/command.md | 265 ++++++++----- docs/zh-CN/core/context.md | 490 ++++++++++++++++++++++++ docs/zh-CN/core/controller.md | 413 -------------------- docs/zh-CN/core/environment.md | 24 +- docs/zh-CN/core/events.md | 53 ++- docs/zh-CN/core/index.md | 2 +- docs/zh-CN/core/ioc.md | 40 +- docs/zh-CN/core/model.md | 56 ++- docs/zh-CN/core/query.md | 247 ++++++------ docs/zh-CN/core/system.md | 63 ++- docs/zh-CN/faq.md | 107 ++++++ docs/zh-CN/troubleshooting.md | 306 +++++++++++++++ 18 files changed, 2720 insertions(+), 693 deletions(-) create mode 100644 docs/zh-CN/api-reference/index.md create mode 100644 docs/zh-CN/best-practices/index.md create mode 100644 docs/zh-CN/core/async-initialization.md create mode 100644 docs/zh-CN/core/context.md delete mode 100644 docs/zh-CN/core/controller.md create mode 100644 docs/zh-CN/faq.md create mode 100644 docs/zh-CN/troubleshooting.md diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 50f5566..ff5e01b 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -66,6 +66,12 @@ jobs: - name: Build VitePress 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 diff --git a/.gitignore b/.gitignore index 1a55ae5..d56f69b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ riderModule.iml /_ReSharper.Caches/ GFramework.sln.DotSettings.user .idea/ -opencode.json \ No newline at end of file +.opencode.json +.claude/ \ No newline at end of file diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 5cc590a..1c09e8d 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -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' } ] diff --git a/docs/zh-CN/api-reference/index.md b/docs/zh-CN/api-reference/index.md new file mode 100644 index 0000000..283b5fc --- /dev/null +++ b/docs/zh-CN/api-reference/index.md @@ -0,0 +1,411 @@ +# API 参考文档 + +本文档提供 GFramework 各模块的完整 API 参考。 + +## 核心命名空间 + +### GFramework.Core.architecture + +核心架构命名空间,包含所有基础组件。 + +#### 主要类型 + +| 类型 | 说明 | +|--------------------|--------| +| `Architecture` | 应用架构基类 | +| `AbstractModel` | 数据模型基类 | +| `AbstractSystem` | 业务系统基类 | +| `AbstractCommand` | 命令基类 | +| `AbstractQuery` | 查询基类 | +| `IController` | 控制器接口 | +| `IUtility` | 工具类接口 | + +### GFramework.Core.events + +事件系统命名空间。 + +#### 主要类型 + +| 类型 | 说明 | +|-------------------|----------| +| `IEvent` | 事件接口 | +| `IEventSystem` | 事件系统接口 | +| `TypeEventSystem` | 类型安全事件系统 | + +### GFramework.Core.property + +属性系统命名空间。 + +#### 主要类型 + +| 类型 | 说明 | +|-----------------------|--------| +| `BindableProperty` | 可绑定属性 | +| `IUnRegister` | 注销接口 | +| `IUnRegisterList` | 注销列表接口 | + +### GFramework.Core.ioc + +IoC 容器命名空间。 + +#### 主要类型 + +| 类型 | 说明 | +|--------------|------| +| `IContainer` | 容器接口 | +| `Container` | 容器实现 | + +### GFramework.Core.pool + +对象池命名空间。 + +#### 主要类型 + +| 类型 | 说明 | +|------------------|-------| +| `IObjectPool` | 对象池接口 | +| `ObjectPool` | 对象池实现 | + +## 常用 API + +### Architecture + +```csharp +public abstract class Architecture : IBelongToArchitecture +{ + // 初始化架构 + public void Initialize(); + + // 销毁架构 + public void Destroy(); + + // 注册模型 + public void RegisterModel(T model) where T : IModel; + + // 获取模型 + public T GetModel() where T : IModel; + + // 注册系统 + public void RegisterSystem(T system) where T : ISystem; + + // 获取系统 + public T GetSystem() where T : ISystem; + + // 注册工具 + public void RegisterUtility(T utility) where T : IUtility; + + // 获取工具 + public T GetUt>() where T : IUtility; + + // 发送命令 + public void SendCommand(T command) where T : ICommand; + + // 发送查询 + public TResult SendQuery(TQuery query) + where TQuery : IQuery; + + // 发送事件 + public void SendEvent(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 e) where T : IEvent; + + // 获取模型 + protected T GetModel() where T : IModel; + + // 获取系统 + protected T GetSystem() where T : ISystem; + + // 获取工具 + protected T GetUtility() 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 e) where T : IEvent; + + // 注册事件 + protected IUnRegister RegisterEvent(Action onEvent) + where T : IEvent; + + // 获取模型 + protected T GetModel() where T : IModel; + + // 获取系统 + protected T GetSystem() where T : ISystem; + + // 获取工具 + protected T GetUtility() where T : IUtility; +} +``` + +### AbstractCommand + +```csharp +public abstract class AbstractCommand : IBelongToArchitecture +{ + // 执行命令 + public void Execute(); + + // 命令实现 + protected abstract void OnDo(); + + // 获取架构 + public IArchitecture GetArchitecture(); + + // 发送事件 + protected void SendEvent(T e) where T : IEvent; + + // 获取模型 + protected T GetModel() where T : IModel; + + // 获取系统 + protected T GetSystem() where T : ISystem; + + // 获取工具 + protected T GetUtility() where T : IUtility; +} +``` + +### AbstractQuery + +```csharp +public abstract class AbstractQuery : IBelongToArchitecture +{ + // 执行查询 + public T Do(); + + // 查询实现 + protected abstract T OnDo(); + + // 获取架构 + public IArchitecture GetArchitecture(); + + // 获取模型 + protected T GetModel() where T : IModel; + + // 获取系统 + protected T GetSystem() where T : ISystem; + + // 获取工具 + protected T GetUtility() where T : IUtility; +} +``` + +### BindableProperty + +```csharp +public class BindableProperty +{ + // 构造函数 + public BindableProperty(T initialValue = default); + + // 获取或设置值 + public T Value { get; set; } + + // 注册监听器 + public IUnRegister Register(Action onValueChanged); + + // 注册监听器(包含初始值) + public IUnRegister RegisterWithInitValue(Action onValueChanged); + + // 获取当前值 + public T GetValue(); + + // 设置值 + public void SetValue(T newValue); +} +``` + +## 扩展方法 + +### 架构扩展 + +```csharp +// 发送命令 +public static void SendCommand(this IBelongToArchitecture self, T command) + where T : ICommand; + +// 发送查询 +public static TResult SendQuery( + this IBelongToArchitecture self, TQuery query) + where TQuery : IQuery; + +// 发送事件 +public static void SendEvent(this IBelongToArchitecture self, T e) + where T : IEvent; + +// 获取模型 +public static T GetModel(this IBelongToArchitecture self) + where T : IModel; + +// 获取系统 +public static T GetSystem(this IBelongToArchitecture self) + where T : ISystem; + +// 获取工具 +public static T GetUtility(this IBelongToArchitecture self) + where T : IUtility; + +// 注册事件 +public static IUnRegister RegisterEvent( + this IBelongToArchitecture self, Action 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(); + this.SendEvent(new AttackEvent { Damage = Damage }); + } +} + +// 使用 +arch.SendCommand(new AttackCommand { Damage = 10 }); +``` + +### 发送查询 + +```csharp +public class GetPlayerHealthQuery : AbstractQuery +{ + protected override int OnDo() + { + return this.GetModel().Health.Value; + } +} + +// 使用 +var health = arch.SendQuery(new GetPlayerHealthQuery()); +``` + +### 监听事件 + +```csharp +public class PlayerSystem : AbstractSystem +{ + protected override void OnInit() + { + this.RegisterEvent(OnPlayerDied); + } + + private void OnPlayerDied(PlayerDiedEvent e) + { + Console.WriteLine("Player died!"); + } +} +``` + +--- + +更多详情请查看各模块的详细文档。 diff --git a/docs/zh-CN/best-practices/index.md b/docs/zh-CN/best-practices/index.md new file mode 100644 index 0000000..4b76286 --- /dev/null +++ b/docs/zh-CN/best-practices/index.md @@ -0,0 +1,483 @@ +# 最佳实践 + +本文档总结了使用 GFramework 的最佳实践和设计模式。 + +## 架构设计 + +### 1. 清晰的职责分离 + +**原则**:每一层都有明确的职责,不要混淆。 + +```csharp +// ✅ 正确的职责分离 +public class PlayerModel : AbstractModel +{ + // Model:只存储数据 + public BindableProperty Health { get; } = new(100); + public BindableProperty Score { get; } = new(0); +} + +public class CombatSystem : AbstractSystem +{ + // System:处理业务逻辑 + protected override void OnInit() + { + this.RegisterEvent(OnAttack); + } + + private void OnAttack(AttackEvent e) + { + var player = this.GetModel(); + player.Health.Value -= e.Damage; + } +} + +public class PlayerController : IController +{ + // Controller:连接 UI 和逻辑 + public void Initialize() + { + var player = _architecture.GetModel(); + 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.DoSomething(); // 直接调用 + } +} + +// ✅ 松耦合 +public class SystemA : AbstractSystem +{ + private void OnEvent(EventA e) + { + this.SendEvent(new EventB()); // 发送事件 + } +} + +public class SystemB : AbstractSystem +{ + protected override void OnInit() + { + this.RegisterEvent(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 +{ + protected override Vector2 OnDo() + { + // 只查询,不修改 + return this.GetModel().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 { } +public class GetInventoryItemsQuery : AbstractQuery> { } + +// 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(); + + // 注册事件并添加到注销列表 + this.RegisterEvent(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(); + model.Health.Value -= 1; +} + +// ✅ 高效:缓存引用 +private PlayerModel _playerModel; + +public void Initialize() +{ + _playerModel = _architecture.GetModel(); +} + +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 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(); + } + + [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(); + Assert.AreEqual(100, player.Health.Value); + + // 执行操作 + _architecture.SendCommand(new AttackCommand { Damage = 20 }); + + // 验证结果 + Assert.AreEqual(80, player.Health.Value); + } +} +``` + +## 文档 + +### 1. 代码注释 + +```csharp +/// +/// 玩家模型,存储玩家的所有状态数据 +/// +public class PlayerModel : AbstractModel +{ + /// + /// 玩家的生命值,使用 BindableProperty 实现响应式更新 + /// + public BindableProperty 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(); + player.Health.Value -= e.Amount; + } +} +``` + +### 2. 忘记注销监听器 + +```csharp +// ❌ 错误:可能导致内存泄漏 +public void Initialize() +{ + this.RegisterEvent(OnEvent1); // 未注销 +} + +// ✅ 正确 +private IUnRegisterList _unregisterList = new UnRegisterList(); + +public void Initialize() +{ + this.RegisterEvent(OnEvent1) + .AddToUnregisterList(_unregisterList); +} + +public void Cleanup() +{ + _unregisterList.UnRegisterAll(); +} +``` + +### 3. 直接调用其他系统 + +```csharp +// ❌ 错误:紧耦合 +public class SystemA : AbstractSystem +{ + private void OnEvent(EventA e) + { + var systemB = this.GetSystem(); + systemB.DoSomething(); + } +} + +// ✅ 正确:使用事件解耦 +public class SystemA : AbstractSystem +{ + private void OnEvent(EventA e) + { + this.SendEvent(new EventB()); + } +} +``` + +--- + +遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。 diff --git a/docs/zh-CN/core/async-initialization.md b/docs/zh-CN/core/async-initialization.md new file mode 100644 index 0000000..f5db460 --- /dev/null +++ b/docs/zh-CN/core/async-initialization.md @@ -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 Config { get; } = new(null); + + protected override void OnInit() + { + // 同步初始化逻辑 + Console.WriteLine("ConfigModel sync init"); + } + + public async Task InitializeAsync() + { + // 异步加载配置 + var storage = this.GetUtility(); + 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 { get; } = new(null); + + protected override void OnInit() + { + // 同步初始化逻辑 + } + + public async Task InitializeAsync() + { + // 异步加载玩家数据 + var storage = this.GetUtility(); + var playerId = this.GetContext().Environment.Get("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(OnGameStarted); + } + + public async Task InitializeAsync() + { + // 异步加载游戏数据 + var storage = this.GetUtility(); + _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 _resources = new(); + + protected override void OnInit() + { + // 同步初始化逻辑 + } + + public async Task InitializeAsync() + { + // 异步加载资源 + var resourceManager = this.GetUtility(); + _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 QueryAsync(string sql) + { + return await _database.QueryAsync(sql); + } +} +``` + +## 完整示例 + +### 场景:游戏启动流程 + +```csharp +// 1. 定义各个异步组件 +public class ConfigModel : AbstractModel, IAsyncInitializable +{ + public BindableProperty Config { get; } = new(null); + + protected override void OnInit() { } + + public async Task InitializeAsync() + { + var storage = this.GetUtility(); + Config.Value = await storage.LoadConfigAsync(); + } +} + +public class PlayerModel : AbstractModel, IAsyncInitializable +{ + public BindableProperty PlayerData { get; } = new(null); + + protected override void OnInit() { } + + public async Task InitializeAsync() + { + var storage = this.GetUtility(); + var playerId = this.GetContext().Environment.Get("PlayerId"); + PlayerData.Value = await storage.LoadPlayerDataAsync(playerId); + } +} + +public class ResourceLoadSystem : AbstractSystem, IAsyncInitializable +{ + private Dictionary _resources = new(); + + protected override void OnInit() + { + this.RegisterEvent(OnGameStarted); + } + + public async Task InitializeAsync() + { + var resourceManager = this.GetUtility(); + _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(); +``` + +**特点:** + +- 阻塞式,等待初始化完成 +- 适用于简单场景或控制台应用 +- 初始化失败时抛出异常 + +### 异步初始化 + +```csharp +var architecture = new GameArchitecture(); +await architecture.InitializeAsync(); // 非阻塞等待 + +// 初始化完成后可以使用架构 +var playerModel = architecture.GetModel(); +``` + +**特点:** + +- 非阻塞式,不会阻塞主线程 +- 支持异步组件初始化 +- 适用于需要加载大量资源的场景 +- 初始化失败时抛出异常 + +## 最佳实践 + +### 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 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 diff --git a/docs/zh-CN/core/command.md b/docs/zh-CN/core/command.md index f4c2f80..3f4252c 100644 --- a/docs/zh-CN/core/command.md +++ b/docs/zh-CN/core/command.md @@ -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(); - var gameModel = this.GetModel(); - - // 执行业务逻辑 - 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()); } } @@ -76,18 +59,17 @@ public class StartGameCommand : AbstractCommand 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`` +### AbstractCommand`` -带返回值命令的抽象基类,同样继承自 ContextAwareBase。 +无输入参数但带返回值的命令基类。 **核心方法:** @@ -99,49 +81,28 @@ protected abstract TResult OnExecute(); // 抽象执行方法,由子类实 **使用示例:** ```csharp -// 定义一个计算伤害的命令 -public class CalculateDamageCommand : AbstractCommandWithResult +// 定义一个无输入但有返回值的命令 +public class GetPlayerHealthQuery : AbstractCommand { - 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(); - - // 计算最终伤害 - var baseDamage = _attackerAttackPower - _defenderDefense; - var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier); - - return (int)finalDamage; + var playerModel = this.GetModel(); + 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 - )); - - // 应用伤害 - defender.Health -= damage; - - // 发送伤害事件 - this.SendEvent(new DamageDealtEvent(attacker, defender, damage)); + this.RegisterEvent(OnUpdateUI); + } + + 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`` -最基础的无返回值命令类 +带输入参数的无返回值命令类。通过 `ICommandInput` 接口传递参数。 -### AbstractCommandWithResult`` - -带返回值的命令基类 - -### AbstractCommandWithInput`` - -带输入参数的无返回值命令类 - -### AbstractCommandWithInputAndResult`` - -既带输入参数又带返回值的命令类 - -### AbstractAsyncCommand - -支持异步执行的命令基类 - -### AbstractAsyncCommandWithResult`` - -支持异步执行的带返回值命令基类 +**核心方法:** ```csharp -// 异步命令示例 -public class LoadSaveDataCommand : AbstractAsyncCommandWithResult +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 +{ + protected override void OnExecute(StartGameInput input) { - _saveSlot = saveSlot; - } + var playerModel = this.GetModel(); + var gameModel = this.GetModel(); - protected override async Task 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`` + +既带输入参数又带返回值的命令类。 + +**核心方法:** + +```csharp +TResult ICommand.Execute(); // 实现 ICommand 接口 +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 +{ + protected override int OnExecute(CalculateDamageInput input) + { + var config = this.GetModel(); + 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`` + +支持异步执行的带输入参数的无返回值命令基类。 + +**核心方法:** + +```csharp +Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口 +protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法 +``` + +### AbstractAsyncCommand`` + +支持异步执行的既带输入参数又带返回值的命令基类。 + +**核心方法:** + +```csharp +Task IAsyncCommand.ExecuteAsync(); // 实现异步命令接口 +protected abstract Task OnExecuteAsync(TInput input); // 抽象异步执行方法 +``` + +**使用示例:** + +```csharp +// 定义输入对象 +public class LoadSaveDataInput : ICommandInput +{ + public string SaveSlot { get; set; } +} + +// 定义异步命令 +public class LoadSaveDataCommand : AbstractAsyncCommand +{ + protected override async Task OnExecuteAsync(LoadSaveDataInput input) { var storage = this.GetUtility(); - return await storage.LoadSaveDataAsync(_saveSlot); + return await storage.LoadSaveDataAsync(input.SaveSlot); + } +} + +// 使用异步命令 +public class SaveSystem : AbstractSystem +{ + protected override void OnInit() + { + this.RegisterEvent(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 }); + } } } ``` diff --git a/docs/zh-CN/core/context.md b/docs/zh-CN/core/context.md new file mode 100644 index 0000000..8866f12 --- /dev/null +++ b/docs/zh-CN/core/context.md @@ -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(IArchitectureContext context) + where TArchitecture : IArchitecture; + +// 获取架构类型对应的上下文 +static IArchitectureContext GetContext() + where TArchitecture : IArchitecture; + +// 解绑架构类型 +static void Unbind() + where TArchitecture : IArchitecture; +``` + +## 在组件中使用 Context + +### 在 Model 中使用 + +```csharp +public class PlayerModel : AbstractModel +{ + public BindableProperty 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(OnEnemyAttack); + } + + private void OnEnemyAttack(EnemyAttackEvent e) + { + var context = this.GetContext(); + var playerModel = context.Container.Get(); + + // 处理伤害 + 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.Health.Value = playerModel.MaxHealth.Value; + + eventBus.Send(new GameStartedEvent()); + } +} +``` + +### 在 Query 中使用 + +```csharp +public class GetPlayerHealthQuery : AbstractQuery +{ + protected override int OnDo() + { + // 通过 Context 访问容器 + var context = this.GetContext(); + var playerModel = context.Container.Get(); + + 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(architecture.Context); +``` + +### 从 GameContext 获取上下文 + +```csharp +// 在任何地方获取架构上下文 +var context = GameContext.GetContext(); + +// 访问服务 +var playerModel = context.Container.Get(); +var eventBus = context.EventBus; +``` + +### 使用 GameContext 的扩展方法 + +```csharp +// 通过扩展方法简化访问 +public static class GameContextExtensions +{ + public static T GetModel(this IArchitectureContext context) + where T : class, IModel + { + return context.Container.Get(); + } + + public static T GetSystem(this IArchitectureContext context) + where T : class, ISystem + { + return context.Container.Get(); + } +} + +// 使用 +var context = GameContext.GetContext(); +var playerModel = context.GetModel(); +var combatSystem = context.GetSystem(); +``` + +## Context 中的服务 + +### EventBus - 事件总线 + +```csharp +var context = architecture.Context; +var eventBus = context.EventBus; + +// 注册事件 +eventBus.Register(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(); +var combatSystem = container.Get(); + +// 获取所有实现某接口的组件 +var allSystems = container.GetAll(); +``` + +### Environment - 环境配置 + +```csharp +var context = architecture.Context; +var environment = context.Environment; + +// 获取环境值 +var gameMode = environment.Get("GameMode"); +var maxPlayers = environment.Get("MaxPlayers"); + +// 安全获取值 +if (environment.TryGet("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(); + +// 销毁前 +architecture.Destroy(); +``` + +### 销毁 + +Context 随着架构的销毁而销毁: + +```csharp +architecture.Destroy(); +// Context 不再可用 +``` + +## 最佳实践 + +### 1. 通过扩展方法简化访问 + +```csharp +public static class ContextExtensions +{ + public static T GetModel(this IArchitectureContext context) + where T : class, IModel + { + return context.Container.Get(); + } + + public static T GetSystem(this IArchitectureContext context) + where T : class, ISystem + { + return context.Container.Get(); + } + + public static void SendCommand(this IArchitectureContext context, ICommand command) + { + context.CommandBus.Send(command); + } + + public static TResult SendQuery(this IArchitectureContext context, IQuery query) + { + return context.QueryBus.Send(query); + } +} + +// 使用 +var context = architecture.Context; +var playerModel = context.GetModel(); +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(OnGameStarted); + } + + private void OnGameStarted(GameStartedEvent e) + { + var playerModel = _context.Container.Get(); + } +} +``` + +### 3. 使用 GameContext 实现全局访问 + +```csharp +// 在应用启动时绑定 +public class GameBootstrapper +{ + public async Task StartAsync() + { + var architecture = new GameArchitecture(); + await architecture.InitializeAsync(); + + // 绑定到 GameContext + GameContext.Bind(architecture.Context); + } +} + +// 在任何地方访问 +public class UIController +{ + public void UpdateHealthDisplay() + { + var context = GameContext.GetContext(); + var playerModel = context.Container.Get(); + + // 更新 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(); + } + 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(); +``` + +## 相关包 + +- [`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 diff --git a/docs/zh-CN/core/controller.md b/docs/zh-CN/core/controller.md deleted file mode 100644 index 1a30073..0000000 --- a/docs/zh-CN/core/controller.md +++ /dev/null @@ -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.Health.RegisterWithInitValue(OnHealthChanged) - .AddToUnregisterList(_unregisterList); - - // 注册事件 - _architecture.RegisterEvent(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(); - UpdateUI(gameModel); - } - - private void OnStartButtonPressed() - { - // 通过命令启动游戏 - _architecture.SendCommand(); - } - - private void OnSettingsButtonPressed() - { - // 查询当前设置 - var settings = _architecture.SendQuery(new GetSettingsQuery()); - - // 打开设置面板 - _uiSystem.OpenSettingsPanel(settings); - } - - private void OnQuitButtonPressed() - { - // 发送退出命令 - _architecture.SendCommand(); - } - - 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(); - _combatSystem = _architecture.GetSystem(); - - // 注册多个事件 - _architecture.RegisterEvent(OnEnemySpawned) - .AddToUnregisterList(_unregisterList); - - _architecture.RegisterEvent(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(); - } - } - - private void OnCombatStateChanged(CombatState state) - { - // 根据战斗状态更新 UI - _architecture.GetSystem().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(OnGameEvent) - .AddToUnregisterList(_unregisterList); - - _architecture.GetModel().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() - .Score - .RegisterWithInitValue(score => UpdateDisplay(score)) - .AddToUnregisterList(_unregisterList); - } - - private void UpdateDisplay(int score) - { - // 更新分数显示 - } -} -``` - -### 状态机模式 - -``csharp -public class PlayerStateController : IController -{ - [Inject] private IArchitecture _architecture; - private Dictionary _stateHandlers; - - public void Initialize() - { - _stateHandlers = new Dictionary - { - { PlayerState.Idle, HandleIdleState }, - { PlayerState.Moving, HandleMovingState }, - { PlayerState.Attacking, HandleAttackingState } - }; - - _architecture.GetModel() - .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(OnPlayerDied) - .UnRegisterWhenNodeExitTree(this); - - _architecture.GetModel() - .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 diff --git a/docs/zh-CN/core/environment.md b/docs/zh-CN/core/environment.md index 49d7913..26a0eb3 100644 --- a/docs/zh-CN/core/environment.md +++ b/docs/zh-CN/core/environment.md @@ -90,11 +90,11 @@ public class DefaultEnvironment : EnvironmentBase ### 1. 定义自定义环境 -``` +```csharp public class GameEnvironment : EnvironmentBase { public override string Name { get; } = "Game"; - + public override void Initialize() { // 注册一些环境特定的值 @@ -102,7 +102,7 @@ public class GameEnvironment : EnvironmentBase Register("MaxPlayers", 4); Register("ServerAddress", "localhost"); } - + // 便捷方法 public string GameMode => Get("GameMode") ?? "Default"; public int MaxPlayers => Get("MaxPlayers") ?? 1; @@ -112,7 +112,7 @@ public class GameEnvironment : EnvironmentBase ### 2. 在架构中使用环境 -``` +```csharp public class GameArchitecture : Architecture { protected override void Init() @@ -120,7 +120,7 @@ public class GameArchitecture : Architecture // 环境已经在架构初始化过程中自动初始化 // 但是我们可以在需要的时候获取环境值 var gameMode = this.Context.Environment.Get("GameMode"); - + // 注册模型和系统 RegisterModel(new PlayerModel()); RegisterSystem(new GameplaySystem()); @@ -130,26 +130,26 @@ public class GameArchitecture : Architecture ### 3. 使用环境值 -``` +```csharp public class NetworkSystem : AbstractSystem { private string _serverAddress; - + protected override void OnInit() { // 从环境中获取服务器地址 _serverAddress = this.GetContext().Environment.Get("ServerAddress") ?? "localhost"; - + // 注册事件 this.RegisterEvent(OnConnectToServer); } - + private void OnConnectToServer(ConnectToServerEvent e) { // 使用环境中的服务器地址 Connect(_serverAddress, e.Port); } - + private void Connect(string address, int port) { // 连接逻辑 @@ -183,7 +183,7 @@ public class NetworkSystem : AbstractSystem ## 错误示例 -``` +```csharp // ❌ 错误:获取必需值但不存在 public class BadExampleSystem : AbstractSystem { @@ -204,7 +204,7 @@ public class GoodExampleSystem : AbstractSystem { // 处理值 } - + // 或者提供默认值 var gameMode = this.GetContext().Environment.Get("GameMode") ?? "Default"; } diff --git a/docs/zh-CN/core/events.md b/docs/zh-CN/core/events.md index 6a7e8e4..d819254 100644 --- a/docs/zh-CN/core/events.md +++ b/docs/zh-CN/core/events.md @@ -353,43 +353,43 @@ 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(OnGameStarted) .AddToUnregisterList(_unregisterList); - + this.RegisterEvent(OnPlayerDied) .AddToUnregisterList(_unregisterList); - + this.RegisterEvent(OnLevelCompleted) .AddToUnregisterList(_unregisterList); } - + private void OnGameStarted(GameStartedEvent e) { Console.WriteLine("Game started!"); } - + private void OnPlayerDied(PlayerDiedEvent e) { Console.WriteLine($"Player died at {e.Position}: {e.Cause}"); ShowGameOverScreen(); } - + private void OnLevelCompleted(LevelCompletedEvent e) { Console.WriteLine($"Level {e.LevelId} completed! Score: {e.Score}"); 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,22 +497,22 @@ 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(OnEvent1) .AddToUnregisterList(_unregisterList); - + this.RegisterEvent(OnEvent2) .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(OnGameEvent) - .UnRegisterWhenNodeExitTree(this); -} -``` - ## 最佳实践 1. **事件命名规范** @@ -553,7 +544,7 @@ public override void _Ready() 5. **注销管理** - 始终注销事件监听 - 使用 `IUnRegisterList` 批量管理 - - 利用 Godot 节点生命周期 + - 在适当的生命周期点调用 `Cleanup()` 6. **性能考虑** - 避免频繁触发的事件(如每帧) diff --git a/docs/zh-CN/core/index.md b/docs/zh-CN/core/index.md index 9f54f40..68532a5 100644 --- a/docs/zh-CN/core/index.md +++ b/docs/zh-CN/core/index.md @@ -45,7 +45,7 @@ ┌─────────────────────────────────────────┐ │ View / UI │ UI 层:用户界面 ├─────────────────────────────────────────┤ -│ Controller │ 控制层:处理用户输入 +│ Controller │ 控制层:连接 UI 和业务逻辑 ├─────────────────────────────────────────┤ │ System │ 逻辑层:业务逻辑 ├─────────────────────────────────────────┤ diff --git a/docs/zh-CN/core/ioc.md b/docs/zh-CN/core/ioc.md index d3989d0..4aa53e5 100644 --- a/docs/zh-CN/core/ioc.md +++ b/docs/zh-CN/core/ioc.md @@ -38,17 +38,15 @@ public class IocContainer : ContextAwareBase, IIocContainer ```csharp public void Register(T instance) -public void Register(Type type, object instance) ``` **参数:** - `instance`: 要注册的实例对象 -- `type`: 要注册的类型(重载方法) **特点:** -- 支持泛型和非泛型注册 +- 支持泛型注册 - 自动注册到实例的所有接口类型 - 线程安全操作 - 容器冻结后抛出异常 @@ -62,11 +60,10 @@ var container = new IocContainer(); container.Register(new PlayerModel()); container.Register(new GameSystem()); container.Register(new StorageUtility()); - -// 非泛型注册 -container.Register(typeof(ICustomService), new CustomService()); ``` +**注意:** 非泛型的 `Register(Type, object)` 方法是内部方法 `RegisterInternal`,不作为公开 API 使用。 + ### RegisterSingleton`` 注册单例实例到容器中。一个类型只允许一个实例。 @@ -136,35 +133,36 @@ var system2 = container.Get(); // 返回 gameSystem var system3 = container.Get(); // 返回 gameSystem ``` -### RegisterSystem +### RegisterSystem(Architecture 方法) -注册系统实例,将其绑定到其所有实现的接口上。 +**注意:** `RegisterSystem` 是 `Architecture` 类的方法,不是 `IocContainer` 的方法。它用于在架构中注册系统实例。 -**方法签名:** +**方法签名(在 Architecture 中):** ```csharp -public void RegisterSystem(ISystem system) +public TSystem RegisterSystem(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`` 和 GetAll`` diff --git a/docs/zh-CN/core/model.md b/docs/zh-CN/core/model.md index 71224f5..d2cab7e 100644 --- a/docs/zh-CN/core/model.md +++ b/docs/zh-CN/core/model.md @@ -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 Config { get; } = new(null); + + protected override void OnInit() + { + // 同步初始化逻辑 + } + + public async Task InitializeAsync() + { + // 异步加载配置 + var storage = this.GetUtility(); + 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 接口** ## 相关包 diff --git a/docs/zh-CN/core/query.md b/docs/zh-CN/core/query.md index 78a7a2a..435c379 100644 --- a/docs/zh-CN/core/query.md +++ b/docs/zh-CN/core/query.md @@ -21,27 +21,40 @@ TResult Do(); // 执行查询并返回结果 ## 核心类 -### AbstractQuery`` +### AbstractQuery`` -抽象查询基类,提供了查询的基础实现。 +抽象查询基类,提供了查询的基础实现。通过 `IQueryInput` 接口传递参数。 **核心方法:** ```csharp TResult IQuery.Do(); // 实现 IQuery 接口 -protected abstract TResult OnDo(); // 抽象查询方法,由子类实现 +protected abstract TResult OnDo(TInput input); // 抽象查询方法,接收输入参数 ``` **使用方式:** ```csharp -public abstract class AbstractQuery : ContextAwareBase, IQuery +public abstract class AbstractQuery : ContextAwareBase, IQuery + 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`` + +支持异步执行的查询基类。 + +**核心方法:** + +```csharp +Task IAsyncQuery.DoAsync(); // 实现异步查询接口 +protected abstract Task OnDoAsync(TInput input); // 抽象异步查询方法 +``` + ### EmptyQueryInput 空查询输入类,用于表示不需要任何输入参数的查询操作。 @@ -83,67 +96,76 @@ public sealed class QueryBus : IQueryBus ### 1. 定义查询 ```csharp +// 定义查询输入 +public class GetPlayerGoldInput : IQueryInput { } + // 查询玩家金币数量 -public class GetPlayerGoldQuery : AbstractQuery +public class GetPlayerGoldQuery : AbstractQuery { - protected override int OnDo() + protected override int OnDo(GetPlayerGoldInput input) { return this.GetModel().Gold.Value; } } -// 查询玩家是否死亡 -public class IsPlayerDeadQuery : AbstractQuery +// 定义查询输入 +public class GetItemCountInput : IQueryInput { - protected override bool OnDo() - { - return this.GetModel().Health.Value <= 0; - } + public string ItemId { get; set; } } // 查询背包中指定物品的数量 -public class GetItemCountQuery : AbstractQuery +public class GetItemCountQuery : AbstractQuery { - private readonly string _itemId; - - public GetItemCountQuery(string itemId) - { - _itemId = itemId; - } - - protected override int OnDo() + protected override int OnDo(GetItemCountInput input) { var inventory = this.GetModel(); - return inventory.GetItemCount(_itemId); + return inventory.GetItemCount(input.ItemId); + } +} + +// 定义异步查询输入 +public class LoadPlayerDataInput : IQueryInput +{ + public string PlayerId { get; set; } +} + +// 异步查询玩家数据 +public class LoadPlayerDataQuery : AbstractAsyncQuery +{ + protected override async Task OnDoAsync(LoadPlayerDataInput input) + { + var storage = this.GetUtility(); + 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; } - + 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 +{ + protected override bool OnDo(EmptyQueryInput input) + { + return this.GetModel().Health.Value <= 0; + } +} ``` ``` @@ -184,122 +215,116 @@ public class CombatSystem : AbstractSystem ### 1. 带参数的复杂查询 ```csharp -// 查询指定范围内的敌人列表 -public class GetEnemiesInRangeQuery : AbstractQuery> +// 定义查询输入 +public class GetEnemiesInRangeInput : IQueryInput { - private readonly Vector3 _center; - private readonly float _radius; - - public GetEnemiesInRangeQuery(Vector3 center, float radius) - { - _center = center; - _radius = radius; - } - - protected override List OnDo() + public Vector3 Center { get; set; } + public float Radius { get; set; } +} + +// 查询指定范围内的敌人列表 +public class GetEnemiesInRangeQuery : AbstractQuery> +{ + protected override List OnDo(GetEnemiesInRangeInput input) { var enemySystem = this.GetSystem(); - 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 +// 定义查询输入 +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 +{ + protected override bool OnDo(CanUseSkillInput input) { var playerModel = this.GetModel(); - var skillModel = this.GetModel(); - + // 查询技能消耗 - 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 +public class GetSkillCostInput : IQueryInput { - private readonly string _skillId; - - public GetSkillCostQuery(string skillId) - { - _skillId = skillId; - } - - protected override SkillCost OnDo() - { - return this.GetModel().GetSkillCost(_skillId); - } + public string SkillId { get; set; } } -public class IsSkillOnCooldownQuery : AbstractQuery +public class GetSkillCostQuery : AbstractQuery { - private readonly string _skillId; - - public IsSkillOnCooldownQuery(string skillId) + protected override SkillCost OnDo(GetSkillCostInput input) { - _skillId = skillId; - } - - protected override bool OnDo() - { - return this.GetModel().IsOnCooldown(_skillId); + return this.GetModel().GetSkillCost(input.SkillId); + } +} + +public class IsSkillOnCooldownInput : IQueryInput +{ + public string SkillId { get; set; } +} + +public class IsSkillOnCooldownQuery : AbstractQuery +{ + protected override bool OnDo(IsSkillOnCooldownInput input) + { + return this.GetModel().IsOnCooldown(input.SkillId); } } -``` ``` ### 3. 聚合数据查询 ```csharp // 查询玩家战斗力 -public class GetPlayerPowerQuery : AbstractQuery +public class GetPlayerPowerQuery : AbstractQuery { - protected override int OnDo() + protected override int OnDo(EmptyQueryInput input) { var playerModel = this.GetModel(); var equipmentModel = this.GetModel(); - + 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 +public class GetPlayerInfoQuery : AbstractQuery { - protected override PlayerInfo OnDo() + protected override PlayerInfo OnDo(EmptyQueryInput input) { var playerModel = this.GetModel(); - + return new PlayerInfo { Name = playerModel.Name.Value, 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 // 在 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 的执行机制 diff --git a/docs/zh-CN/core/system.md b/docs/zh-CN/core/system.md index 7ce3691..2b14135 100644 --- a/docs/zh-CN/core/system.md +++ b/docs/zh-CN/core/system.md @@ -185,16 +185,16 @@ 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(); var questSystem = this.GetSystem(); - + // 使用 System combatSystem.StartBattle(); } @@ -223,6 +223,61 @@ public class AISystem : AbstractSystem ## 常见使用模式 +### 异步 System + +System 支持异步初始化,通过实现 `IAsyncInitializable` 接口可以在初始化时执行异步操作。 + +```csharp +// 异步系统示例 +public class DataLoadSystem : AbstractSystem, IAsyncInitializable +{ + private GameData _gameData; + + protected override void OnInit() + { + // 同步初始化逻辑 + this.RegisterEvent(OnGameStarted); + } + + public async Task InitializeAsync() + { + // 异步加载游戏数据 + var storage = this.GetUtility(); + _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 ``` diff --git a/docs/zh-CN/faq.md b/docs/zh-CN/faq.md new file mode 100644 index 0000000..0401105 --- /dev/null +++ b/docs/zh-CN/faq.md @@ -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。 diff --git a/docs/zh-CN/troubleshooting.md b/docs/zh-CN/troubleshooting.md new file mode 100644 index 0000000..64d106c --- /dev/null +++ b/docs/zh-CN/troubleshooting.md @@ -0,0 +1,306 @@ +# 故障排除与调试 + +本指南帮助你诊断和解决 GFramework 使用中的常见问题。 + +## 常见错误 + +### 1. 组件未注册错误 + +**错误信息**:`KeyNotFoundException: 未找到类型为 'XXX' 的组件` + +**原因**:尝试获取未注册的组件。 + +**解决方案**: + +```csharp +// ❌ 错误:未注册 PlayerModel +var arch = new GameArchitecture(); +arch.Initialize(); +var model = arch.GetModel(); // 抛出异常 + +// ✅ 正确:先注册再获取 +public class GameArchitecture : Architecture +{ + protected override void Init() + { + RegisterModel(new PlayerModel()); // 注册模型 + } +} +``` + +### 2. 事件监听器未触发 + +**问题**:注册了事件监听器但没有被调用。 + +**原因**: + +- 事件类型不匹配 +- 监听器在事件发送前注销 +- 事件发送时使用了错误的类型 + +**解决方案**: + +```csharp +// ❌ 错误:事件类型不匹配 +this.RegisterEvent(OnPlayerDied); +this.SendEvent(new PlayerAttackedEvent()); // 不会触发 + +// ✅ 正确:事件类型匹配 +this.RegisterEvent(OnPlayerAttacked); +this.SendEvent(new PlayerAttackedEvent()); // 正确触发 +``` + +### 3. 内存泄漏 + +**问题**:应用内存持续增长。 + +**原因**: + +- 未注销事件监听器 +- 未注销属性监听器 +- 未销毁 Architecture + +**解决方案**: + +```csharp +// ✅ 正确:使用 UnRegisterList 管理注销 +private IUnRegisterList _unregisterList = new UnRegisterList(); + +public void Initialize() +{ + this.RegisterEvent(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.DoSomething(); // 可能导致循环 + } +} + +// ✅ 正确:使用事件解耦 +public class SystemA : AbstractSystem +{ + private void OnEvent(EventA e) + { + this.SendEvent(new EventB()); // 发送事件 + } +} + +public class SystemB : AbstractSystem +{ + protected override void OnInit() + { + this.RegisterEvent(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(e => + Console.WriteLine("Event: PlayerDiedEvent")); + this.RegisterEvent(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(); + + // 打印初始状态 + 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(); // 每帧调用 + model.Health.Value -= 1; +} + +// ✅ 高效 +private PlayerModel _playerModel; + +public void Initialize() +{ + _playerModel = _architecture.GetModel(); // 只调用一次 +} + +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 等)