docs: 删除多个核心模块的文档文件

- 移除 architecture 模块的 README 文档
- 移除 command 模块的 README 文档
- 移除 environment 模块的 README 文档
- 移除 events 模块的 README 文档
- 清理框架核心组件的使用说明文档
This commit is contained in:
GeWuYou 2026-02-11 10:14:44 +08:00 committed by gewuyou
parent 8b49099231
commit 6b48c92710
26 changed files with 94 additions and 9325 deletions

View File

@ -1,521 +1,26 @@
# GFramework.Core.Abstractions 抽象层 # GFramework.Core.Abstractions
> GFramework 框架的接口定义模块,提供所有核心组件的抽象契约 GFramework 框架的抽象层定义模块,包含所有核心组件的接口定义。
## 概述 ## 主要内容
GFramework.Core.Abstractions 是 GFramework 框架的抽象层定义模块包含了框架中所有核心组件的接口Interface、枚举Enum和配置类Class。该模块采用 - 架构核心接口 (IArchitecture, IArchitectureContext等)
`netstandard2.0` 作为目标框架,确保了广泛的兼容性和可移植性。 - 数据模型接口 (IModel)
- 业务系统接口 (ISystem)
- 控制器接口 (IController)
- 命令与查询接口 (ICommand, IQuery)
- 事件系统接口 (IEvent, IEventBus)
- 依赖注入容器接口 (IIocContainer)
- 可绑定属性接口 (IBindableProperty)
- 日志系统接口 (ILogger)
本模块遵循以下设计原则: ## 设计原则
- **接口隔离**:每个接口职责单一,便于实现和测试 - 接口隔离,每个接口职责单一
- **依赖倒置**:上层模块依赖抽象接口,而非具体实现 - 依赖倒置,上层依赖抽象接口
- **组合优于继承**:通过接口组合获得能力,而非通过继承获得 - 类型安全,充分利用泛型系统
- **类型安全**:充分利用泛型系统确保类型安全 - 广泛兼容,基于 netstandard2.0
本模块的包 ID 为 `GeWuYou.GFramework.Core.Abstractions`,遵循命名空间 `GFramework.Core.Abstractions` 下的所有定义。 ## 详细文档
## 目录结构 参见 [docs/core/](../docs/core/) 目录下的详细文档。
```
GFramework.Core.Abstractions/
├── architecture/ # 架构核心接口
│ ├── IArchitecture.cs
│ ├── IArchitectureConfiguration.cs
│ ├── IArchitectureContext.cs
│ ├── IArchitectureLifecycle.cs
│ ├── IArchitectureModule.cs
│ ├── IArchitecturePhaseAware.cs
│ ├── IArchitectureServices.cs
│ └── IAsyncInitializable.cs
├── command/ # 命令模式接口
│ ├── ICommand.cs
│ ├── ICommandBus.cs
│ └── ICommandInput.cs
├── controller/ # 控制器接口
│ └── IController.cs
├── enums/ # 枚举定义
│ └── ArchitecturePhase.cs
├── environment/ # 环境接口
│ └── IEnvironment.cs
├── events/ # 事件系统接口
│ ├── IEasyEvent.cs
│ ├── ITypeEventSystem.cs
│ ├── IUnRegister.cs
│ └── IUnRegisterList.cs
├── ioc/ # 依赖注入容器接口
│ └── IIocContainer.cs
├── logging/ # 日志系统接口
│ ├── ILogger.cs
│ ├── ILoggerFactory.cs
│ ├── ILoggerFactoryProvider.cs
│ └── LogLevel.cs
├── model/ # 模型接口
│ └── IModel.cs
├── properties/ # 配置类
│ ├── ArchitectureProperties.cs
│ └── LoggerProperties.cs
├── property/ # 可绑定属性接口
│ ├── IBindableProperty.cs
│ └── IReadonlyBindableProperty.cs
├── query/ # 查询模式接口
│ ├── IQuery.cs
│ ├── IQueryBus.cs
│ └── IQueryInput.cs
├── rule/ # 规则接口
│ ├── IContextAware.cs
│ └── ILogAware.cs
├── system/ # 系统接口
│ └── ISystem.cs
└── utility/ # 工具接口
├── IContextUtility.cs
└── IUtility.cs
```
## 模块说明
### 1. architecture架构核心
架构模块是整个框架的核心,定义了应用架构的生命周期管理和组件注册机制。该模块包含以下接口:
#### [`IArchitecture`](architecture/IArchitecture.cs)
架构接口是整个应用的核心管理器,负责管理系统、模型和工具类的注册与获取。它继承自 [
`IAsyncInitializable`](architecture/IAsyncInitializable.cs) 接口,支持异步初始化。该接口提供了注册系统([
`RegisterSystem<T>`](architecture/IArchitecture.cs#L39))、注册模型([
`RegisterModel<T>`](architecture/IArchitecture.cs#L46))、注册工具([
`RegisterUtility<T>`](architecture/IArchitecture.cs#L53))的方法,以及安装模块([
`InstallModule`](architecture/IArchitecture.cs#L59))和注册生命周期钩子([
`RegisterLifecycleHook`](architecture/IArchitecture.cs#L65))的功能。
#### [`IArchitectureContext`](architecture/IArchitectureContext.cs)
架构上下文接口提供了对已注册组件的访问能力,是组件之间通信的桥梁。通过该接口,可以获取系统([
`GetSystem<TSystem>`](architecture/IArchitectureContext.cs#L22))、模型([
`GetModel<TModel>`](architecture/IArchitectureContext.cs#L29))和工具([
`GetUtility<TUtility>`](architecture/IArchitectureContext.cs#L36))实例。同时,该接口还支持发送命令([
`SendCommand`](architecture/IArchitectureContext.cs#L42))、发送查询([
`SendQuery`](architecture/IArchitectureContext.cs#L58))和发送事件([
`SendEvent`](architecture/IArchitectureContext.cs#L64))等横切操作。
#### [`IArchitectureConfiguration`](architecture/IArchitectureConfiguration.cs)
架构配置接口定义了框架的配置选项,包括日志配置([`LoggerProperties`](architecture/IArchitectureConfiguration.cs#L13)
)和架构配置([`ArchitectureProperties`](architecture/IArchitectureConfiguration.cs#L18)
)。通过该接口,可以在运行时调整框架的行为,如设置日志级别、启用延迟注册等。
#### [`IArchitectureLifecycle`](architecture/IArchitectureLifecycle.cs)
架构生命周期接口定义了架构在不同阶段的回调方法。当架构进入特定阶段时,会通知所有注册的生命周期监听器。该接口主要用于模块化架构的阶段感知。
#### [`IArchitectureModule`](architecture/IArchitectureModule.cs)
架构模块接口继承自 [`IArchitectureLifecycle`](architecture/IArchitectureLifecycle.cs) 和 [
`IArchitecturePhaseAware`](architecture/IArchitecturePhaseAware.cs) 接口,定义了模块安装到架构的标准方法([
`Install`](architecture/IArchitectureModule.cs#L13))。通过模块化机制,可以将复杂功能封装为可插拔的模块。
#### [`IArchitecturePhaseAware`](architecture/IArchitecturePhaseAware.cs)
架构阶段感知接口允许组件在架构的不同阶段执行相应的逻辑。该接口提供了 [
`OnArchitecturePhase`](architecture/IArchitecturePhaseAware.cs#L14) 方法,当架构进入指定阶段时会被调用。
#### [`IArchitectureServices`](architecture/IArchitectureServices.cs)
架构服务接口定义了框架核心服务组件,继承自 [`IContextAware`](rule/IContextAware.cs) 接口。该接口提供了依赖注入容器([
`Container`](architecture/IArchitectureServices.cs#L18))、类型事件系统([
`TypeEventSystem`](architecture/IArchitectureServices.cs#L24))、命令总线([
`CommandBus`](architecture/IArchitectureServices.cs#L29))和查询总线([
`QueryBus`](architecture/IArchitectureServices.cs#L34))的访问能力。
#### [`IAsyncInitializable`](architecture/IAsyncInitializable.cs)
异步初始化接口定义了组件的异步初始化方法([`InitializeAsync`](architecture/IAsyncInitializable.cs#L14)
)。该接口用于需要执行异步初始化操作的组件,如加载资源、建立网络连接等。
### 2. command命令模式
命令模块实现了命令查询职责分离模式CQRS中的命令部分用于封装写操作。该模块包含以下接口
#### [`ICommand`](command/ICommand.cs)
命令接口定义了无返回值命令的基本契约,继承自 [`IContextAware`](rule/IContextAware.cs) 接口。该接口提供了命令执行方法([
`Execute`](command/ICommand.cs#L15)),用于执行具体的业务逻辑。带返回值的命令由泛型接口 [
`ICommand<TResult>`](command/ICommand.cs#L23) 定义,同样继承自 [`IContextAware`](rule/IContextAware.cs) 接口。
#### [`ICommandBus`](command/ICommandBus.cs)
命令总线接口负责命令的发送和执行调度。该接口提供了发送无返回值命令([`Send`](command/ICommandBus.cs#L12)
)和发送带返回值命令([`Send<TResult>`](command/ICommandBus.cs#L20))的方法。
#### [`ICommandInput`](command/ICommandInput.cs)
命令输入接口是命令模式中输入数据的标记接口,不包含任何成员定义。该接口用于规范化命令的输入参数类型。
### 3. controller控制器
控制器模块定义了表现层与业务逻辑层之间的桥梁接口。
#### [`IController`](controller/IController.cs)
控制器接口是 MVC 架构中控制层的抽象定义。该接口作为标记接口使用,不包含任何方法定义,但实现该接口的类通常会获得访问架构上下文的能力,从而可以发送命令、查询数据、注册事件等。
### 4. enums枚举定义
枚举模块定义了框架中使用的枚举类型。
#### [`ArchitecturePhase`](enums/ArchitecturePhase.cs)
架构阶段枚举定义了系统架构初始化和运行过程中的各个关键阶段。按照初始化流程,依次包括:`None`(无效阶段)、`BeforeUtilityInit`
(工具类初始化之前)、`AfterUtilityInit`(工具类初始化之后)、`BeforeModelInit`(模型初始化之前)、`AfterModelInit`(模型初始化之后)、
`BeforeSystemInit`(系统初始化之前)、`AfterSystemInit`(系统初始化之后)、`Ready`(就绪阶段),以及销毁相关阶段:`Destroying`
(正在销毁中)、`Destroyed`(已销毁)、`FailedInitialization`(初始化失败)。
### 5. environment环境接口
环境模块定义了应用程序运行环境的抽象接口。
#### [`IEnvironment`](environment/IEnvironment.cs)
环境接口提供了获取应用程序运行环境相关信息的能力。该接口支持根据键值获取配置值([
`Get<T>`](environment/IEnvironment.cs#L20))、尝试获取环境值([`TryGet`](environment/IEnvironment.cs#L29)
)、获取必需的环境值([`GetRequired<T>`](environment/IEnvironment.cs#L37)),以及注册键值对([
`Register`](environment/IEnvironment.cs#L44))和初始化环境([`Initialize`](environment/IEnvironment.cs#L49))。
### 6. events事件系统
事件模块实现了框架的事件驱动通信机制,支持类型安全和松耦合的组件通信。
#### [`ITypeEventSystem`](events/ITypeEventSystem.cs)
类型事件系统接口是基于类型的事件发布-订阅机制的抽象定义。该接口支持发送无参事件([
`Send<T>`](events/ITypeEventSystem.cs#L14))、发送带参事件([`Send<T>`](events/ITypeEventSystem.cs#L21))、注册事件监听器([
`Register<T>`](events/ITypeEventSystem.cs#L29))和注销事件监听器([`UnRegister<T>`](events/ITypeEventSystem.cs#L36))。
#### [`IEasyEvent`](events/IEasyEvent.cs)
简单事件接口定义了基础的事件注册功能([`Register`](events/IEasyEvent.cs#L15))。该接口用于简单的无参事件场景。
#### [`IUnRegister`](events/IUnRegister.cs)
注销接口提供了事件监听器的注销功能([`UnRegister`](events/IUnRegister.cs#L11))。所有事件注册方法的返回值都实现了该接口,用于在适当时机取消事件监听。
#### [`IUnRegisterList`](events/IUnRegisterList.cs)
统一注销接口提供了管理多个注销句柄的能力。该接口维护了一个注销对象列表([`UnregisterList`](events/IUnRegisterList.cs#L13)
),可以在批量注销时统一处理。
### 7. ioc依赖注入
IoC 模块实现了控制反转和依赖注入机制,用于管理组件的生命周期和依赖关系。
#### [`IIocContainer`](ioc/IIocContainer.cs)
依赖注入容器接口是框架的核心服务接口之一,继承自 [`IContextAware`](rule/IContextAware.cs)
接口。该接口提供了丰富的服务注册和解析方法,包括注册单例([`RegisterSingleton<T>`](ioc/IIocContainer.cs#L22)
)、注册多个实例([`RegisterPlurality`](ioc/IIocContainer.cs#L30))、注册系统([`RegisterSystem`](ioc/IIocContainer.cs#L36)
)、注册类型([`Register`](ioc/IIocContainer.cs#L43))等。在解析方面,支持获取单个实例([`Get<T>`](ioc/IIocContainer.cs#L62)
)、获取必需实例([`GetRequired<T>`](ioc/IIocContainer.cs#L70))、获取所有实例([`GetAll<T>`](ioc/IIocContainer.cs#L77)
)和获取排序后的实例([`GetAllSorted<T>`](ioc/IIocContainer.cs#L85))。此外,还提供了检查([
`Contains`](ioc/IIocContainer.cs#L96))、清空([`Clear`](ioc/IIocContainer.cs#L108))和冻结([
`Freeze`](ioc/IIocContainer.cs#L113))等实用方法。
### 8. logging日志系统
日志模块提供了完整的日志记录抽象,支持多级别日志输出。
#### [`LogLevel`](logging/LogLevel.cs)
日志级别枚举定义了日志消息的严重程度等级,包括:`Trace`(跟踪级别,用于详细的程序执行流程信息)、`Debug`(调试级别,用于调试过程中的详细信息)、
`Info`(信息级别,用于一般性的程序运行信息)、`Warning`(警告级别,用于表示可能的问题或异常情况)、`Error`
(错误级别,用于表示错误但程序仍可继续运行的情况)、`Fatal`(致命级别,用于表示严重的错误导致程序无法继续运行)。
#### [`ILogger`](logging/ILogger.cs)
日志记录器接口是框架日志系统的核心接口,提供了完整的日志记录能力。该接口支持以下功能:
- **级别启用检查**:提供了各个日志级别的是否启用检查方法,如 [`IsTraceEnabled`](logging/ILogger.cs#L22)、[
`IsDebugEnabled`](logging/ILogger.cs#L28)、[`IsInfoEnabled`](logging/ILogger.cs#L34)、[
`IsWarnEnabled`](logging/ILogger.cs#L40)、[`IsErrorEnabled`](logging/ILogger.cs#L46)、[
`IsFatalEnabled`](logging/ILogger.cs#L52),以及通用的 [`IsEnabledForLevel`](logging/ILogger.cs#L59) 方法。
- **日志记录方法**:每个日志级别都有多种重载形式,支持简单消息、格式化消息和异常记录。格式化为 `Trace``Debug``Info`
`Warn``Error``Fatal` 六个级别,每个级别都提供了 `msg``format+arg``format+arg1+arg2``format+params``msg+exception`
等多种调用方式。
- **获取日志记录器名称**:通过 [`Name`](logging/ILogger.cs#L14) 方法获取日志记录器的名称。
#### [`ILoggerFactory`](logging/ILoggerFactory.cs)
日志工厂接口用于创建日志记录器实例([`GetLogger`](logging/ILoggerFactory.cs#L14))。该接口支持指定日志记录器名称和最小日志级别。
#### [`ILoggerFactoryProvider`](logging/ILoggerFactoryProvider.cs)
日志工厂提供者接口扩展了日志工厂的功能,支持动态设置最小日志级别([`MinLevel`](logging/ILoggerFactoryProvider.cs#L11)
)和创建日志记录器([`CreateLogger`](logging/ILoggerFactoryProvider.cs#L18))。
### 9. model模型接口
模型模块定义了数据层的抽象接口。
#### [`IModel`](model/IModel.cs)
模型接口继承自 [`IContextAware`](rule/IContextAware.cs) 和 [
`IArchitecturePhaseAware`](architecture/IArchitecturePhaseAware.cs)
接口,定义了模型组件的基本行为。该接口提供了模型初始化方法([`Init`](model/IModel.cs#L14)),用于执行模型相关的初始化逻辑。
### 10. properties配置类
配置模块定义了框架使用的配置选项类。
#### [`LoggerProperties`](properties/LoggerProperties.cs)
日志配置选项类用于配置日志系统的相关参数,包含日志工厂提供程序属性([
`LoggerFactoryProvider`](properties/LoggerProperties.cs#L14))。
#### [`ArchitectureProperties`](properties/ArchitectureProperties.cs)
架构选项配置类用于定义架构行为的相关配置选项,包含两个属性:`AllowLateRegistration`(允许延迟注册开关,控制是否允许在初始化完成后进行组件注册)和
`StrictPhaseValidation`(严格阶段验证开关,控制是否启用严格的阶段验证机制)。
### 11. property可绑定属性
可绑定属性模块实现了响应式数据绑定机制,支持数据变化的自动通知。
#### [`IReadonlyBindableProperty<T>`](property/IReadonlyBindableProperty.cs)
只读可绑定属性接口继承自 [`IEasyEvent`](events/IEasyEvent.cs)
接口,提供了属性值的读取和变更监听功能。该接口支持获取当前值([`Value`](property/IReadonlyBindableProperty.cs#L15)
)、注册带初始值的回调([`RegisterWithInitValue`](property/IReadonlyBindableProperty.cs#L22))、取消注册回调([
`UnRegister`](property/IReadonlyBindableProperty.cs#L28))和注册回调([
`Register`](property/IReadonlyBindableProperty.cs#L35))。
#### [`IBindableProperty<T>`](property/IBindableProperty.cs)
可绑定属性接口继承自只读可绑定属性接口,提供了可读写的属性绑定功能。该接口在只读接口的基础上增加了属性值的设置能力([
`Value`](property/IBindableProperty.cs#L12) 的 setter以及不触发事件的设置方法[
`SetValueWithoutEvent`](property/IBindableProperty.cs#L18))。
### 12. query查询模式
查询模块实现了命令查询职责分离模式CQRS中的查询部分用于封装读操作。
#### [`IQuery<TResult>`](query/IQuery.cs)
查询接口继承自 [`IContextAware`](rule/IContextAware.cs) 接口,定义了执行查询操作的契约。该接口提供了查询执行方法([
`Do`](query/IQuery.cs#L15)),返回指定类型的结果。
#### [`IQueryBus`](query/IQueryBus.cs)
查询总线接口负责查询的发送和执行调度。该接口提供了发送查询并返回结果的方法([`Send<TResult>`](query/IQueryBus.cs#L14))。
#### [`IQueryInput`](query/IQueryInput.cs)
查询输入接口是查询模式中输入数据的标记接口,不包含任何成员定义。
### 13. rule规则接口
规则模块定义了框架组件需要遵循的约束和规则接口。
#### [`IContextAware`](rule/IContextAware.cs)
上下文感知接口允许实现类设置和获取架构上下文。该接口提供了设置上下文([`SetContext`](rule/IContextAware.cs#L14)
)和获取上下文([`GetContext`](rule/IContextAware.cs#L20)的方法。框架中大多数核心组件如命令、查询、系统、模型、工具、IoC
容器等)都实现了该接口,以获得访问架构服务的能力。
#### [`ILogAware`](rule/ILogAware.cs)
日志感知接口允许实现类设置和使用日志记录器。该接口提供了设置日志记录器的方法([`SetLogger`](rule/ILogAware.cs#L14))。
### 14. system系统接口
系统模块定义了业务逻辑层的抽象接口。
#### [`ISystem`](system/ISystem.cs)
系统接口继承自 [`IContextAware`](rule/IContextAware.cs) 和 [
`IArchitecturePhaseAware`](architecture/IArchitecturePhaseAware.cs)
接口,定义了系统组件的基本行为。该接口提供了系统初始化方法([`Init`](system/ISystem.cs#L16))和系统销毁方法([
`Destroy`](system/ISystem.cs#L22)),用于管理系统的生命周期。
### 15. utility工具接口
工具模块定义了无状态工具类的抽象接口。
#### [`IUtility`](utility/IUtility.cs)
工具接口是所有工具类实现的基础接口,作为标记接口使用,不包含任何成员定义。该接口定义了通用工具类的基本契约。
#### [`IContextUtility`](utility/IContextUtility.cs)
上下文工具接口继承自 [`IUtility`](utility/IUtility.cs) 和 [`IContextAware`](rule/IContextAware.cs)
接口,提供了具有上下文感知能力的工具功能。该接口在工具接口的基础上增加了初始化方法([
`Init`](utility/IContextUtility.cs#L14))。
## 接口继承关系图
```
IArchitecture
└── IAsyncInitializable
IArchitectureModule
├── IArchitectureLifecycle
└── IArchitecturePhaseAware
IArchitectureServices
└── IContextAware
ICommand
└── IContextAware
ICommand<TResult>
└── IContextAware
IIocContainer
└── IContextAware
IQuery<TResult>
└── IContextAware
IModel
├── IContextAware
└── IArchitecturePhaseAware
ISystem
├── IContextAware
└── IArchitecturePhaseAware
IContextUtility
├── IUtility
└── IContextAware
IReadonlyBindableProperty<T>
└── IEasyEvent
IBindableProperty<T>
└── IReadonlyBindableProperty<T>
```
## 核心能力接口
框架中的组件通过实现特定的接口来获得相应的能力。这些能力接口主要分为以下几类:
### 上下文感知能力
通过实现 [`IContextAware`](rule/IContextAware.cs) 接口组件可以获得设置和获取架构上下文的能力从而访问框架提供的各种服务。命令、查询、系统、模型、工具、IoC
容器等核心组件都实现了该接口。
### 日志能力
通过实现 [`ILogAware`](rule/ILogAware.cs) 接口,组件可以获得使用日志记录器的能力。该接口提供了设置日志记录器的方法,使组件可以输出日志信息。
### 阶段感知能力
通过实现 [`IArchitecturePhaseAware`](architecture/IArchitecturePhaseAware.cs)
接口,组件可以在架构的不同阶段执行相应的逻辑。该接口提供了 [
`OnArchitecturePhase`](architecture/IArchitecturePhaseAware.cs#L14) 方法,当架构进入指定阶段时会被调用。
## 使用指南
### 实现框架组件
当需要实现框架的组件时,通常需要实现相应的接口并遵循框架的生命周期约定:
```csharp
// 实现系统组件
public class CombatSystem : ISystem
{
public void Init()
{
// 系统初始化逻辑
}
public void Destroy()
{
// 系统销毁逻辑
}
}
// 实现模型组件
public class PlayerModel : IModel
{
public void Init()
{
// 模型初始化逻辑
}
}
// 实现工具组件
public class StorageUtility : IUtility
{
// 工具类方法
}
// 实现命令
public class AttackCommand : ICommand
{
public void Execute()
{
// 命令执行逻辑
}
}
// 实现查询
public class GetPlayerInfoQuery : IQuery<PlayerInfo>
{
public PlayerInfo Do()
{
// 查询执行逻辑
}
}
```
### 访问框架服务
通过实现 [`IContextAware`](rule/IContextAware.cs) 接口,组件可以获得访问框架服务的能力:
```csharp
public class MySystem : ISystem
{
private IArchitectureContext _context;
public void SetContext(IArchitectureContext context)
{
_context = context;
}
public IArchitectureContext GetContext()
{
return _context;
}
private void SomeMethod()
{
// 获取其他组件
var playerModel = _context.GetModel<PlayerModel>();
// 发送命令
_context.SendCommand(new AttackCommand());
// 发送查询
var health = _context.SendQuery(new GetHealthQuery());
// 发送事件
_context.SendEvent(new GameEvent());
// 注册事件监听
_context.RegisterEvent<EnemySpawnedEvent>(OnEnemySpawned);
}
}
```
## 相关文档
- [GFramework.Core](../GFramework.Core/README.md) - 核心框架实现模块
- [GFramework.Godot](../GFramework.Godot/README.md) - Godot 平台集成模块
- [架构模块文档](architecture/README.md) - 架构接口详细说明
- [控制器模块文档](controller/README.md) - 控制器使用说明
---
**版本**: 1.0.0
**许可证**: Apache 2.0

View File

@ -1,413 +0,0 @@
# Controller 包使用说明
## 概述
Controller 包定义了控制器Controller的接口规范。控制器是 MVC 架构中的 C 层,负责处理用户交互、协调视图和模型,是连接表现层和业务层的桥梁。
**注意**本框架使用依赖注入模式Controller 通过构造函数或属性注入获取架构实例,而非使用全局单例。
## 核心接口
### [`IController`](IController.cs)
控制器接口,定义了控制器需要实现的所有功能契约。
**继承的能力接口:**
- [`ICanSendCommand`](../command/ICanSendCommand.cs) - 可发送命令
- [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统
- [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型
- [`ICanRegisterEvent`](../events/ICanRegisterEvent.cs) - 可注册事件
- [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具
**能力说明:**
控制器拥有框架中最全面的能力集合,可以:
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/README.md) - 提供架构访问能力
- [`command`](../command/README.md) - 控制器发送命令执行业务逻辑
- [`query`](../query/README.md) - 控制器发送查询获取数据
- [`events`](../events/README.md) - 控制器注册事件监听变化
- [`model`](../model/README.md) - 控制器读取模型数据
- [`system`](../system/README.md) - 控制器调用系统服务
- [`extensions`](../extensions/README.md) - 提供便捷的扩展方法
- **GFramework.Godot** - Godot 特定的控制器扩展
---
**许可证**: Apache 2.0

31
GFramework.Core/README.md Normal file
View File

@ -0,0 +1,31 @@
# GFramework.Core
GFramework 框架的核心模块提供MVC架构的基础设施。
## 主要功能
- **Architecture** - 应用程序架构管理,支持依赖注入、生命周期管理和模块化扩展
- **Model** - 数据模型层,管理应用状态和数据
- **System** - 业务逻辑层,处理核心业务逻辑和事件响应
- **Controller** - 控制器层处理用户输入和UI协调
- **Command** - 命令模式实现,封装用户操作
- **Query** - 查询模式实现支持CQRS架构
- **Events** - 事件系统,实现组件间松耦合通信
- **IoC** - 轻量级依赖注入容器
- **Property** - 可绑定属性,支持数据绑定和响应式编程
- **Utility** - 无状态工具类
- **Pool** - 对象池系统减少GC压力
- **Extensions** - 框架扩展方法
- **Logging** - 日志系统
- **Environment** - 环境配置管理
## 设计原则
- 与平台解耦,不依赖特定游戏引擎
- 接口隔离,职责单一
- 依赖倒置,面向接口编程
- 组合优于继承
## 详细文档
参见 [docs/core/](../docs/core/) 目录下的详细文档。

View File

@ -1,509 +0,0 @@
# Architecture 包使用说明
## 概述
Architecture 包是整个框架的核心,提供了基于 MVC 架构模式的应用程序架构基础。它实现了依赖注入IoC容器、组件生命周期管理以及命令、查询、事件的统一调度机制。
**注意**:本框架的 Core 模块与 Godot 解耦Godot 相关集成在 GFramework.Godot 包中实现。
## 核心接口
### IArchitecture
架构接口,定义了框架的核心功能契约。
**主要职责:**
- 组件注册:注册 System、Model、Utility
- 组件获取:从容器中获取已注册的组件
- 命令处理:发送并执行命令
- 查询处理:发送并执行查询
- 事件管理:发送、注册、注销事件
**核心方法:**
```csharp
// 组件注册
void RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem;
void RegisterModel<TModel>(TModel model) where TModel : IModel;
void RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility;
// 组件获取(通过容器)
T GetModel<T>() where T : class, IModel;
T GetSystem<T>() where T : class, ISystem;
T GetUtility<T>() where T : class, IUtility;
// 命令处理
void SendCommand(ICommand command);
TResult SendCommand<TResult>(ICommand<TResult> command);
// 查询处理
TResult SendQuery<TResult>(IQuery<TResult> query);
// 事件管理
void SendEvent<T>() where T : new();
void SendEvent<T>(T e);
IUnRegister RegisterEvent<T>(Action<T> onEvent);
void UnRegisterEvent<T>(Action<T> onEvent);
```
### IArchitecturePhaseAware
架构阶段感知接口,允许组件监听架构阶段变化。
**核心方法:**
```csharp
void OnArchitecturePhase(ArchitecturePhase phase);
```
### IArchitectureModule
架构模块接口,支持模块化架构扩展。
**核心方法:**
```csharp
void Install(IArchitecture architecture);
```
### IAsyncInitializable
异步初始化接口,支持组件异步初始化。
**核心方法:**
```csharp
Task InitializeAsync();
```
## 核心类
### [`Architecture`](Architecture.cs)
架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。
**构造函数参数:**
```csharp
public abstract class Architecture(
IArchitectureConfiguration? configuration = null,
IEnvironment? environment = null,
IArchitectureServices? services = null,
IArchitectureContext? context = null
)
```
**特性:**
- **阶段式生命周期管理**
支持多个架构阶段BeforeUtilityInit、AfterUtilityInit、BeforeModelInit、AfterModelInit、BeforeSystemInit、AfterSystemInit、Ready、Destroying、Destroyed
- **模块安装系统**:支持通过 `InstallModule` 扩展架构功能
- **异步初始化**:支持同步和异步两种初始化方式
- **IoC 容器集成**:内置依赖注入容器
- **事件系统集成**:集成类型化事件系统
- **与平台无关**Core 模块不依赖 Godot可以在任何 .NET 环境中使用
**架构阶段:**
```csharp
public enum ArchitecturePhase
{
None = 0, // 初始阶段
BeforeUtilityInit = 1, // 工具初始化前
AfterUtilityInit = 2, // 工具初始化后
BeforeModelInit = 3, // 模型初始化前
AfterModelInit = 4, // 模型初始化后
BeforeSystemInit = 5, // 系统初始化前
AfterSystemInit = 6, // 系统初始化后
Ready = 7, // 就绪状态
FailedInitialization = 8, // 初始化失败
Destroying = 9, // 正在销毁
Destroyed = 10 // 已销毁
}
```
**初始化流程:**
1. 创建架构实例(传入配置或使用默认配置)
2. 调用用户自定义的 `Init()` 方法
3. 初始化上下文工具Context Utility
4. 初始化所有注册的 Model
5. 初始化所有注册的 System
6. 冻结 IOC 容器
7. 进入 Ready 阶段
**销毁流程:**
1. 进入 Destroying 阶段
2. 发送 `ArchitectureDestroyingEvent` 事件
3. 销毁所有 System
4. 进入 Destroyed 阶段
5. 发送 `ArchitectureDestroyedEvent` 事件
**使用示例:**
``csharp
// 1. 定义你的架构(继承 Architecture 基类)
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册 Model
RegisterModel(new PlayerModel());
RegisterModel(new InventoryModel());
// 注册 System
RegisterSystem(new GameplaySystem());
RegisterSystem(new SaveSystem());
// 注册 Utility
RegisterUtility(new StorageUtility());
RegisterUtility(new TimeUtility());
}
}
// 2. 创建并初始化架构
var architecture = new GameArchitecture();
architecture.Initialize();
// 或者异步初始化
// var architecture = new GameArchitecture();
// await architecture.InitializeAsync();
// 3. 通过依赖注入使用架构
// 在 Controller 或其他组件中注入架构实例
public class GameController : IController
{
private readonly IArchitecture _architecture;
// 通过构造函数注入架构
public GameController(IArchitecture architecture)
{
_architecture = architecture;
}
public void Start()
{
// 获取 Model
var playerModel = _architecture.GetModel<PlayerModel>();
// 发送命令
_architecture.SendCommand(new StartGameCommand());
// 发送查询
var score = _architecture.SendQuery(new GetScoreQuery());
// 注册事件
_architecture.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
}
private void OnPlayerDied(PlayerDiedEvent e)
{
// 处理玩家死亡事件
}
}
```
**核心方法与属性:**
### 初始化方法
#### `Initialize()`
同步初始化方法,阻塞当前线程直到初始化完成。
```csharp
public void Initialize()
```
**使用示例:**
```csharp
var architecture = new GameArchitecture();
architecture.Initialize(); // 阻塞等待初始化完成
```
**异常处理:**
如果初始化过程中发生异常,架构会进入 `FailedInitialization` 阶段并发送 `ArchitectureFailedInitializationEvent` 事件。
#### `InitializeAsync()`
异步初始化方法,返回 Task 以便调用者可以等待初始化完成。
```csharp
public async Task InitializeAsync()
```
**使用示例:**
```csharp
var architecture = new GameArchitecture();
await architecture.InitializeAsync(); // 异步等待初始化完成
```
**优势:**
- 支持异步初始化 Model 和 System
- 可以利用异步 I/O 操作(如异步加载数据)
- 提高初始化性能
### 模块管理
#### `InstallModule(IArchitectureModule module)`
安装架构模块,用于扩展架构功能。
```csharp
public void InstallModule(IArchitectureModule module)
```
**参数:**
- `module`:要安装的模块实例
**使用示例:**
```csharp
// 定义模块
public class NetworkModule : IArchitectureModule
{
public void Install(IArchitecture architecture)
{
// 注册网络相关的 System 和 Utility
architecture.RegisterSystem(new NetworkSystem());
architecture.RegisterUtility(new HttpClientUtility());
}
}
// 安装模块
var architecture = new GameArchitecture();
architecture.InstallModule(new NetworkModule());
```
### 生命周期钩子
#### `RegisterLifecycleHook(IArchitectureLifecycle hook)`
注册生命周期钩子,用于在架构阶段变化时执行自定义逻辑。
```csharp
public void RegisterLifecycleHook(IArchitectureLifecycle hook)
```
**参数:**
- `hook`:生命周期钩子实例
**使用示例:**
```csharp
// 定义生命周期钩子
public class PerformanceMonitorHook : IArchitectureLifecycle
{
public void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
{
switch (phase)
{
case ArchitecturePhase.BeforeModelInit:
Console.WriteLine("开始监控 Model 初始化性能");
break;
case ArchitecturePhase.AfterModelInit:
Console.WriteLine("Model 初始化完成,停止监控");
break;
case ArchitecturePhase.Ready:
Console.WriteLine("架构就绪,开始性能统计");
break;
}
}
}
// 注册生命周期钩子
var architecture = new GameArchitecture();
architecture.RegisterLifecycleHook(new PerformanceMonitorHook());
architecture.Initialize();
```
**注意:** 生命周期钩子只能在架构进入 Ready 阶段之前注册。
### 属性
#### `CurrentPhase`
获取当前架构的阶段。
```csharp
public ArchitecturePhase CurrentPhase { get; }
```
**使用示例:**
```csharp
var architecture = new GameArchitecture();
// 检查架构是否已就绪
if (architecture.CurrentPhase == ArchitecturePhase.Ready)
{
Console.WriteLine("架构已就绪,可以开始游戏");
}
// 在异步操作中检查阶段变化
await Task.Run(async () =>
{
while (architecture.CurrentPhase != ArchitecturePhase.Ready)
{
Console.WriteLine($"当前阶段: {architecture.CurrentPhase}");
await Task.Delay(100);
}
});
```
#### `Context`
获取架构上下文,提供对架构服务的访问。
```csharp
public IArchitectureContext Context { get; }
```
**使用示例:**
```csharp
// 通过 Context 访问服务
var context = architecture.Context;
var eventBus = context.EventBus;
var commandBus = context.CommandBus;
var queryBus = context.QueryBus;
var environment = context.Environment;
```
**高级特性:**
``csharp
// 1. 使用自定义配置
var config = new ArchitectureConfiguration();
var architecture = new GameArchitecture(configuration: config);
// 2. 模块安装
var module = new GameModule();
architecture.InstallModule(module);
// 3. 监听架构阶段变化
public class GamePhaseListener : IArchitecturePhaseAware
{
public void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.Ready:
GD.Print("架构已就绪,可以开始游戏了");
break;
case ArchitecturePhase.Destroying:
GD.Print("架构正在销毁");
break;
}
}
}
// 4. 生命周期钩子
public class LifecycleHook : IArchitectureLifecycle
{
public void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
{
GD.Print($"架构阶段变化: {phase}");
}
}
```
### [`ArchitectureConfiguration`](ArchitectureConfiguration.cs)
架构配置类,用于配置架构的行为。
**使用示例:**
``csharp
var config = new ArchitectureConfiguration
{
// 严格阶段验证
StrictPhaseValidation = true,
// 允许延迟注册
AllowLateRegistration = false
};
var architecture = new GameArchitecture(configuration: config);
```
### [`ArchitectureServices`](ArchitectureServices.cs)
架构服务类管理命令总线、查询总线、IOC容器和类型事件系统。
### [`ArchitectureContext`](ArchitectureContext.cs)
架构上下文类,提供对架构服务的访问。
### [`GameContext`](GameContext.cs)
游戏上下文类,管理架构上下文与类型的绑定关系。
## 设计模式
### 1. 依赖注入
通过构造函数注入或容器解析获取架构实例。
### 2. 依赖注入IoC
使用内置 IoC 容器管理组件生命周期和依赖关系。
### 3. 命令模式
通过 `ICommand` 封装所有用户操作。
### 4. 查询模式CQRS
通过 `IQuery<T>` 分离查询和命令操作。
### 5. 观察者模式
通过事件系统实现组件间的松耦合通信。
### 6. 阶段式生命周期管理
通过 `ArchitecturePhase` 枚举和生命周期钩子管理架构状态。
### 7. 组合优于继承
通过接口组合获得不同能力,而不是深层继承链。
## 最佳实践
1. **保持架构类简洁**:只在 `Init()` 中注册组件,不要包含业务逻辑
2. **合理划分职责**
- Model数据和状态
- System业务逻辑和规则
- Utility无状态的工具方法
3. **使用依赖注入**:通过构造函数注入架构实例,便于测试
4. **事件命名规范**:使用过去式命名事件类,如 `PlayerDiedEvent`
5. **避免循环依赖**System 不应直接引用 System应通过事件通信
6. **使用模块扩展**:通过 `IArchitectureModule` 实现架构的可扩展性
7. **Core 模块与平台解耦**GFramework.Core 不包含 Godot 相关代码Godot 集成在单独模块中
## 相关包
- [`command`](../command/README.md) - 命令模式实现
- [`query`](../query/README.md) - 查询模式实现
- [`events`](../events/README.md) - 事件系统
- [`ioc`](../ioc/README.md) - IoC 容器
- [`model`](../model/README.md) - 数据模型
- [`system`](../system/README.md) - 业务系统
- [`utility`](../utility/README.md) - 工具类
- **GFramework.Godot** - Godot 特定集成GodotNode 扩展、GodotLogger 等)
---
**许可证**: Apache 2.0

View File

@ -1,309 +0,0 @@
# Command 包使用说明
## 概述
Command 包实现了命令模式Command Pattern用于封装用户操作和业务逻辑。通过命令模式可以将请求封装为对象实现操作的参数化、队列化、日志记录、撤销等功能。
## 核心接口
### 1. [`ICommand`](ICommand.cs)
无返回值命令接口,定义了命令的基本契约。
**核心方法:**
```csharp
void Execute(); // 执行命令
```
### 2. [`ICommand<TResult>`](ICommand.cs)
带返回值的命令接口,用于需要返回执行结果的命令。
**核心方法:**
```csharp
TResult Execute(); // 执行命令并返回结果
```
## 核心类
### 1. [`AbstractCommand<TInput>`](AbstractCommand.cs)
无返回值命令的抽象基类,提供了命令的基础实现。它继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)
,具有上下文感知能力。
**使用示例:**
```csharp
// 定义一个命令输入参数
public struct StartGameCommandInput : ICommandInput
{
public int LevelId { get; set; }
public string PlayerName { get; set; }
}
// 定义一个开始游戏的命令
public class StartGameCommand : AbstractCommand<StartGameCommandInput>
{
public StartGameCommand(StartGameCommandInput input) : base(input)
{
}
protected override void OnExecute(StartGameCommandInput input)
{
// 获取需要的模型
var playerModel = this.GetModel<PlayerModel>();
var gameModel = this.GetModel<GameModel>();
// 执行业务逻辑
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()
{
// 发送命令实例
this.SendCommand(new StartGameCommand(new StartGameCommandInput
{
LevelId = 1,
PlayerName = "Player1"
}));
}
}
```
### 2. [`AbstractCommand<TInput, TResult>`](AbstractCommand.cs)
带返回值命令的抽象基类,同样继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)。
**使用示例:**
```csharp
// 定义一个计算伤害的命令输入
public struct CalculateDamageCommandInput : ICommandInput
{
public int AttackerAttackPower { get; set; }
public int DefenderDefense { get; set; }
}
// 定义一个计算伤害的命令
public class CalculateDamageCommand : AbstractCommand<CalculateDamageCommandInput, int>
{
public CalculateDamageCommand(CalculateDamageCommandInput input) : base(input)
{
}
protected override int OnExecute(CalculateDamageCommandInput 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 damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput
{
AttackerAttackPower = attacker.AttackPower,
DefenderDefense = defender.Defense
}));
// 应用伤害
defender.Health -= damage;
// 发送伤害事件
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
}
}
```
## 命令的生命周期
1. **创建命令**:实例化命令对象,传入必要的参数
2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
3. **返回结果**:对于带返回值的命令,返回执行结果
4. **命令销毁**:命令执行完毕后可以被垃圾回收
## CommandBus - 命令总线
### 功能说明
[CommandBus](file:///d:/Project/Rider/GFramework/GFramework.Core/command/CommandBus.cs#L8-L34) 是命令执行的核心组件,负责发送和执行命令。
**主要方法:**
```csharp
void Send(ICommand command); // 发送无返回值命令
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令
```
### 使用示例
```csharp
var commandBus = new CommandBus();
// 发送无返回值命令
commandBus.Send(new StartGameCommand(new StartGameCommandInput()));
// 发送带返回值命令
var result = commandBus.Send(new CalculateDamageCommand(new CalculateDamageCommandInput()));
```
## EmptyCommandInput - 空命令输入
当命令不需要输入参数时,可以使用 `EmptyCommandInput` 类:
```csharp
public class SimpleActionCommand : AbstractCommand<EmptyCommandInput>
{
public SimpleActionCommand(EmptyCommandInput input) : base(input)
{
}
protected override void OnExecute(EmptyCommandInput input)
{
// 执行简单操作,无需额外参数
this.SendEvent(new SimpleActionEvent());
}
}
```
## 使用场景
### 1. 用户交互操作
```csharp
public struct SaveGameCommandInput : ICommandInput
{
public string SaveSlot { get; set; }
}
public class SaveGameCommand : AbstractCommand<SaveGameCommandInput>
{
public SaveGameCommand(SaveGameCommandInput input) : base(input)
{
}
protected override void OnExecute(SaveGameCommandInput input)
{
var saveSystem = this.GetSystem<SaveSystem>();
var playerModel = this.GetModel<PlayerModel>();
saveSystem.SavePlayerData(playerModel, input.SaveSlot);
this.SendEvent(new GameSavedEvent(input.SaveSlot));
}
}
```
### 2. 业务流程控制
```csharp
public struct LoadLevelCommandInput : ICommandInput
{
public int LevelId { get; set; }
}
public class LoadLevelCommand : AbstractCommand<LoadLevelCommandInput>
{
public LoadLevelCommand(LoadLevelCommandInput input) : base(input)
{
}
protected override void OnExecute(LoadLevelCommandInput input)
{
var levelSystem = this.GetSystem<LevelSystem>();
var uiSystem = this.GetSystem<UISystem>();
// 显示加载界面
uiSystem.ShowLoadingScreen();
// 加载关卡
levelSystem.LoadLevel(input.LevelId);
// 发送事件
this.SendEvent(new LevelLoadedEvent(input.LevelId));
}
}
```
## 最佳实践
1. **保持命令原子性**:一个命令应该完成一个完整的业务操作
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
5. **合理使用返回值**:只在确实需要返回结果时使用 `AbstractCommand<TInput, TResult>`
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand`
7. **输入参数结构化**:使用 `ICommandInput` 接口的实现类来组织命令参数
## 扩展功能
### 命令撤销/重做(可扩展)
```csharp
public struct MoveCommandInput : ICommandInput
{
public Vector3 NewPosition { get; set; }
}
// 实现可撤销命令
public class MoveCommand : AbstractCommand<MoveCommandInput>, IUndoableCommand
{
private Vector3 _oldPosition;
private Vector3 _newPosition;
public MoveCommand(MoveCommandInput input) : base(input)
{
_newPosition = input.NewPosition;
}
protected override void OnExecute(MoveCommandInput input)
{
var player = this.GetModel<PlayerModel>();
_oldPosition = player.Position;
player.Position = input.NewPosition;
}
public void Undo()
{
var player = this.GetModel<PlayerModel>();
player.Position = _oldPosition;
}
}
```
## 相关包
- [`architecture`](../architecture/README.md) - 架构核心,负责命令的分发和执行
- [`extensions`](../extensions/README.md) - 提供 `SendCommand()` 扩展方法
- [`query`](../query/README.md) - 查询模式,用于数据查询
- [`events`](../events/README.md) - 事件系统,命令执行后的通知机制
- [`system`](../system/README.md) - 业务系统,命令的主要执行者
- [`model`](../model/README.md) - 数据模型,命令操作的数据
---
**许可证**: Apache 2.0

View File

@ -1,218 +0,0 @@
# Environment 包使用说明
## 概述
Environment 包定义了环境配置功能。Environment 提供了一个键值对存储系统,用于在运行时存储和获取各种环境特定的值,如配置选项、路径设置等。它允许应用程序在不同环境下灵活调整行为。
## 核心接口
### IEnvironment
环境接口,定义了环境值的存储和获取功能。
**核心成员:**
```csharp
string Name { get; } // 环境名称
T? Get<T>(string key) where T : class; // 根据键获取值
bool TryGet<T>(string key, out T value) where T : class; // 尝试获取值
T GetRequired<T>(string key) where T : class; // 获取必需值(不存在则抛异常)
void Register(string key, object value); // 注册键值对
void Initialize(); // 初始化环境
```
## 核心类
### [`EnvironmentBase`](EnvironmentBase.cs)
环境基础抽象类,实现了 IEnvironment 接口,提供环境值的存储和获取功能。
**使用方式:**
```csharp
public abstract class EnvironmentBase : ContextAwareBase, IEnvironment
{
protected readonly Dictionary<string, object> Values = new(); // 存储环境值
public abstract string Name { get; } // 环境名称
public virtual T? Get<T>(string key) where T : class
{
return TryGet<T>(key, out var value) ? value : null;
}
public virtual bool TryGet<T>(string key, out T value) where T : class
{
if (Values.TryGetValue(key, out var obj) && obj is T typed)
{
value = typed;
return true;
}
value = null!;
return false;
}
public virtual T GetRequired<T>(string key) where T : class
{
if (TryGet<T>(key, out var value))
return value;
throw new InvalidOperationException(
$"Environment [{Name}] missing required value: key='{key}', type='{typeof(T).Name}'");
}
void IEnvironment.Register(string key, object value);
protected void Register(string key, object value); // 注册键值对
public abstract void Initialize(); // 初始化环境
}
```
### [`DefaultEnvironment`](DefaultEnvironment.cs)
默认环境实现类,继承自 EnvironmentBase。
**使用方式:**
```csharp
public class DefaultEnvironment : EnvironmentBase
{
public override string Name { get; } = "Default"; // 环境名称
public override void Initialize()
{
// 默认环境初始化逻辑
}
}
```
## 基本使用
### 1. 定义自定义环境
```csharp
public class GameEnvironment : EnvironmentBase
{
public override string Name { get; } = "Game";
public override void Initialize()
{
// 注册一些环境特定的值
Register("GameMode", "Survival");
Register("MaxPlayers", 4);
Register("ServerAddress", "localhost");
}
// 便捷方法
public string GameMode => Get<string>("GameMode") ?? "Default";
public int MaxPlayers => Get<int>("MaxPlayers") ?? 1;
public string ServerAddress => Get<string>("ServerAddress") ?? "localhost";
}
```
### 2. 在架构中使用环境
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 环境已经在架构初始化过程中自动初始化
// 但是我们可以在需要的时候获取环境值
var gameMode = this.Context.Environment.Get<string>("GameMode");
// 注册模型和系统
RegisterModel(new PlayerModel());
RegisterSystem(new GameplaySystem());
}
}
```
### 3. 使用环境值
```csharp
public class NetworkSystem : AbstractSystem
{
private string _serverAddress;
protected override void OnInit()
{
// 从环境中获取服务器地址
_serverAddress = this.GetContext().Environment.Get<string>("ServerAddress") ?? "localhost";
// 注册事件
this.RegisterEvent<ConnectToServerEvent>(OnConnectToServer);
}
private void OnConnectToServer(ConnectToServerEvent e)
{
// 使用环境中的服务器地址
Connect(_serverAddress, e.Port);
}
private void Connect(string address, int port)
{
// 连接逻辑
}
}
```
## Environment vs Configuration
### Environment环境
- **动态性**:运行时可以更改
- **灵活性**:根据部署环境调整行为
- **存储类型**:运行时值、连接信息等
- **访问方式**:通过键值对访问
### Configuration配置
- **静态性**:通常在启动时确定
- **持久性**:保存在文件或外部源
- **存储类型**:应用设置、参数等
- **访问方式**:通过配置对象访问
## 最佳实践
1. **明确环境名称**:为每个环境提供有意义的名称
2. **类型安全**:使用泛型方法确保类型安全
3. **错误处理**:使用 GetRequired 方法获取必需值
4. **初始化时机**:在架构初始化期间完成环境设置
5. **避免过度使用**:仅存储环境相关的值
## 错误示例
```csharp
// ❌ 错误:获取必需值但不存在
public class BadExampleSystem : AbstractSystem
{
protected override void OnInit()
{
// 如果"RequiredValue"不存在,这会抛出异常
var value = this.GetContext().Environment.GetRequired<string>("RequiredValue");
}
}
// ✅ 正确:安全获取值
public class GoodExampleSystem : AbstractSystem
{
protected override void OnInit()
{
// 使用 TryGet 安全获取值
if (this.GetContext().Environment.TryGet<string>("OptionalValue", out var value))
{
// 处理值
}
// 或者提供默认值
var gameMode = this.GetContext().Environment.Get<string>("GameMode") ?? "Default";
}
}
```
## 相关包
- [`architecture`](../architecture/README.md) - 在架构中使用环境配置
- [`rule`](../rule/README.md) - 环境基类继承自 ContextAwareBase
- [`ioc`](../ioc/README.md) - 环境值可通过IoC容器管理

View File

@ -1,524 +0,0 @@
# Events 包使用说明
## 概述
Events 包提供了一套完整的事件系统实现了观察者模式Observer Pattern。通过事件系统可以实现组件间的松耦合通信支持无参和带参事件、事件注册/注销、以及灵活的事件组合。
## 核心接口
### 1. [`IEvent`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEvent.cs#L7-L11)
基础事件接口,定义了事件注册的基本功能。
**核心方法:**
```csharp
IUnRegister Register(Action onEvent); // 注册事件处理函数
```
### 2. [`IUnRegister`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegister.cs#L6-L10)
注销接口,用于取消事件注册。
**核心方法:**
```csharp
void UnRegister(); // 执行注销操作
```
### 3. [
`IUnRegisterList`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegisterList.cs#L6-L10)
注销列表接口,用于批量管理注销对象。
**属性:**
```csharp
IList<IUnRegister> UnregisterList { get; } // 获取注销列表
```
### 4. [`IEventBus`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEventBus.cs#L6-L22)
事件总线接口,提供基于类型的事件发送和注册。
**核心方法:**
```csharp
IUnRegister Register<T>(Action<T> onEvent); // 注册类型化事件
void Send<T>(T e); // 发送事件实例
void Send<T>() where T : new(); // 发送事件(自动创建实例)
```
## 核心类
### 1. [`EasyEvent`](EasyEvent.cs)
无参事件类,支持注册、注销和触发无参事件。
**使用示例:**
```csharp
// 创建事件
var onClicked = new EasyEvent();
// 注册监听
var unregister = onClicked.Register(() =>
{
GD.Print("Button clicked!");
});
// 触发事件
onClicked.Trigger();
// 取消注册
unregister.UnRegister();
```
### 2. [`Event<T>`](EasyEventGeneric.cs)
单参数泛型事件类,支持一个参数的事件。
**使用示例:**
```csharp
// 创建带参数的事件
var onScoreChanged = new Event<int>();
// 注册监听
onScoreChanged.Register(newScore =>
{
GD.Print($"Score changed to: {newScore}");
});
// 触发事件并传递参数
onScoreChanged.Trigger(100);
```
### 3. [`Event<T, TK>`](EasyEventGeneric.cs)
双参数泛型事件类。
**使用示例:**
```csharp
// 伤害事件:攻击者、伤害值
var onDamageDealt = new Event<string, int>();
onDamageDealt.Register((attacker, damage) =>
{
GD.Print($"{attacker} dealt {damage} damage!");
});
onDamageDealt.Trigger("Player", 50);
```
### 4. [`EasyEvents`](EasyEvents.cs)
全局事件管理器,提供类型安全的事件注册和获取。
**使用示例:**
```csharp
// 注册全局事件类型
EasyEvents.Register<GameStartEvent>();
// 获取事件实例
var gameStartEvent = EasyEvents.Get<GameStartEvent>();
// 注册监听
gameStartEvent.Register(() =>
{
GD.Print("Game started!");
});
// 触发事件
gameStartEvent.Trigger();
```
### 5. [`EventBus`](EventBus.cs)
类型化事件系统,支持基于类型的事件发送和注册。
**使用示例:**
```csharp
// 使用全局事件系统
var eventBus = new EventBus();
// 注册类型化事件
eventBus.Register<PlayerDiedEvent>(e =>
{
GD.Print($"Player died at position: {e.Position}");
});
// 发送事件(传递实例)
eventBus.Send(new PlayerDiedEvent
{
Position = new Vector3(10, 0, 5)
});
// 发送事件(自动创建实例)
eventBus.Send<PlayerDiedEvent>();
```
### 6. [`DefaultUnRegister`](DefaultUnRegister.cs)
默认注销器实现,封装注销回调。
**使用示例:**
```csharp
Action onUnregister = () => GD.Print("Unregistered");
var unregister = new DefaultUnRegister(onUnregister);
// 执行注销
unregister.UnRegister();
```
### 7. [`OrEvent`](OrEvent.cs)
事件或运算组合器,当任意一个事件触发时触发。
**使用示例:**
```csharp
var onAnyInput = new OrEvent()
.Or(onKeyPressed)
.Or(onMouseClicked)
.Or(onTouchDetected);
// 当上述任意事件触发时,执行回调
onAnyInput.Register(() =>
{
GD.Print("Input detected!");
});
```
### 8. [`UnRegisterList`](UnRegisterList.cs)
批量管理注销对象的列表。
**使用示例:**
```csharp
var unregisterList = new UnRegisterList();
// 添加到列表
someEvent.Register(OnEvent).AddToUnregisterList(unregisterList);
// 批量注销
unregisterList.UnRegisterAll();
```
### 9. [`ArchitectureEvents`](ArchitectureEvents.cs)
定义了架构生命周期相关的事件。
**包含事件:**
- `ArchitectureLifecycleReadyEvent` - 架构生命周期准备就绪
- `ArchitectureDestroyingEvent` - 架构销毁中
- `ArchitectureDestroyedEvent` - 架构已销毁
- `ArchitectureFailedInitializationEvent` - 架构初始化失败
## 在架构中使用事件
### 定义事件类
```csharp
// 简单事件
public struct GameStartedEvent { }
// 带数据的事件
public struct PlayerDiedEvent
{
public Vector3 Position;
public string Cause;
}
// 复杂事件
public struct LevelCompletedEvent
{
public int LevelId;
public float CompletionTime;
public int Score;
public List<string> Achievements;
}
```
### Model 中发送事件
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
protected override void OnInit()
{
// 监听生命值变化
Health.Register(newHealth =>
{
if (newHealth <= 0)
{
// 发送玩家死亡事件
this.SendEvent(new PlayerDiedEvent
{
Position = Position,
Cause = "Health depleted"
});
}
});
}
}
```
### System 中发送事件
```csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit() { }
public void DealDamage(Character attacker, Character target, int damage)
{
target.Health -= damage;
// 发送伤害事件
this.SendEvent(new DamageDealtEvent
{
Attacker = attacker.Name,
Target = target.Name,
Damage = damage
});
}
}
```
### Controller 中注册事件
```csharp
public partial class GameController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 注册多个事件
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted)
.AddToUnregisterList(_unregisterList);
}
private void OnGameStarted(GameStartedEvent e)
{
GD.Print("Game started!");
}
private void OnPlayerDied(PlayerDiedEvent e)
{
GD.Print($"Player died at {e.Position}: {e.Cause}");
ShowGameOverScreen();
}
private void OnLevelCompleted(LevelCompletedEvent e)
{
GD.Print($"Level {e.LevelId} completed! Score: {e.Score}");
ShowVictoryScreen(e);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
}
```
## 高级用法
### 1. 事件链式组合
```csharp
// 使用 Or 组合多个事件
var onAnyDamage = new OrEvent()
.Or(onPhysicalDamage)
.Or(onMagicDamage)
.Or(onPoisonDamage);
onAnyDamage.Register(() =>
{
PlayDamageSound();
});
```
### 2. 事件过滤
```csharp
// 只处理高伤害事件
this.RegisterEvent<DamageDealtEvent>(e =>
{
if (e.Damage >= 50)
{
ShowCriticalHitEffect();
}
});
```
### 3. 事件转发
```csharp
public class EventBridge : AbstractSystem
{
protected override void OnInit()
{
// 将内部事件转发为公共事件
this.RegisterEvent<InternalPlayerDiedEvent>(e =>
{
this.SendEvent(new PublicPlayerDiedEvent
{
PlayerId = e.Id,
Timestamp = DateTime.Now
});
});
}
}
```
### 4. 临时事件监听
```csharp
public class TutorialController : Node, IController
{
public override void _Ready()
{
// 只监听一次
IUnRegister unregister = null;
unregister = this.RegisterEvent<FirstEnemyKilledEvent>(e =>
{
ShowTutorialComplete();
unregister?.UnRegister(); // 立即注销
});
}
}
```
### 5. 条件事件
```csharp
public class AchievementSystem : AbstractSystem
{
private int _killCount = 0;
protected override void OnInit()
{
this.RegisterEvent<EnemyKilledEvent>(e =>
{
_killCount++;
// 条件满足时发送成就事件
if (_killCount >= 100)
{
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "kill_100_enemies"
});
}
});
}
}
```
## 生命周期管理
### 使用 UnRegisterList
```csharp
public class MyController : Node, IController
{
// 统一管理所有注销对象
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
{
// 所有注册都添加到列表
this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<Event2>(OnEvent2)
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
// 一次性注销所有
_unregisterList.UnRegisterAll();
}
}
```
### 使用 Godot 节点生命周期
```csharp
public override void _Ready()
{
// 当节点退出场景树时自动注销
this.RegisterEvent<GameEvent>(OnGameEvent)
.UnRegisterWhenNodeExitTree(this);
}
```
## 最佳实践
1. **事件命名规范**
- 使用过去式:`PlayerDiedEvent``LevelCompletedEvent`
- 使用 `Event` 后缀:便于识别
- 使用结构体:减少内存分配
2. **事件数据设计**
- 只包含必要信息
- 使用值类型struct提高性能
- 避免传递可变引用
3. **避免事件循环**
- 事件处理器中谨慎发送新事件
- 使用命令打破循环依赖
4. **合理使用事件**
- 用于通知状态变化
- 用于跨模块通信
- 不用于返回数据(使用 Query
5. **注销管理**
- 始终注销事件监听
- 使用 `IUnRegisterList` 批量管理
- 利用 Godot 节点生命周期
6. **性能考虑**
- 避免频繁触发的事件(如每帧)
- 事件处理器保持轻量
- 使用结构体事件减少 GC
## 事件 vs 其他通信方式
| 方式 | 适用场景 | 优点 | 缺点 |
|----------------------|--------------|-----------|---------|
| **Event** | 状态变化通知、跨模块通信 | 松耦合、一对多 | 难以追踪调用链 |
| **Command** | 执行操作、修改状态 | 封装逻辑、可撤销 | 单向通信 |
| **Query** | 查询数据 | 职责清晰、有返回值 | 同步调用 |
| **BindableProperty** | UI 数据绑定 | 自动更新、响应式 | 仅限单一属性 |
## 相关包
- [`architecture`](../architecture/README.md) - 提供全局事件系统
- [`extensions`](../extensions/README.md) - 提供事件扩展方法
- [`property`](../property/README.md) - 可绑定属性基于事件实现
- [`controller`](../controller/README.md) - 控制器监听事件
- [`model`](../model/README.md) - 模型发送事件
- [`system`](../system/README.md) - 系统发送和监听事件

View File

@ -1,552 +0,0 @@
# Extensions 包使用说明
## 概述
Extensions 包提供了一系列扩展方法,简化了框架各个接口的使用。通过扩展方法,可以用更简洁的语法访问框架功能,提高代码可读性和开发效率。
## 扩展方法类别
### 1. ContextAware 扩展 ([`ContextAwareExtensions.cs`](ContextAwareExtensions.cs))
为 [`IContextAware`](../../GFramework.Core.Abstractions/rule/IContextAware.cs)
提供扩展方法,允许直接从实现了 [IContextAware](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs)
的对象获取架构组件。
#### GetSystem 扩展方法
```csharp
public static TSystem? GetSystem<TSystem>(this IContextAware contextAware)
where TSystem : class, ISystem
```
**使用示例:**
```csharp
// 在实现了 IContextAware 的类中使用
public class PlayerController : IController
{
public void UpdateUI()
{
// 直接通过 this 调用
var playerSystem = this.GetSystem<PlayerSystem>();
var inventorySystem = this.GetSystem<InventorySystem>();
}
}
```
#### GetModel 扩展方法
```csharp
public static TModel? GetModel<TModel>(this IContextAware contextAware)
where TModel : class, IModel
```
**使用示例:**
```csharp
public class PlayerController : IController
{
public void UpdateStats()
{
// 获取模型
var playerModel = this.GetModel<PlayerModel>();
var inventoryModel = this.GetModel<InventoryModel>();
// 使用模型数据
playerModel.Health += 10;
}
}
```
#### GetUtility 扩展方法
```csharp
public static TUtility? GetUtility<TUtility>(this IContextAware contextAware)
where TUtility : class, IUtility
```
**使用示例:**
```csharp
public class GameModel : AbstractModel, IContextAware
{
protected override void OnInit()
{
// 获取工具
var timeUtility = this.GetUtility<TimeUtility>();
var storageUtility = this.GetUtility<StorageUtility>();
}
}
```
#### SendCommand 扩展方法
```csharp
// 发送无返回值的命令
public static void SendCommand(this IContextAware contextAware, ICommand command)
// 发送带返回值的命令
public static TResult SendCommand<TResult>(this IContextAware contextAware, ICommand<TResult> command)
```
**使用示例:**
```csharp
public class GameController : IController
{
public void OnStartButtonClicked()
{
// 发送命令实例
this.SendCommand(new StartGameCommand());
// 发送带返回值的命令
var result = this.SendCommand(new CalculateScoreCommand());
}
}
```
#### SendQuery 扩展方法
```csharp
public static TResult SendQuery<TResult>(this IContextAware contextAware, IQuery<TResult> query)
```
**使用示例:**
```csharp
public class InventoryController : IController
{
public void ShowInventory()
{
// 发送查询获取数据
var items = this.SendQuery(new GetInventoryItemsQuery());
var gold = this.SendQuery(new GetPlayerGoldQuery());
UpdateInventoryUI(items, gold);
}
}
```
#### SendEvent 扩展方法
```csharp
// 发送无参事件
public static void SendEvent<T>(this IContextAware contextAware) where T : new()
// 发送事件实例
public static void SendEvent<T>(this IContextAware contextAware, T e) where T : class
```
**使用示例:**
```csharp
public class PlayerModel : AbstractModel, IContextAware
{
public void TakeDamage(int damage)
{
Health -= damage;
if (Health <= 0)
{
// 方式1发送无参事件
this.SendEvent<PlayerDiedEvent>();
// 方式2发送带数据的事件
this.SendEvent(new PlayerDiedEvent
{
Position = Position,
Cause = "Damage"
});
}
}
}
```
#### RegisterEvent 扩展方法
```csharp
public static IUnRegister RegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> handler)
```
**使用示例:**
```csharp
public class GameController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize()
{
// 注册事件监听
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<PlayerLevelUpEvent>(OnPlayerLevelUp)
.AddToUnregisterList(_unregisterList);
}
private void OnGameStarted(GameStartedEvent e) { }
private void OnPlayerLevelUp(PlayerLevelUpEvent e) { }
}
```
#### UnRegisterEvent 扩展方法
```csharp
public static void UnRegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> onEvent)
```
### GetEnvironment 扩展方法
```csharp
public static T? GetEnvironment<T>(this IContextAware contextAware) where T : class
public static IEnvironment GetEnvironment(this IContextAware contextAware)
```
### 2. Object 扩展 ([`ObjectExtensions.cs`](ObjectExtensions.cs))
提供基于运行时类型判断的对象扩展方法,用于简化类型分支、链式调用和架构分派逻辑。
#### IfType 扩展方法
```csharp
// 最简单的类型判断
public static bool IfType<T>(this object obj, Action<T> action)
// 带条件的类型判断
public static bool IfType<T>(
this object obj,
Func<T, bool> predicate,
Action<T> action
)
// 条件判断,带不匹配时的处理
public static void IfType<T>(
this object obj,
Action<T> whenMatch,
Action<object>? whenNotMatch = null
)
```
**使用示例:**
```csharp
object obj = new MyRule();
// 简单类型判断
bool executed = obj.IfType<MyRule>(rule =>
{
rule.Initialize();
});
// 带条件的类型判断
obj.IfType<MyRule>(
r => r.Enabled, // 条件
r => r.Execute() // 执行动作
);
// 带不匹配处理的类型判断
obj.IfType<IRule>(
rule => rule.Execute(),
other => Logger.Warn($"Unsupported type: {other.GetType()}")
);
```
#### IfType<T, TResult> 扩展方法
```csharp
public static TResult? IfType<T, TResult>(
this object obj,
Func<T, TResult> func
)
```
**使用示例:**
```csharp
object obj = new MyRule { Name = "TestRule" };
string? name = obj.IfType<MyRule, string>(r => r.Name);
```
#### As 和 Do 扩展方法
```csharp
// 安全类型转换
public static T? As<T>(this object obj) where T : class
// 流式调用
public static T Do<T>(this T obj, Action<T> action)
```
**使用示例:**
```csharp
// 安全类型转换
obj.As<MyRule>()
?.Execute();
// 流式调用
obj.As<MyRule>()
?.Do(r => r.Initialize())
?.Do(r => r.Execute());
// 组合使用
obj.As<MyRule>()
?.Do(rule =>
{
if (rule.Enabled)
rule.Execute();
});
```
#### SwitchType 扩展方法
```csharp
public static void SwitchType(
this object obj,
params (Type type, Action<object> action)[] handlers
)
```
**使用示例:**
```csharp
obj.SwitchType(
(typeof(IRule), o => HandleRule((IRule)o)),
(typeof(ISystem), o => HandleSystem((ISystem)o)),
(typeof(IModel), o => HandleModel((IModel)o))
);
```
### 3. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs))
为 [`IEvent`](../../GFramework.Core.Abstractions/events/IEvent.cs) 提供事件组合功能。
#### OrEventExtensions
```csharp
public static OrEvent Or(this IEvent self, IEvent e)
```
**使用示例:**
```csharp
// 组合多个事件:当任意一个触发时执行
var onAnyInput = onKeyPressed.Or(onMouseClicked).Or(onTouchDetected);
onAnyInput.Register(() =>
{
GD.Print("Any input detected!");
});
// 链式组合
var onAnyDamage = onPhysicalDamage
.Or(onMagicDamage)
.Or(onPoisonDamage);
```
### 4. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs))
为 [`IUnRegister`](../events/IUnRegister.cs) 和 [`IUnRegisterList`](../events/IUnRegisterList.cs) 提供批量管理功能。
#### UnRegisterListExtension
```csharp
// 添加到注销列表
public static void AddToUnregisterList(this IUnRegister self,
IUnRegisterList unRegisterList)
// 批量注销
public static void UnRegisterAll(this IUnRegisterList self)
```
**使用示例:**
```csharp
public class ComplexController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize()
{
// 所有注册都添加到列表中
this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<Event2>(OnEvent2)
.AddToUnregisterList(_unregisterList);
this.GetModel<Model1>().Property1.Register(OnProperty1Changed)
.AddToUnregisterList(_unregisterList);
this.GetModel<Model2>().Property2.Register(OnProperty2Changed)
.AddToUnregisterList(_unregisterList);
}
public void Cleanup()
{
// 一次性注销所有
_unregisterList.UnRegisterAll();
}
}
```
## 完整使用示例
### Controller 示例
```csharp
public partial class GameplayController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize()
{
// 使用扩展方法获取 Model
var playerModel = this.GetModel<PlayerModel>();
var gameModel = this.GetModel<GameModel>();
// 使用扩展方法注册事件
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
// 监听可绑定属性
playerModel.Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
}
public void Process(double delta)
{
// 发送命令
this.SendCommand(new AttackCommand(targetId: 1));
// 发送查询
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
if (hasPotion)
{
this.SendCommand<UseHealthPotionCommand>();
}
}
private void OnGameStarted(GameStartedEvent e)
{
Console.WriteLine("Game started!");
}
private void OnHealthChanged(int health)
{
UpdateHealthBar(health);
}
public void Cleanup()
{
_unregisterList.UnRegisterAll();
}
}
```
### Command 示例
```csharp
public class ComplexGameCommand : AbstractCommand
{
protected override void OnExecute()
{
// 获取多个组件
var playerModel = this.GetModel<PlayerModel>();
var gameSystem = this.GetSystem<GameSystem>();
var timeUtility = this.GetUtility<TimeUtility>();
// 执行业务逻辑
var currentTime = timeUtility.GetCurrentTime();
gameSystem.ProcessGameLogic(playerModel, currentTime);
// 发送事件通知
this.SendEvent(new GameStateChangedEvent());
// 可以发送其他命令(谨慎使用)
this.SendCommand<SaveGameCommand>();
}
}
```
### System 示例
```csharp
public class AchievementSystem : AbstractSystem
{
protected override void OnInit()
{
// 注册事件监听
this.RegisterEvent<EnemyKilledEvent>(OnEnemyKilled);
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted);
}
private void OnEnemyKilled(EnemyKilledEvent e)
{
// 获取模型
var playerModel = this.GetModel<PlayerModel>();
playerModel.EnemyKillCount++;
// 检查成就
if (playerModel.EnemyKillCount >= 100)
{
// 发送成就解锁事件
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "kill_100_enemies"
});
}
}
private void OnLevelCompleted(LevelCompletedEvent e)
{
// 发送查询
var completionTime = this.SendQuery(new GetLevelTimeQuery(e.LevelId));
if (completionTime < 60)
{
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "speed_runner"
});
}
}
}
```
## 扩展方法的优势
1. **简洁的语法**
:不需要显式调用 [GetContext()](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L13-L15)
2. **类型安全**:编译时检查类型
3. **可读性高**:代码意图更清晰
4. **智能提示**IDE 可以提供完整的自动补全
5. **链式调用**:支持流式编程风格
## 注意事项
1. **确保引用命名空间:**
```csharp
using GFramework.Core.extensions;
```
2. **理解扩展方法本质:**
- 扩展方法是静态方法的语法糖
- 不会改变原始类型的结构
- 仅在编译时解析
3. **性能考虑:**
- 扩展方法本身无性能开销
- 实际调用的是底层方法
## 相关包
- [`architecture`](../architecture/README.md) - 扩展方法最终调用架构方法
- [`command`](../command/README.md) - 命令发送扩展
- [`query`](../query/README.md) - 查询发送扩展
- [`events`](../events/README.md) - 事件注册和 Or 组合扩展
- [`model`](../model/README.md) - 模型获取扩展
- [`system`](../system/README.md) - 系统获取扩展
- [`utility`](../utility/README.md) - 工具获取扩展

View File

@ -1,689 +0,0 @@
# IoC 包使用说明
## 概述
IoCInversion of Control控制反转包提供了一个轻量级的依赖注入容器用于管理框架中各种组件的注册和获取。通过 IoC
容器,可以实现组件间的解耦,便于测试和维护。
## 核心类
### [`IocContainer`](IocContainer.cs)
IoC 容器类,负责管理对象的注册和获取。
**主要功能:**
- 注册实例到容器
- 从容器中获取实例
- 类型安全的依赖管理
- 线程安全操作
- 容器冻结保护
## 核心方法
### 1. Register<T> 和 Register(Type, object)
注册一个实例到容器中。
```csharp
public void Register<T>(T instance)
public void Register(Type type, object instance)
```
**参数:**
- `instance`: 要注册的实例对象
- `type`: 要注册的类型(重载方法)
**使用示例:**
```csharp
var container = new IocContainer();
// 注册各种类型的实例
container.Register<IPlayerModel>(new PlayerModel());
container.Register<IGameSystem>(new GameSystem());
container.Register<IStorageUtility>(new StorageUtility());
```
### 2. RegisterSingleton<T>
注册单例实例到容器中。一个类型只允许一个实例。
```csharp
public void RegisterSingleton<T>(T instance)
```
**参数:**
- `instance`: 要注册的单例实例
**使用示例:**
```csharp
var container = new IocContainer();
// 注册单例
container.RegisterSingleton<IPlayerModel>(new PlayerModel());
```
### 3. RegisterPlurality
注册多个实例,将实例注册到其实现的所有接口和具体类型上。
```csharp
public void RegisterPlurality(object instance)
```
**参数:**
- `instance`: 要注册的实例
### 4. RegisterSystem
注册系统实例,将其绑定到其所有实现的接口上。
```csharp
public void RegisterSystem(ISystem system)
```
**参数:**
- `system`: 系统实例对象
### 5. Get<T> 和 GetAll<T>
从容器中获取指定类型的实例。
```csharp
public T? Get<T>() where T : class
public IReadOnlyList<T> GetAll<T>() where T : class
```
**返回值:**
- `Get<T>`: 返回指定类型的实例,如果未找到则返回 `null`
- `GetAll<T>`: 返回指定类型的所有实例列表,如果未找到则返回空数组
**使用示例:**
```csharp
// 获取已注册的实例
var playerModel = container.Get<IPlayerModel>();
var gameSystems = container.GetAll<IGameSystem>();
// 如果类型未注册Get 返回 nullGetAll 返回空数组
var unknownService = container.Get<IUnknownService>(); // null
```
### 6. GetRequired<T>
获取指定类型的必需实例,如果没有注册或注册了多个实例会抛出异常。
```csharp
public T GetRequired<T>() where T : class
```
**返回值:**
- 返回找到的唯一实例
### 7. GetAllSorted<T>
获取并排序(系统调度专用)。
```csharp
public IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class
```
**参数:**
- `comparison`: 比较器委托,定义排序规则
**返回值:**
- 按指定方式排序后的实例列表
## 在框架中的使用
### Architecture 中的应用
IoC 容器是 [`Architecture`](../architecture/Architecture.cs) 类的核心组件,用于管理所有的 System、Model 和 Utility。
```csharp
public abstract class Architecture : IArchitecture
{
// 内置 IoC 容器
private readonly IocContainer _mContainer = new();
// 注册系统
public void RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
{
system.SetArchitecture(this);
_mContainer.Register(system); // 注册到容器
// ...
}
// 获取系统
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem
=> _mContainer.Get<TSystem>(); // 从容器获取
// Model 和 Utility 同理
}
```
### 注册组件到容器
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 这些方法内部都使用 IoC 容器
// 注册 Model存储游戏数据
RegisterModel<IPlayerModel>(new PlayerModel());
RegisterModel<IInventoryModel>(new InventoryModel());
// 注册 System业务逻辑
RegisterSystem<IGameplaySystem>(new GameplaySystem());
RegisterSystem<ISaveSystem>(new SaveSystem());
// 注册 Utility工具类
RegisterUtility<ITimeUtility>(new TimeUtility());
RegisterUtility<IStorageUtility>(new StorageUtility());
}
}
```
### 从容器获取组件
```csharp
// 通过扩展方法间接使用 IoC 容器
public class PlayerController : IController
{
public void Start()
{
// GetModel 内部调用 Architecture.GetModel
// Architecture.GetModel 内部调用 IocContainer.Get
var playerModel = this.GetModel<IPlayerModel>();
var gameplaySystem = this.GetSystem<IGameplaySystem>();
var timeUtility = this.GetUtility<ITimeUtility>();
}
}
```
## 工作原理
### 内部实现
```csharp
public class IocContainer
{
// 使用字典存储类型到实例集合的映射
private readonly Dictionary<Type, HashSet<object>> _typeIndex = new();
private readonly HashSet<object> _objects = [];
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
private volatile bool _frozen = false;
public void Register<T>(T instance)
{
// 获取写锁以确保线程安全
_lock.EnterWriteLock();
try
{
RegisterInternal(typeof(T), instance!);
}
finally
{
_lock.ExitWriteLock();
}
}
public T? Get<T>() where T : class
{
_lock.EnterReadLock();
try
{
if (_typeIndex.TryGetValue(typeof(T), out var set) && set.Count > 0)
{
var result = set.First() as T;
return result;
}
return null;
}
finally
{
_lock.ExitReadLock();
}
}
private void RegisterInternal(Type type, object instance)
{
if (_frozen) throw new InvalidOperationException("IocContainer is frozen");
_objects.Add(instance);
if (!_typeIndex.TryGetValue(type, out var set))
{
set = [];
_typeIndex[type] = set;
}
set.Add(instance);
}
}
```
### 线程安全机制
容器使用 [ReaderWriterLockSlim](xref:System.Threading.ReaderWriterLockSlim) 来确保线程安全操作,允许多个线程同时读取,但在写入时阻止其他线程访问。
### 注册流程
```
用户代码
RegisterSystem<T>(system)
IocContainer.Register<T>(system)
加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet
```
### 获取流程
```
用户代码
this.GetSystem<T>()
Architecture.GetSystem<T>()
IocContainer.Get<T>()
加读锁 -> Dictionary.TryGetValue(typeof(T)) 获取HashSet
返回HashSet中第一个实例或 null
```
## 使用示例
### 基础使用
```csharp
// 1. 创建容器
var container = new IocContainer();
// 2. 注册服务
var playerService = new PlayerService();
container.Register<IPlayerService>(playerService);
// 3. 获取服务
var service = container.Get<IPlayerService>();
service.DoSomething();
```
### 接口和实现分离
```csharp
// 定义接口
public interface IDataService
{
void SaveData(string data);
string LoadData();
}
// 实现类
public class LocalDataService : IDataService
{
public void SaveData(string data) { /* 本地存储 */ }
public string LoadData() { /* 本地加载 */ return ""; }
}
public class CloudDataService : IDataService
{
public void SaveData(string data) { /* 云端存储 */ }
public string LoadData() { /* 云端加载 */ return ""; }
}
// 注册(可以根据配置选择不同实现)
var container = new IocContainer();
#if CLOUD_SAVE
container.Register<IDataService>(new CloudDataService());
#else
container.Register<IDataService>(new LocalDataService());
#endif
// 使用(不需要关心具体实现)
var dataService = container.Get<IDataService>();
dataService.SaveData("game data");
```
### 注册多个实现
```csharp
var container = new IocContainer();
// 注册多个相同接口的不同实现
container.Register<IDataService>(new LocalDataService());
container.Register<IDataService>(new CloudDataService());
// 获取单个实例(返回第一个)
var singleService = container.Get<IDataService>(); // 返回第一个注册的实例
// 获取所有实例
var allServices = container.GetAll<IDataService>(); // 返回两个实例的列表
```
## 其他实用方法
### `Contains<T>()`
检查容器中是否包含指定类型的实例。
```csharp
public bool Contains<T>() where T : class
```
**参数:**
- 无泛型参数
**返回值:**
- 如果容器中包含指定类型的实例则返回 `true`,否则返回 `false`
**使用示例:**
```csharp
var container = new IocContainer();
// 检查服务是否已注册
if (container.Contains<IPlayerService>())
{
Console.WriteLine("Player service is available");
}
// 根据检查结果决定是否注册
if (!container.Contains<ISettingsService>())
{
container.Register<ISettingsService>(new SettingsService());
}
```
**应用场景:**
- 条件注册服务
- 检查依赖是否可用
- 动态功能开关
### `ContainsInstance(object instance)`
判断容器中是否包含某个具体的实例对象。
```csharp
public bool ContainsInstance(object instance)
```
**参数:**
- `instance`:待查询的实例对象
**返回值:**
- 若容器中包含该实例则返回 `true`,否则返回 `false`
**使用示例:**
```csharp
var container = new IocContainer();
var service = new MyService();
container.Register<IMyService>(service);
// 检查特定实例是否在容器中
if (container.ContainsInstance(service))
{
Console.WriteLine("This instance is registered in the container");
}
// 检查另一个实例
var anotherService = new MyService();
if (!container.ContainsInstance(anotherService))
{
Console.WriteLine("This instance is not in the container");
}
```
**应用场景:**
- 避免重复注册同一实例
- 检查对象是否已被管理
- 调试和日志记录
### `Clear()`
清空容器中的所有实例。
```csharp
public void Clear()
```
**使用示例:**
```csharp
var container = new IocContainer();
// 注册多个服务
container.Register<IService1>(new Service1());
container.Register<IService2>(new Service2());
container.Register<IService3>(new Service3());
// 清空容器
container.Clear();
// 检查是否清空成功
Console.WriteLine($"Contains IService1: {container.Contains<IService1>()}"); // False
Console.WriteLine($"Contains IService2: {container.Contains<IService2>()}"); // False
```
**应用场景:**
- 重置容器状态
- 内存清理
- 测试环境准备
**注意事项:**
- 容器冻结后也可以调用 `Clear()` 方法
- 清空后,所有已注册的实例都将丢失
- 不会自动清理已注册对象的其他引用
## 设计特点
### 1. 简单轻量
- 支持多种注册方式:普通注册、单例注册、多实例注册
- 基于字典和哈希集实现,性能高效
- 无复杂的依赖解析逻辑
### 2. 手动注册
- 需要显式注册每个组件
- 不支持自动依赖注入
- 完全可控的组件生命周期
### 3. 多实例支持
- 每个类型可以注册多个实例
- 提供 [GetAll](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L244-L261) 方法获取所有实例
- 提供 [Get](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L208-L225) 方法获取单个实例
### 4. 类型安全
- 基于泛型,编译时类型检查
- 避免字符串键导致的错误
- IDE 友好,支持自动补全
### 5. 线程安全
- 使用读写锁确保多线程环境下的安全操作
- 读操作可以并发执行
- 写操作独占锁,防止并发修改冲突
### 6. 容器冻结
- 提供 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372) 方法,防止进一步修改容器内容
- 防止在初始化后意外修改注册内容
## 与其他 IoC 容器的区别
### 本框架的 IocContainer
```csharp
// 简单直接
var container = new IocContainer();
container.Register(new MyService());
var service = container.Get<MyService>();
```
**特点:**
- ✅ 简单易用
- ✅ 性能高
- ✅ 线程安全
- ✅ 支持多实例
- ❌ 不支持构造函数注入
- ❌ 不支持自动解析依赖
- ❌ 不支持生命周期管理Transient/Scoped/Singleton
### 完整的 IoC 框架(如 Autofac、Zenject
```csharp
// 复杂但功能强大
var builder = new ContainerBuilder();
builder.RegisterType<MyService>().As<IMyService>().SingleInstance();
builder.RegisterType<MyController>().WithParameter("config", config);
var container = builder.Build();
// 自动解析依赖
var controller = container.Resolve<MyController>();
```
**特点:**
- ✅ 自动依赖注入
- ✅ 生命周期管理
- ✅ 复杂场景支持
- ❌ 学习成本高
- ❌ 性能开销大
## 最佳实践
### 1. 在架构初始化时注册
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 按顺序注册组件
// 1. 工具类(无依赖)
RegisterUtility(new TimeUtility());
RegisterUtility(new StorageUtility());
// 2. 模型(可能依赖工具)
RegisterModel(new PlayerModel());
RegisterModel(new GameModel());
// 3. 系统(可能依赖模型和工具)
RegisterSystem(new GameplaySystem());
RegisterSystem(new SaveSystem());
}
}
```
### 2. 使用接口类型注册
```csharp
// ❌ 不推荐:直接使用实现类
RegisterSystem(new ConcreteSystem());
var system = GetSystem<ConcreteSystem>();
// ✅ 推荐:使用接口
RegisterSystem<IGameSystem>(new ConcreteSystem());
var system = GetSystem<IGameSystem>();
```
### 3. 避免运行时频繁注册
```csharp
// ❌ 不好:游戏运行时频繁注册
void Update()
{
RegisterService(new TempService()); // 每帧创建
}
// ✅ 好:在初始化时一次性注册
protected override void Init()
{
RegisterService(new PersistentService());
}
```
### 4. 检查 null 返回值
```csharp
// 获取可能不存在的服务
var service = container.Get<IOptionalService>();
if (service != null)
{
service.DoSomething();
}
else
{
GD.Print("Service not registered!");
}
```
### 5. 合理使用容器冻结
```csharp
// 在架构初始化完成后冻结容器,防止意外修改
protected override void OnInit()
{
// 注册所有组件
RegisterModel(new PlayerModel());
RegisterSystem(new GameSystem());
// ...
// 冻结容器
Container.Freeze(); // 此后无法再注册新组件
}
```
## 注意事项
1. **线程安全操作**:容器内部使用读写锁确保线程安全,无需额外同步
2. **容器冻结**:一旦调用 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372)
方法,将不能再注册新实例
3. **单例注册限制
**[RegisterSingleton](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L84-L106)
方法确保一个类型只能有一个实例,重复注册会抛出异常
4. **内存管理**:容器持有的实例不会自动释放,需要注意内存泄漏问题
5. **注册顺序**:组件的依赖关系需要手动保证,先注册被依赖的组件
## 相关包
- [`architecture`](../architecture/README.md) - 使用 IoC 容器管理所有组件
- [`model`](../model/README.md) - Model 通过 IoC 容器注册和获取
- [`system`](../system/README.md) - System 通过 IoC 容器注册和获取
- [`utility`](../utility/README.md) - Utility 通过 IoC 容器注册和获取

View File

@ -1,364 +0,0 @@
# Logging 包使用说明
## 概述
Logging 包提供了灵活的日志系统,支持多级别日志记录。默认日志级别为 `Info`,确保框架的关键操作都能被记录下来。
## 核心接口
### [ILogger](ILogger.cs)
日志记录器接口,定义了日志记录的基本功能。
**核心方法:**
```csharp
// 日志级别检查
bool IsTraceEnabled();
bool IsDebugEnabled();
bool IsInfoEnabled();
bool IsWarnEnabled();
bool IsErrorEnabled();
bool IsFatalEnabled();
// 记录日志
void Trace(string msg);
void Trace(string format, object arg);
void Trace(string format, object arg1, object arg2);
void Trace(string format, params object[] arguments);
void Trace(string msg, Exception t);
void Debug(string msg);
void Debug(string format, object arg);
void Debug(string format, object arg1, object arg2);
void Debug(string format, params object[] arguments);
void Debug(string msg, Exception t);
void Info(string msg);
void Info(string format, object arg);
void Info(string format, object arg1, object arg2);
void Info(string format, params object[] arguments);
void Info(string msg, Exception t);
void Warn(string msg);
void Warn(string format, object arg);
void Warn(string format, object arg1, object arg2);
void Warn(string format, params object[] arguments);
void Warn(string msg, Exception t);
void Error(string msg);
void Error(string format, object arg);
void Error(string format, object arg1, object arg2);
void Error(string format, params object[] arguments);
void Error(string msg, Exception t);
void Fatal(string msg);
void Fatal(string format, object arg);
void Fatal(string format, object arg1, object arg2);
void Fatal(string format, params object[] arguments);
void Fatal(string msg, Exception t);
// 获取日志器名称
string Name();
```
### [ILoggerFactory](ILoggerFactory.cs)
日志工厂接口,用于创建日志记录器实例。
**核心方法:**
```csharp
ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info);
```
### [ILoggerFactoryProvider](ILoggerFactoryProvider.cs)
日志工厂提供程序接口,用于获取日志工厂。
**核心方法:**
```csharp
ILoggerFactory GetLoggerFactory();
ILogger CreateLogger(string name);
```
### [LogLevel](LogLevel.cs)
日志级别枚举。
```csharp
public enum LogLevel
{
Trace = 0, // 最详细的跟踪信息
Debug = 1, // 调试信息
Info = 2, // 一般信息(默认级别)
Warning = 3, // 警告信息
Error = 4, // 错误信息
Fatal = 5 // 致命错误
}
```
## 核心类
### [AbstractLogger](AbstractLogger.cs)
抽象日志基类,封装了日志级别判断、格式化与异常处理逻辑。平台日志器只需实现 `Write` 方法即可。
**使用示例:**
```csharp
public class CustomLogger : AbstractLogger
{
public CustomLogger(string? name = null, LogLevel minLevel = LogLevel.Info)
: base(name, minLevel)
{
}
protected override void Write(LogLevel level, string message, Exception? exception)
{
// 自定义日志输出逻辑
var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
if (exception != null)
logMessage += $"\n{exception}";
Console.WriteLine(logMessage);
}
}
```
### [ConsoleLogger](ConsoleLogger.cs)
控制台日志记录器实现,支持彩色输出。
**使用示例:**
```csharp
// 创建控制台日志记录器
var logger = new ConsoleLogger("MyLogger", LogLevel.Debug);
// 记录不同级别的日志
logger.Info("应用程序启动");
logger.Debug("调试信息");
logger.Warn("警告信息");
logger.Error("错误信息");
logger.Fatal("致命错误");
```
**输出格式:**
```
[2025-01-09 01:40:00.000] INFO [MyLogger] 应用程序启动
[2025-01-09 01:40:01.000] DEBUG [MyLogger] 调试信息
[2025-01-09 01:40:02.000] WARN [MyLogger] 警告信息
```
**日志级别颜色:**
- **Trace**: 深灰色
- **Debug**: 青色
- **Info**: 白色
- **Warning**: 黄色
- **Error**: 红色
- **Fatal**: 洋红色
**构造函数参数:**
- `name`:日志器名称,默认为 "ROOT"
- `minLevel`:最低日志级别,默认为 LogLevel.Info
- `writer`TextWriter 输出流,默认为 Console.Out
- `useColors`:是否使用颜色,默认为 true仅在输出到控制台时生效
### [ConsoleLoggerFactory](ConsoleLoggerFactory.cs)
控制台日志工厂,用于创建控制台日志记录器实例。
**使用示例:**
```csharp
var factory = new ConsoleLoggerFactory();
var logger = factory.GetLogger("MyModule", LogLevel.Debug);
logger.Info("日志记录器创建成功");
```
### [ConsoleLoggerFactoryProvider](ConsoleLoggerFactoryProvider.cs)
控制台日志工厂提供程序实现。
**使用示例:**
```csharp
var provider = new ConsoleLoggerFactoryProvider();
provider.MinLevel = LogLevel.Debug; // 设置最低日志级别
var logger = provider.CreateLogger("MyApp");
logger.Info("应用程序启动");
```
### [LoggerFactoryResolver](LoggerFactoryResolver.cs)
日志工厂提供程序解析器,用于管理和提供日志工厂提供程序实例。
**使用示例:**
```csharp
// 设置日志工厂提供程序
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
// 设置最小日志级别
LoggerFactoryResolver.MinLevel = LogLevel.Debug;
// 获取日志记录器
var logger = LoggerFactoryResolver.Provider.CreateLogger("MyApp");
logger.Info("应用程序启动");
```
## 在架构中使用日志
### 1. 在 Architecture 中使用
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
var logger = LoggerFactoryResolver.Provider.CreateLogger("GameArchitecture");
logger.Info("游戏架构初始化开始");
RegisterModel(new PlayerModel());
RegisterSystem(new GameSystem());
logger.Info("游戏架构初始化完成");
}
}
```
### 2. 在 System 中使用
```csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
var logger = LoggerFactoryResolver.Provider.CreateLogger("CombatSystem");
logger.Info("战斗系统初始化完成");
}
protected override void OnDestroy()
{
var logger = LoggerFactoryResolver.Provider.CreateLogger("CombatSystem");
logger.Info("战斗系统已销毁");
}
}
```
### 3. 在 Model 中使用
```csharp
public class PlayerModel : AbstractModel
{
protected override void OnInit()
{
var logger = LoggerFactoryResolver.Provider.CreateLogger("PlayerModel");
logger.Info("玩家模型初始化完成");
}
}
```
### 4. 自定义日志级别
```csharp
public class DebugLogger : AbstractLogger
{
public DebugLogger() : base("Debug", LogLevel.Debug)
{
}
protected override void Write(LogLevel level, string message, Exception? exception)
{
// 只输出调试及更高级别的日志
if (level >= LogLevel.Debug)
{
Console.WriteLine($"[{level}] {message}");
if (exception != null)
Console.WriteLine(exception);
}
}
}
```
## 日志级别说明
| 级别 | 说明 | 使用场景 |
|-------------|----------|-------------------|
| **Trace** | 最详细的跟踪信息 | 调试复杂的执行流程,记录函数调用等 |
| **Debug** | 调试信息 | 开发阶段,记录变量值、流程分支等 |
| **Info** | 一般信息 | 记录重要的业务流程和系统状态 |
| **Warning** | 警告信息 | 可能的问题但不中断程序执行 |
| **Error** | 错误信息 | 影响功能但不致命的问题 |
| **Fatal** | 致命错误 | 导致程序无法继续运行的严重错误 |
## 最佳实践
1. **使用合适的日志级别**
- 使用 `Info` 记录重要业务流程
- 使用 `Debug` 记录调试信息
- 使用 `Warning` 记录异常情况
- 使用 `Error` 记录错误但不影响程序运行
- 使用 `Fatal` 记录严重错误
2. **提供上下文信息**
```csharp
logger.Info($"用户登录成功: UserId={userId}, UserName={userName}");
```
3. **异常日志记录**
```csharp
try
{
// 业务逻辑
}
catch (Exception ex)
{
logger.Error("数据库操作失败", ex);
}
```
4. **分类使用日志**
```csharp
var dbLogger = LoggerFactoryResolver.Provider.CreateLogger("Database");
var netLogger = LoggerFactoryResolver.Provider.CreateLogger("Network");
dbLogger.Info("查询用户数据");
netLogger.Debug("发送HTTP请求");
```
5. **在框架组件中合理使用日志**
```csharp
// 在系统初始化时记录
var logger = LoggerFactoryResolver.Provider.CreateLogger("System");
logger.Info("系统初始化完成");
```
## 注意事项
1. **日志级别检查**
- 每个日志方法都会自动检查日志级别
- 如果当前级别低于最小级别,不会输出日志
2. **格式化参数**
- 支持字符串格式化参数
- 支持异常信息传递
3. **ConsoleLogger 的额外参数**
- ConsoleLogger 现在支持自定义TextWriter输出流
- 支持禁用颜色输出的功能useColors参数
## 相关包
- [architecture](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
- [property](../property/README.md) - 可绑定属性基于事件系统实现
- [extensions](../extensions/README.md) - 提供便捷的扩展方法
---
**许可证**: Apache 2.0

View File

@ -1,184 +0,0 @@
# Model 包使用说明
## 概述
Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的 M 层负责管理应用程序的数据和状态。Model
层应该只包含数据和简单的数据逻辑,不包含复杂的业务逻辑。
## 核心接口
### [`IModel`](IModel.cs)
模型接口,定义了模型的基本行为和功能。
**继承的能力接口:**
- [`IContextAware`](../rule/IContextAware.cs) - 上下文感知接口
- [`ILogAware`](../rule/ILogAware.cs) - 日志感知接口
**核心方法:**
```csharp
void Init(); // 初始化模型
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
```
### [`ICanGetModel`](ICanGetModel.cs)
标记接口,表示实现者可以获取模型。继承自 [`IBelongToArchitecture`](../rule/IBelongToArchitecture.cs)。
## 核心类
### [`AbstractModel`](AbstractModel.cs)
抽象模型基类实现IModel接口提供模型的基础实现。该类继承自[
`ContextAwareBase`](file:///d:/Project/Rider/GFramework/GFramework.Core/rule/ContextAwareBase.cs#L11-L37),提供了上下文感知能力。
**使用示例:**
```csharp
public class PlayerModel : AbstractModel
{
// 使用 BindableProperty 定义可监听的属性
public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
protected override void OnInit()
{
// 监听生命值变化
Health.Register(newHealth =>
{
if (newHealth <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
});
}
public override void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.Initializing:
// 架构初始化阶段的处理
break;
case ArchitecturePhase.Ready:
// 架构就绪阶段的处理
break;
// ... 其他阶段处理
}
}
}
```
## Model 的职责
### ✅ 应该做的事
1. **存储数据和状态**
2. **提供数据访问接口**
3. **监听自身属性变化并做出响应**
4. **发送数据变化事件**
5. **处理架构生命周期事件**
### ❌ 不应该做的事
1. **不包含复杂业务逻辑** - 业务逻辑应该在 System 中
2. **不直接依赖其他 Model** - 通过 Command 协调
3. **不包含 UI 逻辑** - UI 逻辑应该在 Controller 中
## 常见 Model 示例
### 玩家数据模型
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<string> PlayerId { get; } = new("");
public BindableProperty<string> PlayerName { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<int> Gold { get; } = new(0);
public Vector3 Position { get; set; }
protected override void OnInit()
{
Health.Register(hp =>
{
if (hp <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
});
}
public override void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.Ready:
// 模型准备好后的处理
_log?.Log("PlayerModel is ready.");
break;
default:
break;
}
}
}
```
### 游戏状态模型
```csharp
public class GameModel : AbstractModel
{
public BindableProperty<GameState> State { get; } = new(GameState.Menu);
public BindableProperty<int> Score { get; } = new(0);
public BindableProperty<int> HighScore { get; } = new(0);
public BindableProperty<int> CurrentLevel { get; } = new(1);
protected override void OnInit()
{
Score.Register(newScore =>
{
if (newScore > HighScore.Value)
{
HighScore.Value = newScore;
this.SendEvent(new NewHighScoreEvent { Score = newScore });
}
});
}
public override void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.ShuttingDown:
// 游戏模型清理工作
break;
default:
break;
}
}
}
```
## 最佳实践
1. **使用 BindableProperty 存储需要监听的数据**
2. **Model 之间不要直接调用,通过 Command/System 协调**
3. **复杂计算和业务逻辑放在 System 中**
4. **使用事件通知数据的重要变化**
5. **保持 Model 简单纯粹,只做数据管理**
## 相关包
- [`architecture`](../architecture/README.md) - 提供 Model 的注册和获取
- [`property`](../property/README.md) - BindableProperty 用于定义可监听属性
- [`events`](../events/README.md) - Model 发送事件通知变化
- [`utility`](../utility/README.md) - Model 可以使用工具类
- [`extensions`](../extensions/README.md) - 提供 GetModel 等扩展方法

View File

@ -1,951 +0,0 @@
# 对象池系统 (Object Pool System)
## 概述
GFramework 的对象池系统是一个高效的内存管理机制旨在减少垃圾回收GC压力通过复用对象实例来提高应用程序性能。该系统实现了对象的创建、获取、释放和销毁的完整生命周期管理。
**核心优势:**
- **减少 GC 压力**:复用对象实例,避免频繁的内存分配和回收
- **提高性能**:避免重复创建开销大的对象
- **灵活管理**:支持多个独立的对象池,按需分类管理
- **自动生命周期**:与 System 生命周期集成,自动管理对象销毁
- **类型安全**:基于泛型实现,编译时类型检查
## 核心组件
### IPoolableObject 接口
定义了可池化对象的行为规范,所有需要池化的对象都必须实现此接口。
```csharp
public interface IPoolableObject
{
/// <summary>
/// 当对象从池中获取时调用,用于初始化或重置对象状态
/// </summary>
void OnAcquire();
/// <summary>
/// 当对象被放回池中时调用,用于清理对象状态
/// </summary>
void OnRelease();
/// <summary>
/// 当对象池被销毁时调用,用于执行最终清理
/// </summary>
void OnPoolDestroy();
}
```
**生命周期:**
```
创建 → Acquire从池取出→ 使用 → Release放回池→ 可再次 Acquire
Pool Destroy → OnPoolDestroy → 销毁
```
### IObjectPoolSystem 接口
定义了对象池系统的基本操作接口。
```csharp
public interface IObjectPoolSystem<TKey, TObject>
where TObject : IPoolableObject
where TKey : notnull
{
/// <summary>
/// 从指定键的对象池中获取一个对象
/// </summary>
/// <param name="key">对象池的键值</param>
/// <returns>获取到的对象实例</returns>
TObject Acquire(TKey key);
/// <summary>
/// 将对象释放回指定键的对象池中
/// </summary>
/// <param name="key">对象池的键值</param>
/// <param name="obj">需要释放的对象</param>
void Release(TKey key, TObject obj);
/// <summary>
/// 清空所有对象池,销毁所有池中的对象
/// </summary>
void Clear();
}
```
### AbstractObjectPoolSystem 抽象类
实现了 `IObjectPoolSystem` 接口的具体逻辑,提供了对象池管理的完整实现。
**核心特性:**
- 使用字典存储多个对象池,以键区分不同的对象池
- 使用栈Stack存储池中的对象实现 LIFO后进先出管理
- 提供获取和释放对象的方法
- 通过抽象方法 `Create` 让子类决定如何创建对象
- 在系统销毁时自动清理所有对象池
**内部实现:**
```csharp
public abstract class AbstractObjectPoolSystem<TKey, TObject>
: AbstractSystem, IObjectPoolSystem<TKey, TObject>
where TObject : IPoolableObject
where TKey : notnull
{
// 存储对象池的字典,键为池标识,值为对应类型的对象栈
protected readonly Dictionary<TKey, Stack<TObject>> Pools = new();
// 获取对象
public TObject Acquire(TKey key)
{
if (!Pools.TryGetValue(key, out var pool))
{
pool = new Stack<TObject>();
Pools[key] = pool;
}
var obj = pool.Count > 0
? pool.Pop() // 从池中取出
: Create(key); // 创建新对象
obj.OnAcquire(); // 调用对象的获取钩子
return obj;
}
// 释放对象
public void Release(TKey key, TObject obj)
{
obj.OnRelease(); // 调用对象的释放钩子
if (!Pools.TryGetValue(key, out var pool))
{
pool = new Stack<TObject>();
Pools[key] = pool;
}
pool.Push(obj); // 放回池中
}
// 清空所有对象池
public void Clear()
{
foreach (var obj in Pools.Values.SelectMany(pool => pool))
{
obj.OnPoolDestroy(); // 调用对象的销毁钩子
}
Pools.Clear();
}
// 子类实现:创建新对象
protected abstract TObject Create(TKey key);
// 系统销毁时自动清空对象池
protected override void OnDestroy()
{
Clear();
}
}
```
## 基本使用
### 1. 定义池化对象
首先,创建一个实现 `IPoolableObject` 接口的类。
```csharp
public class Bullet : IPoolableObject
{
public int Damage { get; private set; }
public float Speed { get; private set; }
public Vector3 Position { get; private set; }
public Vector3 Direction { get; private set; }
public bool IsActive { get; private set; }
public void OnAcquire()
{
// 从池中获取时调用,初始化对象状态
IsActive = true;
}
public void OnRelease()
{
// 放回池中时调用,清理对象状态
IsActive = false;
Damage = 0;
Speed = 0;
Position = Vector3.Zero;
Direction = Vector3.Zero;
}
public void OnPoolDestroy()
{
// 对象池销毁时调用,执行最终清理
// 可以在这里释放非托管资源
}
// 设置子弹属性的方法
public void Setup(int damage, float speed, Vector3 position, Vector3 direction)
{
Damage = damage;
Speed = speed;
Position = position;
Direction = direction;
}
// 更新子弹逻辑
public void Update(float deltaTime)
{
Position += Direction * Speed * deltaTime;
}
}
```
### 2. 实现对象池系统
继承 `AbstractObjectPoolSystem<TKey, TObject>` 并实现 `Create` 方法。
```csharp
public class BulletPoolSystem : AbstractObjectPoolSystem<string, Bullet>
{
protected override Bullet Create(string key)
{
// 根据键值创建不同类型的子弹
return key switch
{
"standard" => new Bullet(),
"heavy" => new Bullet(),
"explosive" => new Bullet(),
_ => new Bullet()
};
}
protected override void OnInit()
{
// 可以预先创建一些对象放入池中
for (int i = 0; i < 10; i++)
{
var bullet = Create("standard");
bullet.OnAcquire();
bullet.OnRelease();
Release("standard", bullet);
}
}
}
```
### 3. 在架构中注册对象池系统
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册其他组件
RegisterModel(new PlayerModel());
RegisterSystem(new CombatSystem());
// 注册对象池系统
RegisterSystem(new BulletPoolSystem());
}
}
```
### 4. 使用对象池
```csharp
public class ShootingSystem : AbstractSystem
{
private BulletPoolSystem _bulletPool;
private List<Bullet> _activeBullets = new();
protected override void OnInit()
{
_bulletPool = this.GetSystem<BulletPoolSystem>();
this.RegisterEvent<ShootEvent>(OnShoot);
this.RegisterEvent<GameUpdateEvent>(OnUpdate);
}
private void OnShoot(ShootEvent e)
{
// 从对象池获取子弹
var bullet = _bulletPool.Acquire("standard");
// 设置子弹属性
bullet.Setup(
damage: e.Damage,
speed: 10.0f,
position: e.StartPosition,
direction: e.Direction
);
// 添加到活跃列表
_activeBullets.Add(bullet);
}
private void OnUpdate(GameUpdateEvent e)
{
// 更新所有活跃子弹
for (int i = _activeBullets.Count - 1; i >= 0; i--)
{
var bullet = _activeBullets[i];
bullet.Update(e.DeltaTime);
// 检查子弹是否需要销毁(例如:超出范围或击中目标)
if (ShouldDestroyBullet(bullet))
{
// 放回对象池
_bulletPool.Release("standard", bullet);
_activeBullets.RemoveAt(i);
}
}
}
private bool ShouldDestroyBullet(Bullet bullet)
{
// 简单示例:子弹超出一定范围则销毁
return bullet.Position.Length() > 1000.0f;
}
}
```
## 高级用法
### 1. 多键对象池管理
```csharp
public class ParticlePoolSystem : AbstractObjectPoolSystem<string, Particle>
{
protected override Particle Create(string key)
{
// 根据键值创建不同类型的粒子效果
return key switch
{
"explosion" => new Particle(explosionPrefab),
"smoke" => new Particle(smokePrefab),
"spark" => new Particle(sparkPrefab),
_ => throw new ArgumentException($"Unknown particle type: {key}")
};
}
// 提供便捷方法
public Particle SpawnExplosion(Vector3 position)
{
var particle = Acquire("explosion");
particle.Position = position;
particle.Play();
return particle;
}
}
// 使用
public class EffectSystem : AbstractSystem
{
private ParticlePoolSystem _particlePool;
protected override void OnInit()
{
_particlePool = this.GetSystem<ParticlePoolSystem>();
}
public void PlayExplosion(Vector3 position)
{
_particlePool.SpawnExplosion(position);
}
}
```
### 2. 动态对象池管理
```csharp
public class EnemyPoolSystem : AbstractObjectPoolSystem<string, Enemy>
{
protected override Enemy Create(string key)
{
// 根据敌人类型创建不同的敌人
var enemyPrefab = LoadEnemyPrefab(key);
return new Enemy(enemyPrefab);
}
// 动态注册新的敌人类型池
public void RegisterEnemyType(string enemyType)
{
if (!Pools.ContainsKey(enemyType))
{
Pools[enemyType] = new Stack<Enemy>();
// 预热:预先创建几个敌人放入池中
for (int i = 0; i < 3; i++)
{
var enemy = Create(enemyType);
enemy.OnAcquire();
enemy.OnRelease();
Release(enemyType, enemy);
}
}
}
}
// 使用
public class EnemySpawnerSystem : AbstractSystem
{
private EnemyPoolSystem _enemyPool;
protected override void OnInit()
{
_enemyPool = this.GetSystem<EnemyPoolSystem>();
// 注册不同类型的敌人
_enemyPool.RegisterEnemyType("goblin");
_enemyPool.RegisterEnemyType("orc");
_enemyPool.RegisterEnemyType("dragon");
}
public void SpawnEnemy(string enemyType, Vector3 position)
{
var enemy = _enemyPool.Acquire(enemyType);
enemy.Position = position;
enemy.Activate();
}
}
```
### 3. 对象池大小限制
```csharp
public class LimitedBulletPoolSystem : AbstractObjectPoolSystem<string, Bullet>
{
private const int MaxPoolSize = 50;
protected override Bullet Create(string key)
{
return new Bullet();
}
public new void Release(string key, Bullet obj)
{
// 检查对象池大小
if (Pools.TryGetValue(key, out var pool) && pool.Count >= MaxPoolSize)
{
// 池已满,不回收对象,让它被 GC 回收
return;
}
// 调用基类的 Release 方法
base.Release(key, obj);
}
}
```
### 4. 对象池统计和调试
```csharp
public class DebuggablePoolSystem : AbstractObjectPoolSystem<string, PoolableObject>
{
public Dictionary<string, int> PoolSizes => Pools.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Count
);
public int TotalPooledObjects => Pools.Values.Sum(stack => stack.Count);
public void LogPoolStatus()
{
foreach (var (key, stack) in Pools)
{
Console.WriteLine($"Pool [{key}]: {stack.Count} objects");
}
Console.WriteLine($"Total pooled objects: {TotalPooledObjects}");
}
protected override void OnDestroy()
{
LogPoolStatus();
base.OnDestroy();
}
}
```
## 使用场景
### 1. 游戏对象池
**适用对象:**
- 子弹、箭矢、投射物
- 敌人、NPC
- 爆炸效果、粒子系统
- UI 元素(提示、对话框)
**示例:子弹池**
```csharp
// 定义子弹
public class Bullet : IPoolableObject
{
public Vector3 Position { get; private set; }
public Vector3 Velocity { get; private set; }
public float Lifetime { get; private set; }
public void OnAcquire()
{
Lifetime = 5.0f; // 5秒后自动销毁
}
public void OnRelease()
{
Position = Vector3.Zero;
Velocity = Vector3.Zero;
}
public void OnPoolDestroy() { }
public void Update(float deltaTime)
{
Position += Velocity * deltaTime;
Lifetime -= deltaTime;
}
}
// 子弹对象池
public class BulletPoolSystem : AbstractObjectPoolSystem<string, Bullet>
{
protected override Bullet Create(string key) => new Bullet();
}
// 射击系统
public class ShootingSystem : AbstractSystem
{
private BulletPoolSystem _bulletPool;
private List<Bullet> _activeBullets = new();
protected override void OnInit()
{
_bulletPool = this.GetSystem<BulletPoolSystem>();
this.RegisterEvent<ShootEvent>(OnShoot);
this.RegisterEvent<GameUpdateEvent>(OnUpdate);
}
private void OnShoot(ShootEvent e)
{
var bullet = _bulletPool.Acquire("normal");
bullet.Position = e.StartPosition;
bullet.Velocity = e.Direction * e.Speed;
_activeBullets.Add(bullet);
}
private void OnUpdate(GameUpdateEvent e)
{
for (int i = _activeBullets.Count - 1; i >= 0; i--)
{
var bullet = _activeBullets[i];
bullet.Update(e.DeltaTime);
if (bullet.Lifetime <= 0)
{
_bulletPool.Release("normal", bullet);
_activeBullets.RemoveAt(i);
}
}
}
}
```
### 2. UI 元素池
**适用对象:**
- 对话框
- 提示框
- 菜单项
- 列表项
**示例:提示框池**
```csharp
public class Tooltip : IPoolableObject
{
public string Text { get; set; }
public bool IsActive { get; private set; }
public void OnAcquire()
{
IsActive = true;
}
public void OnRelease()
{
IsActive = false;
Text = "";
}
public void OnPoolDestroy() { }
public void Show(string text, Vector3 position)
{
Text = text;
// 更新 UI 位置和内容
}
}
public class TooltipPoolSystem : AbstractObjectPoolSystem<string, Tooltip>
{
protected override Tooltip Create(string key) => new Tooltip();
}
public class UISystem : AbstractSystem
{
private TooltipPoolSystem _tooltipPool;
protected override void OnInit()
{
_tooltipPool = this.GetSystem<TooltipPoolSystem>();
}
public void ShowTooltip(string text, Vector3 position)
{
var tooltip = _tooltipPool.Acquire("default");
tooltip.Show(text, position);
}
}
```
### 3. 网络消息对象池
**适用对象:**
- 网络包
- 协议消息
- 数据包
**示例:网络包池**
```csharp
public class NetworkPacket : IPoolableObject
{
public byte[] Data { get; private set; }
public int Length { get; private set; }
public void OnAcquire()
{
Data = Array.Empty<byte>();
Length = 0;
}
public void OnRelease()
{
// 清理敏感数据
if (Data != null)
{
Array.Clear(Data, 0, Data.Length);
}
Length = 0;
}
public void OnPoolDestroy() { }
public void SetData(byte[] data)
{
Data = data;
Length = data.Length;
}
}
public class PacketPoolSystem : AbstractObjectPoolSystem<string, NetworkPacket>
{
protected override NetworkPacket Create(string key) => new NetworkPacket();
}
```
## 最佳实践
### 1. 对象生命周期管理
```csharp
// ✅ 好的做法:确保所有对象都放回池中
public class BulletSystem : AbstractSystem
{
private List<Bullet> _activeBullets = new();
protected override void OnDestroy()
{
// 系统销毁时,确保所有活跃子弹都放回池中
foreach (var bullet in _activeBullets)
{
_bulletPool.Release("standard", bullet);
}
_activeBullets.Clear();
base.OnDestroy();
}
}
// ❌ 不好的做法:忘记放回对象,导致泄漏
public class BadBulletSystem : AbstractSystem
{
private List<Bullet> _activeBullets = new();
private void OnUpdate(GameUpdateEvent e)
{
// 子弹销毁时忘记放回池中
if (bullet.Lifetime <= 0)
{
_activeBullets.RemoveAt(i);
// 忘记调用 _bulletPool.Release(...)
}
}
}
```
### 2. 对象状态重置
```csharp
// ✅ 好的做法:在 OnRelease 中彻底重置对象状态
public class Bullet : IPoolableObject
{
public int Damage { get; set; }
public float Speed { get; set; }
public List<string> Tags { get; set; }
public Dictionary<string, object> Data { get; set; }
public void OnRelease()
{
// 重置所有属性
Damage = 0;
Speed = 0;
Tags?.Clear();
Data?.Clear();
// 也可以设置为新实例(如果性能允许)
Tags = new List<string>();
Data = new Dictionary<string, object>();
}
}
// ❌ 不好的做法:不完全重置状态
public class BadBullet : IPoolableObject
{
public List<string> Tags = new List<string>();
public void OnRelease()
{
// 只清空列表,但列表实例本身保留
// 这可能导致问题:如果其他代码持有列表引用
Tags.Clear();
}
}
```
### 3. 对象池预热
```csharp
// ✅ 好的做法:预先创建一些对象放入池中
public class BulletPoolSystem : AbstractObjectPoolSystem<string, Bullet>
{
protected override Bullet Create(string key) => new Bullet();
protected override void OnInit()
{
// 为常用的子弹类型预热对象池
var commonTypes = new[] { "standard", "heavy", "sniper" };
foreach (var type in commonTypes)
{
// 预先创建 5 个对象
for (int i = 0; i < 5; i++)
{
var bullet = Create(type);
bullet.OnAcquire();
bullet.OnRelease();
Release(type, bullet);
}
}
}
}
```
### 4. 对象池大小管理
```csharp
// ✅ 好的做法:限制对象池大小,避免内存浪费
public class BoundedPoolSystem : AbstractObjectPoolSystem<string, PooledObject>
{
private const int MaxPoolSize = 100;
public new void Release(string key, PooledObject obj)
{
if (Pools.TryGetValue(key, out var pool) && pool.Count >= MaxPoolSize)
{
// 池已满,不回收对象
return;
}
base.Release(key, obj);
}
}
// ✅ 好的做法:动态调整对象池大小
public class AdaptivePoolSystem : AbstractObjectPoolSystem<string, PooledObject>
{
private Dictionary<string, int> _peakUsage = new();
public new void Release(string key, PooledObject obj)
{
// 记录峰值使用量
if (Pools.TryGetValue(key, out var pool))
{
_peakUsage[key] = Math.Max(_peakUsage.GetValueOrDefault(key, 0), pool.Count);
}
// 根据使用情况动态调整
int maxAllowed = _peakUsage.GetValueOrDefault(key, 10) * 2;
if (pool.Count >= maxAllowed)
{
return; // 不回收
}
base.Release(key, obj);
}
}
```
### 5. 调试和监控
```csharp
// ✅ 好的做法:添加对象池统计功能
public class MonitoredPoolSystem : AbstractObjectPoolSystem<string, PooledObject>
{
private Dictionary<string, int> _acquireCount = new();
private Dictionary<string, int> _releaseCount = new();
public new PooledObject Acquire(string key)
{
_acquireCount[key] = _acquireCount.GetValueOrDefault(key, 0) + 1;
return base.Acquire(key);
}
public new void Release(string key, PooledObject obj)
{
_releaseCount[key] = _releaseCount.GetValueOrDefault(key, 0) + 1;
base.Release(key, obj);
}
public void PrintStatistics()
{
foreach (var key in Pools.Keys)
{
int acquired = _acquireCount.GetValueOrDefault(key, 0);
int released = _releaseCount.GetValueOrDefault(key, 0);
int leaked = acquired - released;
Console.WriteLine($"Pool [{key}]:");
Console.WriteLine($" Acquired: {acquired}");
Console.WriteLine($" Released: {released}");
Console.WriteLine($" Leaked: {leaked}");
Console.WriteLine($" Pool size: {Pools[key].Count}");
}
}
}
```
## 性能优化
### 1. 减少对象创建
```csharp
// ✅ 好的做法:对象池预热,避免运行时创建
public class PreheatedPoolSystem : AbstractObjectPoolSystem<string, Bullet>
{
protected override Bullet Create(string key) => new Bullet();
protected override void OnInit()
{
// 预热常用对象池
PreheatPool("standard", 20);
PreheatPool("heavy", 10);
}
private void PreheatPool(string key, int count)
{
for (int i = 0; i < count; i++)
{
var bullet = Create(key);
bullet.OnAcquire();
bullet.OnRelease();
Release(key, bullet);
}
}
}
```
### 2. 使用值类型作为键
```csharp
// ✅ 好的做法:使用枚举或整数作为键,避免字符串比较
public enum BulletType
{
Standard,
Heavy,
Explosive
}
public class FastBulletPoolSystem : AbstractObjectPoolSystem<BulletType, Bullet>
{
protected override Bullet Create(BulletType key) => key switch
{
BulletType.Standard => new Bullet(),
BulletType.Heavy => new Bullet(),
BulletType.Explosive => new Bullet(),
_ => throw new ArgumentException()
};
}
```
### 3. 批量操作
```csharp
// ✅ 好的做法:批量获取和释放对象
public class BatchPoolSystem : AbstractObjectPoolSystem<string, PooledObject>
{
public List<PooledObject> AcquireBatch(string key, int count)
{
var objects = new List<PooledObject>(count);
for (int i = 0; i < count; i++)
{
objects.Add(Acquire(key));
}
return objects;
}
public void ReleaseBatch(string key, IEnumerable<PooledObject> objects)
{
foreach (var obj in objects)
{
Release(key, obj);
}
}
}
```
## 注意事项
1. **对象状态管理**:确保 `OnRelease` 方法彻底重置对象状态,避免状态污染
2. **对象引用**:不要在放回对象池后继续持有对象引用
3. **线程安全**:对象池本身不是线程安全的,如需多线程访问,需要自行加锁
4. **内存泄漏**:确保所有获取的对象最终都放回池中
5. **对象池大小**:合理设置对象池大小,避免内存浪费或频繁创建
## 相关包
- [`system`](../system/README.md) - 对象池系统继承自 AbstractSystem
- [`architecture`](../architecture/README.md) - 在架构中注册对象池系统
---
**许可证**: Apache 2.0

View File

@ -1,341 +0,0 @@
# Property 包使用说明
## 概述
Property 包提供了可绑定属性BindableProperty的实现支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。
## 核心接口
### [`IReadonlyBindableProperty<T>`](IReadonlyBindableProperty.cs)
只读可绑定属性接口,提供属性值的读取和变更监听功能。
**核心成员:**
```csharp
// 获取属性值
T Value { get; }
// 注册监听(不立即触发回调)
IUnRegister Register(Action<T> onValueChanged);
// 注册监听并立即触发回调传递当前值
IUnRegister RegisterWithInitValue(Action<T> action);
// 取消监听
void UnRegister(Action<T> onValueChanged);
```
### [`IBindableProperty<T>`](IBindableProperty.cs)
可绑定属性接口,继承自只读接口,增加了修改能力。
**核心成员:**
```csharp
// 可读写的属性值
new T Value { get; set; }
// 设置值但不触发事件
void SetValueWithoutEvent(T newValue);
```
## 核心类
### [`BindableProperty<T>`](BindableProperty.cs)
可绑定属性的完整实现。
**使用示例:**
```csharp
// 创建可绑定属性
var health = new BindableProperty<int>(100);
// 监听值变化(不会立即触发)
var unregister = health.Register(newValue =>
{
GD.Print($"Health changed to: {newValue}");
});
// 设置值(会触发监听器)
health.Value = 50; // 输出: Health changed to: 50
// 取消监听
unregister.UnRegister();
// 设置值但不触发事件
health.SetValueWithoutEvent(75);
```
**高级功能:**
```csharp
// 1. 注册并立即获得当前值
health.RegisterWithInitValue(value =>
{
GD.Print($"Current health: {value}"); // 立即输出当前值
// 后续值变化时也会调用
});
// 2. 自定义比较器(静态方法)
BindableProperty<int>.Comparer = (a, b) => Math.Abs(a - b) < 1;
// 3. 使用实例方法设置比较器
var position = new BindableProperty<Vector3>(Vector3.Zero)
.WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等
```
### [`BindablePropertyUnRegister<T>`](BindablePropertyUnRegister.cs)
可绑定属性的注销器,负责清理监听。
**使用示例:**
```csharp
var unregister = health.Register(OnHealthChanged);
// 当需要取消监听时
unregister.UnRegister();
```
## 在 Model 中使用
### 定义可绑定属性
```csharp
public class PlayerModel : AbstractModel
{
// 可读写属性
public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
// 只读属性(外部只能读取和监听)
public IReadonlyBindableProperty<int> ReadonlyHealth => Health;
protected override void OnInit()
{
// 内部监听属性变化
Health.Register(hp =>
{
if (hp <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
});
}
}
```
## 在 Controller 中监听
### UI 数据绑定
```csharp
public partial class PlayerUI : Control, IController
{
[Export] private Label _healthLabel;
[Export] private Label _nameLabel;
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
var playerModel = this.GetModel<PlayerModel>();
// 绑定生命值到UI立即显示当前值
playerModel.Health
.RegisterWithInitValue(health =>
{
_healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}";
})
.AddToUnregisterList(_unregisterList);
// 绑定名称
playerModel.Name
.RegisterWithInitValue(name =>
{
_nameLabel.Text = name;
})
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
}
```
## 常见使用模式
### 1. 双向绑定
```csharp
// Model
public class SettingsModel : AbstractModel
{
public BindableProperty<float> MasterVolume { get; } = new(1.0f);
protected override void OnInit() { }
}
// UI Controller
public partial class VolumeSlider : HSlider, IController
{
private BindableProperty<float> _volumeProperty;
public override void _Ready()
{
_volumeProperty = this.GetModel<SettingsModel>().MasterVolume;
// Model -> UI
_volumeProperty.RegisterWithInitValue(vol => Value = vol)
.UnRegisterWhenNodeExitTree(this);
// UI -> Model
ValueChanged += newValue => _volumeProperty.Value = (float)newValue;
}
}
```
### 2. 计算属性
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<float> HealthPercent { get; } = new(1.0f);
protected override void OnInit()
{
// 自动计算百分比
Action updatePercent = () =>
{
HealthPercent.Value = (float)Health.Value / MaxHealth.Value;
};
Health.Register(_ => updatePercent());
MaxHealth.Register(_ => updatePercent());
updatePercent(); // 初始计算
}
}
```
### 3. 属性验证
```csharp
public class PlayerModel : AbstractModel
{
private BindableProperty<int> _health = new(100);
public BindableProperty<int> Health
{
get => _health;
set
{
// 限制范围
var clampedValue = Math.Clamp(value.Value, 0, MaxHealth.Value);
_health.Value = clampedValue;
}
}
public BindableProperty<int> MaxHealth { get; } = new(100);
protected override void OnInit() { }
}
```
### 4. 条件监听
```csharp
public class CombatController : Node, IController
{
public override void _Ready()
{
var playerModel = this.GetModel<PlayerModel>();
// 只在生命值低于30%时显示警告
playerModel.Health.Register(hp =>
{
if (hp < playerModel.MaxHealth.Value * 0.3f)
{
ShowLowHealthWarning();
}
else
{
HideLowHealthWarning();
}
}).UnRegisterWhenNodeExitTree(this);
}
}
```
## 性能优化
### 1. 避免频繁触发
```csharp
// 使用 SetValueWithoutEvent 批量修改
public void LoadPlayerData(SaveData data)
{
// 临时关闭事件
Health.SetValueWithoutEvent(data.Health);
Mana.SetValueWithoutEvent(data.Mana);
Gold.SetValueWithoutEvent(data.Gold);
// 最后统一触发一次更新事件
this.SendEvent(new PlayerDataLoadedEvent());
}
```
### 2. 自定义比较器
```csharp
// 避免浮点数精度问题导致的频繁触发
var position = new BindableProperty<Vector3>()
.WithComparer((a, b) => a.DistanceTo(b) < 0.001f);
```
## 实现原理
### 值变化检测
```csharp
// 使用 EqualityComparer<T>.Default 进行比较
if (!EqualityComparer<T>.Default.Equals(value, MValue))
{
MValue = value;
_mOnValueChanged?.Invoke(value);
}
```
### 事件触发机制
```csharp
// 当值变化时触发所有注册的回调
_mOnValueChanged?.Invoke(value);
```
## 最佳实践
1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层
2. **使用只读接口暴露** - 防止外部随意修改
3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
## 相关包
- [`model`](../model/README.md) - Model 中大量使用 BindableProperty
- [`events`](../events/README.md) - BindableProperty 基于事件系统实现
- [`extensions`](../extensions/README.md) - 提供便捷的注销扩展方法
---
**许可证**: Apache 2.0

View File

@ -1,447 +0,0 @@
# Query 包使用说明
## 概述
Query 包实现了 CQRS命令查询职责分离模式中的查询部分。Query 用于封装数据查询逻辑,与 Command 不同的是Query
有返回值且不应该修改系统状态。
## 核心接口
### IQuery<TResult>
查询接口,定义了查询的基本契约。
**核心成员:**
```csharp
TResult Do(); // 执行查询并返回结果
```
## 核心类
### [`AbstractQuery<TInput, TResult>`](AbstractQuery.cs)
抽象查询基类,提供了查询的基础实现。它接受一个泛型输入参数 TInput该参数必须实现 IQueryInput 接口。
**使用方式:**
```csharp
public abstract class AbstractQuery<TInput, TResult>(TInput input) : ContextAwareBase, IQuery<TResult>
where TInput : IQueryInput
{
public TResult Do() => OnDo(input); // 执行查询,传入输入参数
protected abstract TResult OnDo(TInput input); // 子类实现查询逻辑
}
```
### [`EmptyQueryInput`](EmptyQueryInput.cs)
空查询输入类,用于表示不需要任何输入参数的查询操作。
**使用方式:**
```csharp
public sealed class EmptyQueryInput : IQueryInput
{
// 作为占位符使用,适用于那些不需要额外输入参数的查询场景
}
```
### [`QueryBus`](QueryBus.cs)
查询总线实现,负责执行查询并返回结果。
**使用方式:**
```csharp
public sealed class QueryBus : IQueryBus
{
public TResult Send<TResult>(IQuery<TResult> query)
{
ArgumentNullException.ThrowIfNull(query);
return query.Do();
}
}
```
## 基本使用
### 1. 定义查询
``csharp
// 定义查询输入参数
public record GetPlayerGoldQueryInput : IQueryInput;
// 查询玩家金币数量
public class GetPlayerGoldQuery : AbstractQuery<GetPlayerGoldQueryInput, int>
{
public GetPlayerGoldQuery() : base(new EmptyQueryInput())
{
}
protected override int OnDo(GetPlayerGoldQueryInput input)
{
return this.GetModel<PlayerModel>().Gold.Value;
}
}
// 查询玩家是否死亡
public record IsPlayerDeadQueryInput : IQueryInput;
public class IsPlayerDeadQuery : AbstractQuery<IsPlayerDeadQueryInput, bool>
{
public IsPlayerDeadQuery() : base(new EmptyQueryInput())
{
}
protected override bool OnDo(IsPlayerDeadQueryInput input)
{
return this.GetModel<PlayerModel>().Health.Value <= 0;
}
}
// 查询背包中指定物品的数量
public record GetItemCountQueryInput(string ItemId) : IQueryInput;
public class GetItemCountQuery : AbstractQuery<GetItemCountQueryInput, int>
{
public GetItemCountQuery(string itemId) : base(new GetItemCountQueryInput(itemId))
{
}
protected override int OnDo(GetItemCountQueryInput input)
{
var inventory = this.GetModel<InventoryModel>();
return inventory.GetItemCount(input.ItemId);
}
}
```
### 2. 发送查询(在 Controller 中)
``csharp
public partial class ShopUI : Control, IController
{
[Export] private Button _buyButton;
[Export] private int _itemPrice = 100;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
_buyButton.Pressed += OnBuyButtonPressed;
}
private void OnBuyButtonPressed()
{
// 查询玩家金币
int playerGold = this.SendQuery(new GetPlayerGoldQuery());
if (playerGold >= _itemPrice)
{
// 发送购买命令
this.SendCommand(new BuyItemCommand { ItemId = "sword_01" });
}
else
{
GD.Print("金币不足!");
}
}
}
```
### 3. 在 System 中使用
``csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
// 注册事件监听
this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
}
private void OnEnemyAttack(EnemyAttackEvent e)
{
// 查询玩家是否已经死亡
bool isDead = this.SendQuery(new IsPlayerDeadQuery());
if (!isDead)
{
// 执行伤害逻辑
this.SendCommand(new TakeDamageCommand { Damage = e.Damage });
}
}
}
```
## 高级用法
### 1. 带参数的复杂查询
``csharp
// 查询指定范围内的敌人列表
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
{
public Vector3 Center { get; set; }
public float Radius { get; set; }
protected override List<Enemy> OnDo()
{
var enemySystem = this.GetSystem<EnemySpawnSystem>();
return enemySystem.GetEnemiesInRange(Center, Radius);
}
}
// 使用
var enemies = this.SendQuery(new GetEnemiesInRangeQuery
{
Center = playerPosition,
Radius = 10.0f
});
```
### 2. 组合查询
``csharp
// 查询玩家是否可以使用技能
public class CanUseSkillQuery : AbstractQuery<bool>
{
public string SkillId { get; set; }
protected override bool OnDo()
{
var playerModel = this.GetModel<PlayerModel>();
var skillModel = this.GetModel<SkillModel>();
// 查询技能消耗
var skillCost = this.SendQuery(new GetSkillCostQuery { SkillId = SkillId });
// 检查是否满足条件
return playerModel.Mana.Value >= skillCost.ManaCost
&& !this.SendQuery(new IsSkillOnCooldownQuery { SkillId = SkillId });
}
}
public class GetSkillCostQuery : AbstractQuery<SkillCost>
{
public string SkillId { get; set; }
protected override SkillCost OnDo()
{
return this.GetModel<SkillModel>().GetSkillCost(SkillId);
}
}
public class IsSkillOnCooldownQuery : AbstractQuery<bool>
{
public string SkillId { get; set; }
protected override bool OnDo()
{
return this.GetModel<SkillModel>().IsOnCooldown(SkillId);
}
}
```
### 3. 聚合数据查询
``csharp
// 查询玩家战斗力
public class GetPlayerPowerQuery : AbstractQuery<int>
{
protected override int OnDo()
{
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());
return basePower + equipmentPower + buffPower;
}
}
// 查询玩家详细信息用于UI显示
public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
{
protected override PlayerInfo OnDo()
{
var playerModel = this.GetModel<PlayerModel>();
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())
};
}
}
```
### 4. 跨 System 查询
``csharp
// 在 AI System 中查询玩家状态
public class EnemyAISystem : AbstractSystem
{
protected override void OnInit() { }
public void UpdateEnemyBehavior(Enemy enemy)
{
// 查询玩家位置
var playerPos = this.SendQuery(new GetPlayerPositionQuery());
// 查询玩家是否在攻击范围内
bool inRange = this.SendQuery(new IsPlayerInRangeQuery
{
Position = enemy.Position,
Range = enemy.AttackRange
});
if (inRange)
{
// 查询是否可以攻击
bool canAttack = this.SendQuery(new CanEnemyAttackQuery
{
EnemyId = enemy.Id
});
if (canAttack)
{
this.SendCommand(new EnemyAttackCommand { EnemyId = enemy.Id });
}
}
}
}
```
## Command vs Query
### Command命令
- **用途**:修改系统状态
- **返回值**无返回值void
- **示例**:购买物品、造成伤害、升级角色
### Query查询
- **用途**:读取数据,不修改状态
- **返回值**:有返回值
- **示例**:获取金币数量、检查技能冷却、查询玩家位置
``csharp
// ❌ 错误:在 Query 中修改状态
public class BadQuery : AbstractQuery<int>
{
protected override int OnDo()
{
var model = this.GetModel<PlayerModel>();
model.Gold.Value += 100; // 不应该在 Query 中修改数据!
return model.Gold.Value;
}
}
// ✅ 正确Query 只读取数据
public class GoodQuery : AbstractQuery<int>
{
protected override int OnDo()
{
return this.GetModel<PlayerModel>().Gold.Value;
}
}
// ✅ 修改数据应该使用 Command
public class AddGoldCommand : AbstractCommand
{
public int Amount { get; set; }
protected override void OnExecute()
{
var model = this.GetModel<PlayerModel>();
model.Gold.Value += Amount;
}
}
```
## 最佳实践
1. **查询只读取,不修改** - 保持 Query 的纯粹性
2. **小而专注** - 每个 Query 只负责一个具体的查询任务
3. **可组合** - 复杂查询可以通过组合简单查询实现
4. **避免过度查询** - 如果需要频繁查询,考虑使用 BindableProperty
5. **命名清晰** - Query 名称应该清楚表达查询意图Get、Is、Can、Has等前缀
## 性能优化
### 1. 缓存查询结果
``csharp
// 在 Model 中缓存复杂计算
public class PlayerModel : AbstractModel
{
private int? _cachedPower;
public int GetPower()
{
if (_cachedPower == null)
{
_cachedPower = CalculatePower();
}
return _cachedPower.Value;
}
private int CalculatePower()
{
// 复杂计算...
return 100;
}
public void InvalidatePowerCache()
{
_cachedPower = null;
}
}
```
### 2. 批量查询
``csharp
// 一次查询多个数据,而不是多次单独查询
public class GetMultipleItemCountsQuery : AbstractQuery<Dictionary<string, int>>
{
public List<string> ItemIds { get; set; }
protected override Dictionary<string, int> OnDo()
{
var inventory = this.GetModel<InventoryModel>();
return ItemIds.ToDictionary(id => id, id => inventory.GetItemCount(id));
}
}
```
## 相关包
- [`command`](../command/README.md) - CQRS 的命令部分
- [`model`](../model/README.md) - Query 主要从 Model 获取数据
- [`system`](../system/README.md) - System 中可以发送 Query
- [`controller`](../controller/README.md) - Controller 中可以发送 Query
- [`extensions`](../extensions/README.md) - 提供 SendQuery 扩展方法

View File

@ -1,316 +0,0 @@
# Rule 包使用说明
## 概述
Rule 包定义了框架的核心规则接口,这些接口规定了框架各个组件之间的关系和约束。所有框架组件都需要遵循这些规则接口。
## 核心接口
### IContextAware
标记接口,表示该类型可以感知架构上下文。
**接口定义:**
```csharp
public interface IContextAware
{
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
}
```
**实现此接口的类型:**
- System
- Query
- Model
- Command
- 以及其他需要感知架构上下文的组件
**作用:**
所有实现此接口的类型都能够获取其所属的架构上下文实例,从而访问架构提供的各种能力。
## 核心类
### [`ContextAwareBase`](ContextAwareBase.cs)
上下文感知基类,实现了 IContextAware 接口,为需要感知架构上下文的类提供基础实现。
**使用方式:**
```csharp
public abstract class ContextAwareBase : IContextAware
{
protected IArchitectureContext? Context { get; set; }
void IContextAware.SetContext(IArchitectureContext context)
{
Context = context;
OnContextReady(); // 上下文准备好后调用此方法
}
IArchitectureContext IContextAware.GetContext()
{
Context ??= GameContext.GetFirstArchitectureContext();
return Context;
}
protected virtual void OnContextReady() // 子类可以重写此方法进行初始化
{
}
}
```
## 接口关系图
```
IContextAware (上下文感知接口)
↓ 被继承于
├── AbstractSystem (抽象系统基类)
├── AbstractQuery<TInput, TResult> (抽象查询基类)
├── AbstractModel (抽象模型基类)
└── AbstractCommand (抽象命令基类)
```
## 使用场景
### 1. Component 继承 ContextAwareBase
```csharp
// 组件通过继承 ContextAwareBase 获得架构上下文访问能力
public partial class PlayerController : Node, IController
{
// 不再需要手动实现 IContextAware基类已处理
// 可以直接使用扩展方法
public override void _Ready()
{
var playerModel = this.GetModel<PlayerModel>();
this.SendCommand(new InitPlayerCommand());
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
}
private void OnPlayerDied(PlayerDiedEvent e)
{
GD.Print("Player died!");
}
}
```
### 2. Command 继承 AbstractCommand (IContextAware)
```csharp
// Command 继承 AbstractCommand自动成为 IContextAware
public class BuyItemCommand : AbstractCommand
{
public string ItemId { get; set; }
protected override void OnExecute()
{
// 框架或上下文系统会自动注入 IArchitectureContext
// 所以这里可以直接使用 this.GetModel
var playerModel = this.GetModel<PlayerModel>();
var shopModel = this.GetModel<ShopModel>();
int price = shopModel.GetItemPrice(ItemId);
if (playerModel.Gold.Value >= price)
{
playerModel.Gold.Value -= price;
this.SendCommand(new AddItemCommand { ItemId = ItemId });
}
}
}
```
### 3. 自定义组件遵循规则
```csharp
// 自定义管理器遵循框架规则,继承 ContextAwareBase
public class SaveManager : ContextAwareBase
{
// 不再需要手动构造函数传参,上下文由框架注入
// protected override void OnContextReady() 可用于初始化
public void SaveGame()
{
// 因为继承了 ContextAwareBase可以使用扩展方法
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
PlayerName = playerModel.Name.Value,
Level = playerModel.Level.Value,
Gold = playerModel.Gold.Value
};
// 保存逻辑...
}
}
```
## 规则约束
### 1. 组件注册规则
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// Model/System/Utility 自动获得架构引用
this.RegisterModel<PlayerModel>(new PlayerModel());
this.RegisterSystem<CombatSystem>(new CombatSystem());
this.RegisterUtility<StorageUtility>(new StorageUtility());
}
}
```
### 2. Command/Query 自动注入规则
```csharp
// Controller 中发送 Command
public class ShopUI : Control, IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
private void OnBuyButtonPressed()
{
var command = new BuyItemCommand { ItemId = "sword_01" };
// SendCommand 内部会自动调用 command.SetArchitecture(this.GetArchitecture())
this.SendCommand(command);
}
}
// Command 执行时已经有了架构引用
public class BuyItemCommand : AbstractCommand
{
public string ItemId { get; set; }
protected override void OnExecute()
{
// 此时 GetArchitecture() 已经可用
var model = this.GetModel<ShopModel>();
}
}
```
## 设计模式
### 1. 依赖注入模式
Rule 接口体现了依赖注入DI的思想
```csharp
// 接口定义了"需要什么"
public interface IContextAware
{
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
}
// 框架负责"提供什么"
public static class CanSendExtensions
{
public static void SendCommand<T>(this ICanSendCommand self, T command)
where T : ICommand
{
// 自动注入架构上下文依赖
command.SetContext(self.GetContext());
command.Execute();
}
}
```
### 2. 接口隔离原则ISP
Rule 接口遵循接口隔离原则,每个接口职责单一:
```csharp
// ❌ 不好的设计:一个大接口包含所有能力
public interface IBigInterface
{
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
T GetModel<T>() where T : class, IModel;
T GetSystem<T>() where T : class, ISystem;
void SendCommand<T>(T command) where T : ICommand;
// ... 更多方法
}
// ✅ 好的设计:小接口组合
public interface IContextAware { ... } // 只负责上下文的设置与获取
public interface ICanGetModel { ... } // 只负责获取 Model
public interface ICanSendCommand { ... } // 只负责发送 Command
```
### 3. 组合优于继承
通过接口组合实现不同能力:
```csharp
// Controller 需要获取 Model 和发送 Command
public interface IController : ICanGetModel, ICanGetSystem, ICanSendCommand,
ICanSendQuery, ICanRegisterEvent
{
}
// Command 需要上下文感知和获取 Model/System
public interface ICommand : IContextAware, ICanGetModel, ICanGetSystem,
ICanSendEvent, ICanSendQuery
{
}
// System 只需要获取其他组件
public interface ISystem : IContextAware, ICanGetModel, ICanGetUtility, ICanGetSystem,
ICanRegisterEvent, ICanSendEvent, ICanSendQuery
{
}
```
## 扩展规则
### 自定义规则接口
```csharp
// 定义新的规则接口
public interface ICanAccessDatabase : IBelongToArchitecture
{
}
// 提供扩展方法
public static class CanAccessDatabaseExtensions
{
public static DatabaseUtility GetDatabase(this ICanAccessDatabase self)
{
return self.GetArchitecture().GetUtility<DatabaseUtility>();
}
}
// 让特定组件实现这个接口
public class DatabaseCommand : AbstractCommand, ICanAccessDatabase
{
protected override void OnExecute()
{
var db = this.GetDatabase();
// 使用数据库...
}
}
```
## 最佳实践
1. **遵循既有规则** - 优先使用框架提供的规则接口
2. **不要绕过规则** - 不要直接存储架构引用,使用接口获取
3. **接口组合** - 通过实现多个小接口获得所需能力
4. **扩展规则** - 需要新能力时,定义新的规则接口
5. **保持一致** - 自定义组件也应遵循相同的规则体系
## 相关包
- [`architecture`](../architecture/README.md) - 定义 IArchitectureContext 接口
- [`command`](../command/README.md) - Command 继承 AbstractCommand (IContextAware)
- [`query`](../query/README.md) - Query 继承 AbstractQuery (IContextAware)
- [`controller`](../controller/README.md) - Controller 实现 ICanSendCommand 等接口
- [`system`](../system/README.md) - System 继承 AbstractSystem (IContextAware)
- [`model`](../model/README.md) - Model 继承 AbstractModel (IContextAware)
- [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法

View File

@ -1,544 +0,0 @@
# System 包使用说明
## 概述
System 包定义了业务逻辑层Business Logic Layer。System 负责处理游戏的核心业务逻辑,协调 Model 之间的交互,响应事件并执行复杂的业务流程。
## 核心接口
### [`ICanGetSystem`](ICanGetSystem.cs)
标记接口,表示该类型可以获取其他 System。
**继承关系:**
```csharp
public interface ICanGetSystem : IBelongToArchitecture
```
### [`ISystem`](ISystem.cs)
System 接口,定义了系统的基本行为。
**核心成员:**
```csharp
void Init(); // 系统初始化方法
void Destroy(); // 系统销毁方法
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
```
**继承的能力:**
- `ICanSetArchitecture` - 可设置架构
- `ICanGetModel` - 可获取 Model
- `ICanGetUtility` - 可获取 Utility
- `ICanGetSystem` - 可获取其他 System
- `ICanRegisterEvent` - 可注册事件
- `ICanSendEvent` - 可发送事件
## 核心类
### [`AbstractSystem`](AbstractSystem.cs)
抽象 System 基类,提供了 System 的基础实现。继承自 ContextAwareBase具有上下文感知能力。
**使用方式:**
```csharp
public abstract class AbstractSystem : ContextAwareBase, ISystem
{
void ISystem.Init() => OnInit(); // 系统初始化,内部调用抽象方法 OnInit()
void ISystem.Destroy() => OnDestroy(); // 系统销毁,内部调用 OnDestroy()
protected abstract void OnInit(); // 子类实现初始化逻辑
protected virtual void OnDestroy(); // 子类可选择重写销毁逻辑
public virtual void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
}
```
## 基本使用
### 1. 定义 System
```csharp
// 战斗系统
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
// 注册事件监听
this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack);
}
private void OnEnemyAttack(EnemyAttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// 计算伤害
int damage = CalculateDamage(e.AttackPower, playerModel.Defense.Value);
// 应用伤害
playerModel.Health.Value -= damage;
// 发送伤害事件
this.SendEvent(new PlayerTookDamageEvent { Damage = damage });
}
private void OnPlayerAttack(PlayerAttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
var enemyModel = this.GetModel<EnemyModel>();
int damage = CalculateDamage(playerModel.AttackPower.Value, e.Enemy.Defense);
e.Enemy.Health -= damage;
this.SendEvent(new EnemyTookDamageEvent
{
EnemyId = e.Enemy.Id,
Damage = damage
});
}
private int CalculateDamage(int attackPower, int defense)
{
return Math.Max(1, attackPower - defense / 2);
}
protected override void OnDestroy()
{
// 清理资源
base.OnDestroy();
}
}
```
### 2. 注册 System
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册 Model
this.RegisterModel<PlayerModel>(new PlayerModel());
this.RegisterModel<EnemyModel>(new EnemyModel());
// 注册 System系统注册后会自动调用 Init
this.RegisterSystem<CombatSystem>(new CombatSystem());
this.RegisterSystem<InventorySystem>(new InventorySystem());
this.RegisterSystem<QuestSystem>(new QuestSystem());
}
}
```
### 3. 在其他组件中获取 System
```csharp
// 在 Controller 中
public partial class GameController : Node, IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 获取 System
var combatSystem = this.GetSystem<CombatSystem>();
var questSystem = this.GetSystem<QuestSystem>();
}
}
// 在 Command 中
public class StartBattleCommand : AbstractCommand
{
protected override void OnExecute()
{
var combatSystem = this.GetSystem<CombatSystem>();
// 使用 System...
}
}
```
## 常见使用模式
### 1. 事件驱动的 System
```csharp
public class InventorySystem : AbstractSystem
{
protected override void OnInit()
{
// 监听物品相关事件
this.RegisterEvent<ItemAddedEvent>(OnItemAdded);
this.RegisterEvent<ItemRemovedEvent>(OnItemRemoved);
this.RegisterEvent<ItemUsedEvent>(OnItemUsed);
}
private void OnItemAdded(ItemAddedEvent e)
{
var inventoryModel = this.GetModel<InventoryModel>();
// 添加物品
inventoryModel.AddItem(e.ItemId, e.Count);
// 检查成就
CheckAchievements(e.ItemId);
// 发送通知
this.SendEvent(new ShowNotificationEvent
{
Message = $"获得物品: {e.ItemId} x{e.Count}"
});
}
private void OnItemUsed(ItemUsedEvent e)
{
var inventoryModel = this.GetModel<InventoryModel>();
var playerModel = this.GetModel<PlayerModel>();
if (inventoryModel.HasItem(e.ItemId))
{
// 应用物品效果
ApplyItemEffect(e.ItemId, playerModel);
// 移除物品
inventoryModel.RemoveItem(e.ItemId, 1);
this.SendEvent(new ItemEffectAppliedEvent { ItemId = e.ItemId });
}
}
private void ApplyItemEffect(string itemId, PlayerModel player)
{
// 物品效果逻辑...
if (itemId == "health_potion")
{
player.Health.Value = Math.Min(
player.Health.Value + 50,
player.MaxHealth.Value
);
}
}
private void CheckAchievements(string itemId)
{
// 成就检查逻辑...
}
}
```
### 2. 定时更新的 System
```csharp
public class BuffSystem : AbstractSystem
{
private List<BuffData> _activeBuffs = new();
protected override void OnInit()
{
this.RegisterEvent<BuffAppliedEvent>(OnBuffApplied);
this.RegisterEvent<GameUpdateEvent>(OnUpdate);
}
private void OnBuffApplied(BuffAppliedEvent e)
{
_activeBuffs.Add(new BuffData
{
BuffId = e.BuffId,
Duration = e.Duration,
RemainingTime = e.Duration
});
ApplyBuffEffect(e.BuffId, true);
}
private void OnUpdate(GameUpdateEvent e)
{
// 更新所有 Buff
for (int i = _activeBuffs.Count - 1; i >= 0; i--)
{
var buff = _activeBuffs[i];
buff.RemainingTime -= e.DeltaTime;
if (buff.RemainingTime <= 0)
{
// Buff 过期
ApplyBuffEffect(buff.BuffId, false);
_activeBuffs.RemoveAt(i);
this.SendEvent(new BuffExpiredEvent { BuffId = buff.BuffId });
}
}
}
private void ApplyBuffEffect(string buffId, bool apply)
{
var playerModel = this.GetModel<PlayerModel>();
// 应用或移除 Buff 效果...
}
}
```
### 3. 跨 System 协作
```csharp
public class QuestSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<EnemyKilledEvent>(OnEnemyKilled);
this.RegisterEvent<ItemCollectedEvent>(OnItemCollected);
}
private void OnEnemyKilled(EnemyKilledEvent e)
{
var questModel = this.GetModel<QuestModel>();
var activeQuests = questModel.GetActiveQuests();
foreach (var quest in activeQuests)
{
if (quest.Type == QuestType.KillEnemy && quest.TargetId == e.EnemyType)
{
quest.Progress++;
if (quest.Progress >= quest.RequiredAmount)
{
// 任务完成
CompleteQuest(quest.Id);
}
}
}
}
private void CompleteQuest(string questId)
{
var questModel = this.GetModel<QuestModel>();
var quest = questModel.GetQuest(questId);
// 标记任务完成
questModel.CompleteQuest(questId);
// 发放奖励(通过其他 System
this.SendEvent(new QuestCompletedEvent
{
QuestId = questId,
Rewards = quest.Rewards
});
}
}
public class RewardSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<QuestCompletedEvent>(OnQuestCompleted);
}
private void OnQuestCompleted(QuestCompletedEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// 发放奖励
foreach (var reward in e.Rewards)
{
switch (reward.Type)
{
case RewardType.Gold:
playerModel.Gold.Value += reward.Amount;
break;
case RewardType.Experience:
playerModel.Experience.Value += reward.Amount;
break;
case RewardType.Item:
this.SendEvent(new ItemAddedEvent
{
ItemId = reward.ItemId,
Count = reward.Amount
});
break;
}
}
this.SendEvent(new RewardsGrantedEvent { Rewards = e.Rewards });
}
}
```
### 4. 管理复杂状态机
```csharp
public class GameStateSystem : AbstractSystem
{
private GameState _currentState = GameState.MainMenu;
protected override void OnInit()
{
this.RegisterEvent<GameStateChangeRequestEvent>(OnStateChangeRequest);
}
private void OnStateChangeRequest(GameStateChangeRequestEvent e)
{
if (CanTransition(_currentState, e.TargetState))
{
ExitState(_currentState);
_currentState = e.TargetState;
EnterState(_currentState);
this.SendEvent(new GameStateChangedEvent
{
PreviousState = _currentState,
NewState = e.TargetState
});
}
}
private bool CanTransition(GameState from, GameState to)
{
// 状态转换规则
return (from, to) switch
{
(GameState.MainMenu, GameState.Playing) => true,
(GameState.Playing, GameState.Paused) => true,
(GameState.Paused, GameState.Playing) => true,
(GameState.Playing, GameState.GameOver) => true,
_ => false
};
}
private void EnterState(GameState state)
{
switch (state)
{
case GameState.Playing:
// 开始游戏
this.SendCommand(new StartGameCommand());
break;
case GameState.Paused:
// 暂停游戏
this.SendEvent(new GamePausedEvent());
break;
case GameState.GameOver:
// 游戏结束
this.SendCommand(new GameOverCommand());
break;
}
}
private void ExitState(GameState state)
{
// 清理当前状态
}
}
```
## System vs Model
### Model数据层
- **职责**:存储数据和状态
- **特点**:被动,等待修改
- **示例**PlayerModel、InventoryModel
### System逻辑层
- **职责**:处理业务逻辑,协调 Model
- **特点**:主动,响应事件
- **示例**CombatSystem、QuestSystem
```csharp
// ✅ 正确的职责划分
// Model: 存储数据
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Mana { get; } = new(50);
protected override void OnInit() { }
}
// System: 处理逻辑
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// System 负责计算和决策
int damage = CalculateDamage(e);
playerModel.Health.Value -= damage;
if (playerModel.Health.Value <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
}
}
```
## 最佳实践
1. **单一职责** - 每个 System 专注于一个业务领域
2. **事件驱动** - 通过事件与其他组件通信
3. **无状态或少状态** - 优先将状态存储在 Model 中
4. **可组合** - System 之间通过事件松耦合协作
5. **初始化注册** - 在 `OnInit` 中注册所有事件监听
## 性能优化
### 1. 避免频繁的 GetModel/GetSystem
```csharp
// ❌ 不好:每次都获取
private void OnUpdate(GameUpdateEvent e)
{
var model = this.GetModel<PlayerModel>(); // 频繁调用
// ...
}
// ✅ 好:缓存引用
private PlayerModel _playerModel;
protected override void OnInit()
{
_playerModel = this.GetModel<PlayerModel>(); // 只获取一次
}
private void OnUpdate(GameUpdateEvent e)
{
// 直接使用缓存的引用
_playerModel.Health.Value += 1;
}
```
### 2. 批量处理
```csharp
public class ParticleSystem : AbstractSystem
{
private List<Particle> _particles = new();
private void OnUpdate(GameUpdateEvent e)
{
// 批量更新,而不是每个粒子发一个事件
for (int i = _particles.Count - 1; i >= 0; i--)
{
UpdateParticle(_particles[i], e.DeltaTime);
}
}
}
```
## 相关包
- [`model`](../model/README.md) - System 操作 Model 的数据
- [`events`](../events/README.md) - System 通过事件通信
- [`command`](../command/README.md) - System 中可以发送 Command
- [`query`](../query/README.md) - System 中可以发送 Query
- [`utility`](../utility/README.md) - System 可以使用 Utility
- [`architecture`](../architecture/README.md) - 在架构中注册 System

View File

@ -1,613 +0,0 @@
# Utility 包使用说明
## 概述
Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如数学计算、文件操作、序列化等通用工具方法。与 System 不同Utility
不依赖架构状态,是纯粹的工具函数集合。
## 核心接口
### IUtility
Utility 标记接口,所有工具类都应实现此接口。
**接口定义:**
```csharp
public interface IUtility
{
// 标记接口,无方法定义
}
```
### IContextUtility
上下文工具接口扩展了IUtility接口为需要感知架构上下文的工具类提供基础能力。
**接口定义:**
```csharp
public interface IContextUtility : IUtility
{
void Init(); // 初始化上下文工具
}
```
## 核心类
### [`AbstractContextUtility`](AbstractContextUtility.cs)
抽象上下文工具类,提供上下文相关的通用功能实现。继承自 ContextAwareBase 并实现 IContextUtility 接口。
**使用方式:**
```csharp
public abstract class AbstractContextUtility : ContextAwareBase, IContextUtility
{
protected ILogger Logger = null!;
void IContextUtility.Init()
{
var name = GetType().Name;
Logger = LoggerFactoryResolver.Provider.CreateLogger(name);
Logger.Debug($"Initializing Context Utility: {name}");
OnInit(); // 子类实现初始化逻辑
Logger.Info($"Context Utility initialized: {name}");
}
protected abstract void OnInit(); // 子类实现具体的初始化逻辑
}
```
## 基本使用
### 1. 定义 Utility
```csharp
// 存储工具类继承自AbstractContextUtility
public class StorageUtility : AbstractContextUtility
{
private const string SavePath = "user://save_data.json";
protected override void OnInit()
{
Logger.Info("StorageUtility initialized");
}
public void Save<T>(T data)
{
string json = JsonSerializer.Serialize(data);
// 实际保存逻辑
File.WriteAllText(SavePath, json);
}
public T Load<T>()
{
if (!File.Exists(SavePath))
return default(T);
string json = File.ReadAllText(SavePath);
return JsonSerializer.Deserialize<T>(json);
}
public void Delete()
{
if (File.Exists(SavePath))
{
File.Delete(SavePath);
}
}
}
// 数学工具类作为普通Utility
public class MathUtility : IUtility
{
public float Lerp(float a, float b, float t)
{
return a + (b - a) * Math.Clamp(t, 0f, 1f);
}
public bool IsInRange(float value, float min, float max)
{
return value >= min && value <= max;
}
}
// 时间工具类
public class TimeUtility : IUtility
{
public string FormatTime(float seconds)
{
int minutes = (int)(seconds / 60);
int secs = (int)(seconds % 60);
return $"{minutes:D2}:{secs:D2}";
}
public long GetCurrentTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
public bool IsExpired(long timestamp, int durationSeconds)
{
return GetCurrentTimestamp() > timestamp + durationSeconds;
}
}
```
### 2. 注册 Utility
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册 Utility不需要初始化
this.RegisterUtility<StorageUtility>(new StorageUtility());
this.RegisterUtility<MathUtility>(new MathUtility());
this.RegisterUtility<TimeUtility>(new TimeUtility());
}
}
```
### 3. 使用 Utility
```csharp
// 在 System 中使用
public class SaveSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<SaveGameEvent>(OnSaveGame);
this.RegisterEvent<LoadGameEvent>(OnLoadGame);
}
private void OnSaveGame(SaveGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
PlayerName = playerModel.Name.Value,
Level = playerModel.Level.Value,
Gold = playerModel.Gold.Value,
Timestamp = this.GetUtility<TimeUtility>().GetCurrentTimestamp()
};
storage.Save(saveData);
this.SendEvent(new GameSavedEvent());
}
private void OnLoadGame(LoadGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var saveData = storage.Load<SaveData>();
if (saveData != null)
{
var playerModel = this.GetModel<PlayerModel>();
playerModel.Name.Value = saveData.PlayerName;
playerModel.Level.Value = saveData.Level;
playerModel.Gold.Value = saveData.Gold;
this.SendEvent(new GameLoadedEvent());
}
}
}
// 在 Command 中使用
public class MovePlayerCommand : AbstractCommand
{
public Vector3 TargetPosition { get; set; }
public float Speed { get; set; }
protected override void OnExecute()
{
var playerModel = this.GetModel<PlayerModel>();
var mathUtil = this.GetUtility<MathUtility>();
// 使用工具类计算
Vector3 currentPos = playerModel.Position.Value;
Vector3 direction = (TargetPosition - currentPos).Normalized();
Vector3 newPos = currentPos + direction * Speed;
playerModel.Position.Value = newPos;
}
}
```
## 常见 Utility 类型
### 1. 序列化/反序列化工具
```csharp
public class JsonUtility : IUtility
{
public string Serialize<T>(T obj)
{
return Json.Stringify(obj);
}
public T Deserialize<T>(string json) where T : new()
{
return Json.Parse<T>(json);
}
public bool TryDeserialize<T>(string json, out T result) where T : new()
{
try
{
result = Json.Parse<T>(json);
return true;
}
catch
{
result = default;
return false;
}
}
}
```
### 2. 随机数工具
```csharp
public class RandomUtility : IUtility
{
private Random _random = new Random();
public int Range(int min, int max)
{
return _random.Next(min, max + 1);
}
public float Range(float min, float max)
{
return min + (float)_random.NextDouble() * (max - min);
}
public T Choose<T>(params T[] items)
{
return items[Range(0, items.Length - 1)];
}
public List<T> Shuffle<T>(List<T> list)
{
var shuffled = new List<T>(list);
for (int i = shuffled.Count - 1; i > 0; i--)
{
int j = Range(0, i);
(shuffled[i], shuffled[j]) = (shuffled[j], shuffled[i]);
}
return shuffled;
}
public bool Probability(float chance)
{
return _random.NextDouble() < chance;
}
}
```
### 3. 字符串工具
```csharp
public class StringUtility : IUtility
{
public string Truncate(string text, int maxLength, string suffix = "...")
{
if (text.Length <= maxLength)
return text;
return text.Substring(0, maxLength - suffix.Length) + suffix;
}
public string FormatNumber(int number)
{
if (number >= 1000000)
return $"{number / 1000000.0:F1}M";
if (number >= 1000)
return $"{number / 1000.0:F1}K";
return number.ToString();
}
public string ToTitleCase(string text)
{
return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text.ToLower());
}
public bool IsValidEmail(string email)
{
return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}
}
```
### 4. 加密工具
```csharp
public class EncryptionUtility : IUtility
{
private const string EncryptionKey = "YourSecretKey123";
public string Encrypt(string plainText)
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = EncryptBytes(data);
return Convert.ToBase64String(encrypted);
}
public string Decrypt(string encryptedText)
{
byte[] data = Convert.FromBase64String(encryptedText);
byte[] decrypted = DecryptBytes(data);
return System.Text.Encoding.UTF8.GetString(decrypted);
}
private byte[] EncryptBytes(byte[] data)
{
// 简单的 XOR 加密示例(实际项目应使用更安全的算法)
byte[] key = System.Text.Encoding.UTF8.GetBytes(EncryptionKey);
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[i % key.Length]);
}
return result;
}
private byte[] DecryptBytes(byte[] data)
{
return EncryptBytes(data); // XOR 解密与加密相同
}
}
```
### 5. 对象池工具
```csharp
public class ObjectPoolUtility : IUtility
{
private Dictionary<Type, Queue<object>> _pools = new();
public T Get<T>() where T : new()
{
Type type = typeof(T);
if (_pools.ContainsKey(type) && _pools[type].Count > 0)
{
return (T)_pools[type].Dequeue();
}
return new T();
}
public void Return<T>(T obj)
{
Type type = typeof(T);
if (!_pools.ContainsKey(type))
{
_pools[type] = new Queue<object>();
}
_pools[type].Enqueue(obj);
}
public void Clear<T>()
{
Type type = typeof(T);
if (_pools.ContainsKey(type))
{
_pools[type].Clear();
}
}
public void ClearAll()
{
_pools.Clear();
}
}
```
### 6. 日志工具
```csharp
public class LogUtility : IUtility
{
public enum LogLevel
{
Debug,
Info,
Warning,
Error
}
public void Log(string message, LogLevel level = LogLevel.Info)
{
string prefix = level switch
{
LogLevel.Debug => "[DEBUG]",
LogLevel.Info => "[INFO]",
LogLevel.Warning => "[WARN]",
LogLevel.Error => "[ERROR]",
_ => ""
};
string timestamp = DateTime.Now.ToString("HH:mm:ss");
GD.Print($"{timestamp} {prefix} {message}");
}
public void Debug(string message) => Log(message, LogLevel.Debug);
public void Info(string message) => Log(message, LogLevel.Info);
public void Warning(string message) => Log(message, LogLevel.Warning);
public void Error(string message) => Log(message, LogLevel.Error);
}
```
## Utility vs System
### Utility工具层
- **无状态** - 不存储业务数据
- **纯函数** - 相同输入产生相同输出
- **独立性** - 不依赖架构状态
- **可复用** - 可在多个项目中使用
### System逻辑层
- **有状态** - 可能存储临时状态
- **业务逻辑** - 处理特定业务流程
- **架构依赖** - 需要访问 Model
- **项目特定** - 针对特定项目设计
```csharp
// ✅ Utility: 无状态的工具方法
public class MathUtility : IUtility
{
public float CalculateDamage(float attackPower, float defense)
{
return Math.Max(1, attackPower - defense * 0.5f);
}
}
// ✅ System: 有状态的业务逻辑
public class CombatSystem : AbstractSystem
{
private List<CombatInstance> _activeCombats = new();
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var mathUtil = this.GetUtility<MathUtility>();
var playerModel = this.GetModel<PlayerModel>();
// 使用 Utility 计算,但在 System 中处理业务逻辑
float damage = mathUtil.CalculateDamage(e.AttackPower, playerModel.Defense.Value);
playerModel.Health.Value -= (int)damage;
_activeCombats.Add(new CombatInstance { Damage = damage });
}
}
```
## 最佳实践
1. **保持无状态** - Utility 不应存储业务状态
2. **纯函数优先** - 相同输入应产生相同输出
3. **单一职责** - 每个 Utility 专注于一类功能
4. **避免依赖** - 不依赖 Model、System 等架构组件
5. **可测试** - 易于单元测试的纯函数
## 错误示例
```csharp
// ❌ 错误Utility 中存储状态
public class BadUtility : IUtility
{
private int _counter = 0; // 不应该有状态
public int GetNextId()
{
return ++_counter; // 依赖内部状态
}
}
// ❌ 错误Utility 中访问 Model
public class BadUtility2 : IUtility
{
public void DoSomething()
{
// Utility 不应该访问架构组件
var model = this.GetModel<PlayerModel>(); // 编译错误!
}
}
// ✅ 正确:如果需要状态,应该使用 System
public class IdGeneratorSystem : AbstractSystem
{
private int _counter = 0;
protected override void OnInit() { }
public int GetNextId()
{
return ++_counter;
}
}
```
## 性能优化
### 1. 缓存计算结果
```csharp
public class PathfindingUtility : IUtility
{
private Dictionary<(Vector3, Vector3), List<Vector3>> _pathCache = new();
public List<Vector3> FindPath(Vector3 start, Vector3 end, bool useCache = true)
{
var key = (start, end);
if (useCache && _pathCache.ContainsKey(key))
{
return _pathCache[key];
}
var path = CalculatePath(start, end);
if (useCache)
{
_pathCache[key] = path;
}
return path;
}
private List<Vector3> CalculatePath(Vector3 start, Vector3 end)
{
// A* 算法等复杂计算...
return new List<Vector3>();
}
public void ClearCache()
{
_pathCache.Clear();
}
}
```
### 2. 对象复用
```csharp
public class CollectionUtility : IUtility
{
private List<Vector3> _tempList = new();
public List<Vector3> GetPointsInRadius(Vector3 center, float radius, List<Vector3> points)
{
_tempList.Clear();
foreach (var point in points)
{
if (point.DistanceTo(center) <= radius)
{
_tempList.Add(point);
}
}
return new List<Vector3>(_tempList); // 返回副本
}
}
```
## 相关包
- [`system`](../system/README.md) - System 中使用 Utility
- [`command`](../command/README.md) - Command 中可以使用 Utility
- [`architecture`](../architecture/README.md) - 在架构中注册 Utility
- [`ioc`](../ioc/README.md) - Utility 通过 IoC 容器管理
- [`extensions`](../extensions/README.md) - 提供 GetUtility 扩展方法

View File

@ -1,14 +1,13 @@
# GFramework.Game.Abstractions # GFramework.Game.Abstractions
`GFramework.Game.Abstractions` 提供 `GFramework.Game` 的抽象层接口定义,用于解耦业务逻辑与具体实现 GFramework.Game 模块的抽象层,提供游戏业务相关的接口定义
## 主要内容 ## 主要内容
- 游戏业务常用抽象数据、场景、设置、存储、UI 等) - 游戏业务常用抽象接口
- 与 `GFramework.Core.Abstractions` 配合使用的接口契约 - 与 GFramework.Core.Abstractions 配合使用的契约
- 供上层应用或扩展模块进行面向接口编程
## 使用建议 ## 使用建议
- 若你需要直接使用完整游戏扩展能力,优先安装 `GeWuYou.GFramework.Game` - 若需直接使用完整游戏扩展能力,优先使用 GFramework.Game
- 若你在做模块拆分、测试替身Mock或跨实现解耦可单独依赖本包。 - 若在做模块拆分或需要解耦,可单独依赖本包

16
GFramework.Game/README.md Normal file
View File

@ -0,0 +1,16 @@
# GFramework.Game
GFramework 框架的游戏通用模块,提供游戏开发常用的功能。
## 主要功能
- **Settings** - 游戏设置系统,支持设置分类和配置应用
## 依赖关系
- 依赖 GFramework.Core
- 依赖 GFramework.Core.Abstractions
## 详细文档
参见 [docs/game/](../docs/game/) 目录下的详细文档。

View File

@ -1,204 +0,0 @@
# 设置系统 (Settings System)
## 概述
设置系统是 GFramework.Game 的核心组件之一,负责管理游戏中各种设置配置。该系统采用了模型-系统分离的设计模式支持设置部分Section的管理和设置应用器模式。
## 核心类
### SettingsModel
设置模型类,继承自 `AbstractModel` 并实现 `ISettingsModel` 接口。
**主要功能:**
- 管理不同类型的设置部分Settings Section
- 提供类型安全的设置访问
- 支持可应用设置对象的注册
**关键方法:**
- `Get<T>()` - 获取或创建指定类型的设置部分
- `TryGet(Type, out ISettingsSection)` - 尝试获取设置部分
- `Register(IApplyAbleSettings)` - 注册可应用的设置对象
- `All()` - 获取所有设置部分
### SettingsSystem
设置系统类,继承自 `AbstractSystem` 并实现 `ISettingsSystem` 接口。
**主要功能:**
- 应用设置配置到相应系统
- 支持单个或批量设置应用
- 自动识别可应用设置类型
**关键方法:**
- `ApplyAll()` - 应用所有设置配置
- `Apply<T>()` - 应用指定类型的设置
- `Apply(IEnumerable<Type>)` - 应用指定类型集合的设置
## 架构设计
```mermaid
graph TD
A[ISettingsModel] --> B[SettingsModel]
C[ISettingsSystem] --> D[SettingsSystem]
B --> E[Dictionary<Type, ISettingsSection>]
D --> B
F[ISettingsSection] --> G[IApplyAbleSettings]
H[AudioSettings] --> G
I[GraphicsSettings] --> G
E --> H
E --> I
J[Application] --> D
D --> G
```
## 使用示例
### 基本使用
```csharp
// 获取设置模型
var settingsModel = this.GetModel<ISettingsModel>();
// 获取或创建音频设置
var audioSettings = settingsModel.Get<GodotAudioSettings>();
audioSettings.MasterVolume = 0.8f;
audioSettings.BgmVolume = 0.6f;
audioSettings.SfxVolume = 0.9f;
// 注册设置到模型
settingsModel.Register(audioSettings);
```
### 应用设置
```csharp
// 获取设置系统
var settingsSystem = this.GetSystem<ISettingsSystem>();
// 应用所有设置
await settingsSystem.ApplyAll();
// 应用特定类型设置
await settingsSystem.Apply<GodotAudioSettings>();
// 应用多个类型设置
var types = new[] { typeof(GodotAudioSettings), typeof(GodotGraphicsSettings) };
await settingsSystem.Apply(types);
```
### 创建自定义设置
```csharp
/// <summary>
/// 游戏设置类
/// </summary>
public class GameSettings : ISettingsSection
{
public float GameSpeed { get; set; } = 1.0f;
public int Difficulty { get; set; } = 1;
public bool AutoSave { get; set; } = true;
}
// 使用自定义设置
var gameSettings = settingsModel.Get<GameSettings>();
gameSettings.GameSpeed = 1.5f;
```
### 创建可应用设置
```csharp
/// <summary>
/// 游戏设置应用器
/// </summary>
public class GameSettings : ISettingsSection, IApplyAbleSettings
{
public float GameSpeed { get; set; } = 1.0f;
public int Difficulty { get; set; } = 1;
public Task Apply()
{
// 应用游戏速度
Time.timeScale = GameSpeed;
// 应用难度设置
GameDifficulty.Current = Difficulty;
return Task.CompletedTask;
}
}
```
## 接口定义
### ISettingsSection
```csharp
public interface ISettingsSection
{
// 设置部分的标识接口
}
```
### IApplyAbleSettings
```csharp
public interface IApplyAbleSettings : ISettingsSection
{
Task Apply();
}
```
### ISettingsModel
```csharp
public interface ISettingsModel
{
T Get<T>() where T : class, ISettingsSection, new();
bool TryGet(Type type, out ISettingsSection section);
IEnumerable<ISettingsSection> All();
void Register(IApplyAbleSettings applyAble);
}
```
### ISettingsSystem
```csharp
public interface ISettingsSystem
{
Task ApplyAll();
Task Apply<T>() where T : class, ISettingsSection;
Task Apply(Type settingsType);
Task Apply(IEnumerable<Type> settingsTypes);
}
```
## 设计模式
该系统使用了以下设计模式:
1. **Repository Pattern** - SettingsModel 作为设置数据的仓库
2. **Command Pattern** - IApplyAbleSettings 的 Apply 方法作为命令
3. **Factory Pattern** - Get<T>() 方法创建设置实例
4. **Template Method** - AbstractSystem 提供初始化模板
## 最佳实践
1. **设置分类** - 将相关设置组织到同一个设置类中
2. **延迟应用** - 批量修改后再应用,而不是每次修改都应用
3. **类型安全** - 使用泛型方法确保类型安全
4. **可测试性** - 通过接口实现便于单元测试
## 相关链接
- [Godot 设置模块](../../GFramework.Godot/setting/README.md)
- [存储模块](../../GFramework.Godot/storage/README.md)
- [抽象接口定义](../../../GFramework.Core/Abstractions/)

View File

@ -1,14 +1,13 @@
# GFramework.Godot.SourceGenerators # GFramework.Godot.SourceGenerators
`GFramework.Godot.SourceGenerators` 提供面向 Godot 场景的源码生成扩展,减少模板代码与手写样板 面向 Godot 场景的源码生成扩展模块,减少模板代码。
## 主要内容 ## 主要功能
- 与 Godot 场景相关的编译期生成能力 - 与 Godot 场景相关的编译期生成能力
- 基于 Roslyn 的增量生成器实现 - 基于 Roslyn 的增量生成器实现
- 与 `GFramework.SourceGenerators` 协同工作
## 使用建议 ## 使用建议
- 仅在 **Godot + C#** 项目中启用 - 仅在 Godot + C# 项目中启用
- 非 Godot 项目可只使用 `GeWuYou.GFramework.SourceGenerators`。 - 非 Godot 项目可只使用 GFramework.SourceGenerators

View File

@ -0,0 +1,19 @@
# GFramework.Godot
GFramework 框架的 Godot 引擎集成模块提供Godot特定的功能和扩展。
## 主要功能
- **Extensions** - Godot节点扩展方法简化常见开发任务
- **Signal** - 流畅的信号连接API支持链式调用
- **Storage** - Godot文件存储系统支持虚拟路径
- **Settings** - Godot设置系统管理音频和图形设置
## 依赖关系
- 依赖 GFramework.Core
- 依赖 GFramework.Core.Abstractions
## 详细文档
参见 [docs/godot/](../docs/godot/) 目录下的详细文档。

View File

@ -1,335 +0,0 @@
# Godot 扩展方法 (Godot Extensions)
## 概述
Godot 扩展方法模块为 Godot 引擎提供了丰富的便捷扩展方法集合。这些扩展方法简化了常见的 Godot
开发任务,提高了代码的可读性和开发效率。该模块遵循流畅接口设计原则,支持链式调用。
## 模块结构
```mermaid
graph TD
A[Extensions] --> B[GodotPathExtensions]
A --> C[NodeExtensions]
A --> D[SignalFluentExtensions]
A --> E[UnRegisterExtension]
D --> F[SignalBuilder]
B --> G[路径判断扩展]
C --> H[节点生命周期]
C --> I[节点查询]
C --> J[场景树操作]
C --> K[输入控制]
C --> L[调试工具]
D --> M[信号连接系统]
E --> N[事件管理]
```
## 扩展模块详解
### 1. 路径扩展 (GodotPathExtensions)
提供 Godot 虚拟路径的判断和识别功能。
**主要方法:**
- `IsUserPath()` - 判断是否为 `user://` 路径
- `IsResPath()` - 判断是否为 `res://` 路径
- `IsGodotPath()` - 判断是否为 Godot 虚拟路径
**使用示例:**
```csharp
string savePath = "user://save.dat";
string configPath = "res://config.json";
string logPath = "C:/logs/debug.log";
if (savePath.IsUserPath()) Console.WriteLine("用户数据路径");
if (configPath.IsResPath()) Console.WriteLine("资源路径");
if (logPath.IsGodotPath()) Console.WriteLine("Godot 虚拟路径");
else Console.WriteLine("文件系统路径");
```
### 2. 节点扩展 (NodeExtensions)
最丰富的扩展模块,提供全面的节点操作功能。
#### 节点生命周期管理
```csharp
// 安全释放节点
node.QueueFreeX(); // 延迟释放
node.FreeX(); // 立即释放
// 等待节点就绪
await node.WaitUntilReady();
// 检查节点有效性
if (node.IsValidNode()) Console.WriteLine("节点有效");
if (node.IsInvalidNode()) Console.WriteLine("节点无效");
```
#### 节点查询操作
```csharp
// 查找子节点
var sprite = node.FindChildX<Sprite2D>("Sprite");
var parent = node.GetParentX<Control>();
// 获取或创建节点
var panel = parent.GetOrCreateNode<Panel>("MainPanel");
// 遍历子节点
node.ForEachChild<Sprite2D>(sprite => {
sprite.Modulate = Colors.White;
});
```
#### 场景树操作
```csharp
// 获取根节点
var root = node.GetRootNodeX();
// 异步添加子节点
await parent.AddChildX(childNode);
// 设置场景树暂停状态
node.Paused(true); // 暂停
node.Paused(false); // 恢复
```
#### 输入控制
```csharp
// 标记输入事件已处理
node.SetInputAsHandled();
// 禁用/启用输入
node.DisableInput();
node.EnableInput();
```
#### 调试工具
```csharp
// 打印节点路径
node.LogNodePath();
// 打印节点树
node.PrintTreeX();
// 安全延迟调用
node.SafeCallDeferred("UpdateUI");
```
#### 类型转换
```csharp
// 安全的类型转换
var button = node.OfType<Button>();
var sprite = childNode.OfType<Sprite2D>();
```
### 3. 信号扩展 (SignalFluentExtensions)
提供流畅的信号连接 API详见 [signal/README.md](signal/README.md)。
**快速示例:**
```csharp
button.Signal(Button.SignalName.Pressed)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.ToAndCall(new Callable(this, nameof(OnButtonPressed)));
```
### 4. 取消注册扩展 (UnRegisterExtension)
自动管理事件监听器的生命周期。
**主要方法:**
- `UnRegisterWhenNodeExitTree()` - 节点退出场景树时自动取消注册
**使用示例:**
```csharp
var unRegister = eventManager.Subscribe<GameEvent>(OnGameEvent);
unRegister.UnRegisterWhenNodeExitTree(node);
```
## 快速参考
### 常用代码片段
#### 场景节点管理
```csharp
public class GameManager : Node
{
private Node _uiRoot;
public override void _Ready()
{
_uiRoot = GetNode<Node>("UI");
// 创建游戏面板
var gamePanel = _uiRoot.GetOrCreateNode<Panel>("GamePanel");
// 安全添加子节点
var player = new Player();
await AddChildX(player);
// 查找并配置玩家
var sprite = player.FindChildX<Sprite2D>("Sprite");
if (sprite.IsValidNode()) sprite.Modulate = Colors.Red;
}
public void Cleanup()
{
// 安全释放所有子节点
ForEachChild<Node>(child => child.QueueFreeX());
}
}
```
#### UI 事件处理
```csharp
public class MainMenu : Control
{
private Button _startButton;
private Button _quitButton;
public override void _Ready()
{
_startButton = FindChildX<Button>("StartButton");
_quitButton = FindChildX<Button>("QuitButton");
// 流畅的信号连接
_startButton.Signal(BaseButton.SignalName.Pressed)
.ToAndCall(new Callable(this, nameof(OnStartPressed)));
_quitButton.Signal(BaseButton.SignalName.Pressed)
.To(new Callable(this, nameof(OnQuitPressed)));
}
private void OnStartPressed()
{
GetTree().ChangeSceneToFile("res://scenes/game.tscn");
}
private void OnQuitPressed()
{
GetTree().Quit();
}
}
```
#### 异步场景管理
```csharp
public class SceneManager : Node
{
public async Task<T> LoadSceneAsync<T>(string scenePath) where T : Node
{
var packedScene = GD.Load<PackedScene>(scenePath);
var instance = packedScene.Instantiate<T>();
// 等待场景加载完成
await instance.WaitUntilReady();
return instance;
}
public async Task TransitionToScene(string scenePath)
{
var newScene = await LoadSceneAsync<Node>(scenePath);
// 清理当前场景
ForEachChild<Node>(child => child.QueueFreeX());
// 加载新场景
await AddChildX(newScene);
// 设置输入处理
newScene.SetInputAsHandled();
}
}
```
## 设计原则
### 1. 安全性
- 所有节点操作都包含有效性检查
- 提供安全的类型转换方法
- 避免空引用异常
### 2. 便利性
- 流畅的 API 设计
- 支持链式调用
- 减少样板代码
### 3. 一致性
- 统一的命名约定
- 一致的返回类型
- 预测性方法行为
### 4. 性能
- 避免不必要的节点查找
- 最小化内存分配
- 优化常见操作
## 最佳实践
### 1. 节点生命周期
```csharp
// ✅ 推荐:使用安全释放
node.QueueFreeX();
// ❌ 避免:直接释放可能导致错误
node.QueueFree();
```
### 2. 节点查询
```csharp
// ✅ 推荐:类型安全的查找
var button = node.FindChildX<Button>("Button");
// ❌ 避免:需要手动类型转换
var button = node.FindChild("Button") as Button;
```
### 3. 异步操作
```csharp
// ✅ 推荐:等待节点就绪
await child.WaitUntilReady();
// ❌ 避免:假设节点已就绪
child.DoSomething();
```
### 4. 事件管理
```csharp
// ✅ 推荐:自动清理事件
var unRegister = eventSystem.Subscribe(eventHandler);
unRegister.UnRegisterWhenNodeExitTree(node);
// ❌ 避免:手动管理事件生命周期
// 可能导致内存泄漏
```
## 相关链接
- [信号连接系统](signal/README.md) - 详细的信号连接 API 文档
- [存储模块](../storage/README.md) - 文件存储系统
- [设置模块](../setting/README.md) - 游戏设置系统
- [设置系统](../../GFramework.Game/setting/README.md) - 通用设置框架

View File

@ -1,427 +0,0 @@
# 信号连接系统 (Signal Connection System)
## 概述
信号连接系统是 Godot 扩展方法模块中的一个专门子模块,提供流畅、类型安全的 Godot 信号连接 API。该系统采用构建器模式Builder
Pattern和流畅接口设计Fluent Interface大大简化了信号订阅代码提高了代码的可读性和可维护性。
## 核心类
### SignalBuilder
信号连接构建器,负责构建和执行信号连接操作。
**特性:**
- 支持链式调用
- 可配置连接标志
- 支持连接后立即调用
- 返回原始对象以便继续操作
### SignalFluentExtensions
`GodotObject` 提供信号连接扩展方法,创建 `SignalBuilder` 实例。
## 架构设计
```mermaid
graph TD
A[GodotObject] --> B[SignalFluentExtensions]
B --> C[Signal Extension Method]
C --> D[SignalBuilder]
D --> E[WithFlags]
D --> F[To]
D --> G[ToAndCall]
D --> H[End]
F --> I[Connect Signal]
G --> J[Connect + Call]
H --> K[Return GodotObject]
L[ConnectFlags] --> E
M[Callable] --> F
M --> G
```
## 使用示例
### 基本信号连接
```csharp
// 基本连接
button.Signal(Button.SignalName.Pressed)
.To(new Callable(this, nameof(OnButtonPressed)));
// 带连接标志
timer.Signal(Timer.SignalName.Timeout)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.To(new Callable(this, nameof(OnTimerTimeout)));
```
### 连接并立即调用
```csharp
// 连接信号并立即调用一次
button.Signal(Button.SignalName.Pressed)
.ToAndCall(new Callable(this, nameof(OnButtonPressed)));
// 连接带参数的信号并立即调用
area2D.Signal(Area2D.SignalName.BodyEntered)
.ToAndCall(new Callable(this, nameof(OnBodyEntered)), new Variant[] { node });
```
### 复杂的连接链
```csharp
// 设置连接标志并连接
player.Signal(Player.SignalName.HealthChanged)
.WithFlags(GodotObject.ConnectFlags.Deferred)
.To(new Callable(this, nameof(OnHealthChanged)));
// 连接多个信号
var button = GetNode<Button>("StartButton");
button.Signal(Button.SignalName.Pressed)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.ToAndCall(new Callable(this, nameof(OnGameStarted)));
```
## API 详细说明
### SignalBuilder 构造函数
```csharp
public SignalBuilder(GodotObject target, StringName signal)
```
**参数:**
- `target` - 要连接信号的 Godot 对象
- `signal` - 要连接的信号名称
### SignalBuilder 方法
#### WithFlags
设置连接标志。
```csharp
public SignalBuilder WithFlags(GodotObject.ConnectFlags flags)
```
**参数:**
- `flags` - Godot 连接标志枚举值
**常用的连接标志:**
- `ConnectFlags.Deferred` - 延迟调用
- `ConnectFlags.OneShot` - 一次性连接
- `ConnectFlags.ConnectPersisted` - 连接持久化
- `ConnectFlags.ReferenceCounted` - 引用计数
#### To
连接信号到指定的可调用对象。
```csharp
public SignalBuilder To(Callable callable, GodotObject.ConnectFlags? flags = null)
```
**参数:**
- `callable` - 要连接的可调用对象
- `flags` - 可选的连接标志,覆盖之前设置的标志
#### ToAndCall
连接信号并立即调用一次。
```csharp
public SignalBuilder ToAndCall(Callable callable, GodotObject.ConnectFlags? flags = null, params Variant[] args)
```
**参数:**
- `callable` - 要连接的可调用对象
- `flags` - 可选的连接标志
- `args` - 调用时传递的参数
#### End
显式结束构建,返回原始对象。
```csharp
public GodotObject End()
```
### SignalFluentExtensions 扩展方法
#### Signal
为 Godot 对象创建信号构建器。
```csharp
public static SignalBuilder Signal(this GodotObject @object, StringName signal)
```
**参数:**
- `@object` - 扩展方法的目标对象
- `signal` - 要连接的信号名称
## 实际应用场景
### UI 事件处理
```csharp
public class MainMenu : Control
{
public override void _Ready()
{
var startButton = GetNode<Button>("StartButton");
var quitButton = GetNode<Button>("QuitButton");
var settingsButton = GetNode<Button>("SettingsButton");
// 开始按钮 - 一次性连接并立即禁用
startButton.Signal(Button.SignalName.Pressed)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.ToAndCall(new Callable(this, nameof(OnStartPressed)));
// 退出按钮
quitButton.Signal(Button.SignalName.Pressed)
.To(new Callable(this, nameof(OnQuitPressed)));
// 设置按钮 - 延迟调用避免嵌套问题
settingsButton.Signal(Button.SignalName.Pressed)
.WithFlags(GodotObject.ConnectFlags.Deferred)
.To(new Callable(this, nameof(OnSettingsPressed)));
}
private void OnStartPressed()
{
GetTree().ChangeSceneToFile("res://scenes/game.tscn");
}
private void OnQuitPressed()
{
GetTree().Quit();
}
private void OnSettingsPressed()
{
// 打开设置面板
GetNode<Control>("SettingsPanel").Show();
}
}
```
### 游戏逻辑事件
```csharp
public class Player : CharacterBody2D
{
private HealthComponent _health;
private AnimationPlayer _animPlayer;
public override void _Ready()
{
_health = GetNode<HealthComponent>("HealthComponent");
_animPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
// 生命值变化 - 延迟处理避免在动画中修改状态
_health.Signal(HealthComponent.SignalName.HealthChanged)
.WithFlags(GodotObject.ConnectFlags.Deferred)
.To(new Callable(this, nameof(OnHealthChanged)));
// 死亡事件 - 一次性连接
_health.Signal(HealthComponent.SignalName.Died)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.To(new Callable(this, nameof(OnDied)));
}
private void OnHealthChanged(float newHealth, float maxHealth)
{
// 更新UI或状态
UpdateHealthBar(newHealth / maxHealth);
// 播放受伤动画
if (newHealth < _health.PreviousHealth)
{
_animPlayer.Play("hurt");
}
}
private void OnDied()
{
// 播放死亡动画
_animPlayer.Play("death");
// 游戏结束
GetTree().CallDeferred(SceneTree.MethodName.Quit);
}
}
```
### 音频管理
```csharp
public class AudioManager : Node
{
private AudioStreamPlayer _bgmPlayer;
private AudioStreamPlayer _sfxPlayer;
public override void _Ready()
{
_bgmPlayer = GetNode<AudioStreamPlayer>("BGMPlayer");
_sfxPlayer = GetNode<AudioStreamPlayer>("SFXPlayer");
// 背景音乐播放完成
_bgmPlayer.Signal(AudioStreamPlayer.SignalName.Finished)
.To(new Callable(this, nameof(OnBGMFinished)));
// 音效播放完成 - 延迟清理
_sfxPlayer.Signal(AudioStreamPlayer.SignalName.Finished)
.WithFlags(GodotObject.ConnectFlags.Deferred)
.To(new Callable(this, nameof(OnSFXFinished)));
}
private void OnBGMFinished()
{
// 循环播放背景音乐
PlayBGM(_currentBGM);
}
private void OnSFXFinished()
{
// 清理音效资源或播放队列中的下一个音效
CleanupSFXResources();
}
}
```
## 设计模式分析
### Builder Pattern
SignalBuilder 实现了构建器模式:
- 分步构建复杂的信号连接
- 支持链式调用
- 延迟执行到最终调用时
### Fluent Interface
流畅接口设计:
- 方法链式调用
- 可读性强
- 表达力强
### Extension Method Pattern
扩展方法模式:
- 为现有类型添加功能
- 不修改原始类
- 保持向后兼容
## 与原生 API 对比
### 原生 Godot API
```csharp
// 传统方式
button.Connect(Button.SignalName.Pressed, new Callable(this, nameof(OnButtonPressed)));
// 带标志的方式
button.Connect(Button.SignalName.Pressed, new Callable(this, nameof(OnButtonPressed)), (uint)GodotObject.ConnectFlags.OneShot);
// 连接并立即调用
button.Connect(Button.SignalName.Pressed, new Callable(this, nameof(OnButtonPressed)));
new Callable(this, nameof(OnButtonPressed)).Call();
```
### 信号连接系统 API
```csharp
// 流畅方式
button.Signal(Button.SignalName.Pressed)
.To(new Callable(this, nameof(OnButtonPressed)));
// 带标志的方式
button.Signal(Button.SignalName.Pressed)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.To(new Callable(this, nameof(OnButtonPressed)));
// 连接并立即调用
button.Signal(Button.SignalName.Pressed)
.ToAndCall(new Callable(this, nameof(OnButtonPressed)));
```
## 性能考虑
### 内存分配
- SignalBuilder 是轻量级对象
- 创建开销很小
- 使用后可被垃圾回收
### 调用开销
- 与原生 API 性能基本相同
- 主要开销在方法链调用
- 运行时性能无差异
### 推荐做法
- 避免在热循环中创建大量 SignalBuilder
- 适合 UI 事件、游戏逻辑等场景
- 可以放心使用,性能影响可忽略
## 最佳实践
### 1. 选择合适的连接标志
```csharp
// UI 事件通常使用延迟调用
button.Signal(Button.SignalName.Pressed)
.WithFlags(GodotObject.ConnectFlags.Deferred)
.To(callable);
// 一次性事件使用一次性标志
dialog.Signal(CustomDialog.SignalName.Accepted)
.WithFlags(GodotObject.ConnectFlags.OneShot)
.To(callable);
```
### 2. 合理使用 ToAndCall
```csharp
// ✅ 适合:初始化时立即触发
settingsSlider.Signal(Slider.SignalName.ValueChanged)
.ToAndCall(new Callable(this, nameof(OnSettingsChanged)), initialSliderValue);
// ❌ 避免:重复连接并调用
button.Signal(Button.SignalName.Pressed)
.ToAndCall(new Callable(this, nameof(OnButtonPressed))); // 可能不必要
```
### 3. 链式调用可读性
```csharp
// ✅ 推荐:清晰的链式调用
player.Signal(Player.SignalName.HealthChanged)
.WithFlags(GodotObject.ConnectFlags.Deferred)
.To(new Callable(this, nameof(UpdateHealthUI)));
// ❌ 避免:过度嵌套
node.Signal(CustomSignal.Signal1).WithFlags(Flags1).To(callable1)
.Signal(CustomSignal.Signal2).WithFlags(Flags2).To(callable2);
```
## 相关链接
- [Godot 扩展方法](../README.md) - 扩展方法总览
- [节点扩展](../README.md#nodeextensions) - 更多节点操作方法
- [取消注册扩展](../README.md#unregisterextension) - 事件生命周期管理
- [Godot 官方信号文档](https://docs.godotengine.org/en/stable/tutorials/scripting_signals.html) - Godot 信号系统基础

View File

@ -1,579 +0,0 @@
# Godot 设置模块 (Godot Settings Module)
## 概述
Godot 设置模块是 GFramework.Godot 的核心组件之一,专门为 Godot 引擎提供游戏设置系统的实现。该模块将通用的设置框架与 Godot
引擎的特定功能相结合,提供了音频设置和图形设置的完整解决方案。
## 核心类
### 音频设置系统
#### AudioBusMap
音频总线映射配置类,用于定义音频系统中不同类型音频的总线名称。
**属性:**
- `Master` - 主音频总线名称(默认:"Master"
- `Bgm` - 背景音乐音频总线名称(默认:"BGM"
- `Sfx` - 音效音频总线名称(默认:"SFX"
#### GodotAudioApplier
音频设置应用器,负责将音频设置应用到 Godot 引擎的音频总线系统。
**功能:**
- 应用音量设置到指定音频总线
- 处理音量格式转换(线性值到分贝)
- 音频总线存在性检查和警告
#### GodotAudioSettings
Godot 音频设置实现类,接收 AudioSettings 配置并实现 IApplyAbleSettings 接口,负责将音频配置应用到 Godot 音频系统。
**实现关系:**
```
AudioSettings (配置数据)
↓ [组合]
GodotAudioSettings (Godot 特定实现) → IApplyAbleSettings (可应用设置接口)
```
**功能:**
- 接收 AudioSettings 配置对象和 AudioBusMap 总线映射
- 实现 Apply() 方法,将音量设置应用到指定音频总线
- 支持自定义音频总线映射
- 自动处理音量格式转换(线性值到分贝)
### 图形设置系统
#### GodotGraphicsSettings
Godot 图形设置实现类,继承自 GraphicsSettings 并实现 IApplyAbleSettings。
**功能:**
- 分辨率设置和窗口尺寸调整
- 全屏模式切换
- 窗口位置自动居中
- 多显示器支持
## 架构设计
```mermaid
graph TD
A[AudioSettings] --> B[GodotAudioSettings]
C[GraphicsSettings] --> D[GodotGraphicsSettings]
E[IApplyAbleSettings] --> B
E --> D
G[AudioBusMap] --> B
B --> I[AudioServer API]
D --> J[DisplayServer API]
K[SettingsSystem] --> L[Apply Method]
L --> B
L --> D
```
## 使用示例
### 音频设置配置
#### 基本音频设置
```csharp
// 创建音频配置数据
var settings = new AudioSettings
{
MasterVolume = 0.8f, // 80% 主音量
BgmVolume = 0.6f, // 60% 背景音乐音量
SfxVolume = 0.9f // 90% 音效音量
};
// 创建 Godot 音频设置应用器
var audioSettings = new GodotAudioSettings(settings, new AudioBusMap());
// 应用设置
await audioSettings.Apply();
```
#### 自定义音频总线映射
```csharp
// 自定义音频总线映射
var customBusMap = new AudioBusMap
{
Master = "Master_Bus",
Bgm = "Background_Music",
Sfx = "Sound_Effects"
};
// 创建音频配置
var settings = new AudioSettings
{
MasterVolume = 0.7f,
BgmVolume = 0.5f,
SfxVolume = 0.8f
};
// 使用自定义总线映射应用设置
var audioSettings = new GodotAudioSettings(settings, customBusMap);
await audioSettings.Apply();
```
#### 通过设置系统使用
```csharp
// 注册音频设置到设置模型
var settingsModel = this.GetModel<ISettingsModel>();
var audioSettingsData = settingsModel.Get<AudioSettings>();
audioSettingsData.MasterVolume = 0.8f;
audioSettingsData.BgmVolume = 0.6f;
audioSettingsData.SfxVolume = 0.9f;
// 创建 Godot 音频设置应用器
var godotAudioSettings = new GodotAudioSettings(audioSettingsData, new AudioBusMap());
await godotAudioSettings.Apply();
```
### 图形设置配置
#### 基本图形设置
```csharp
// 创建图形设置
var graphicsSettings = new GodotGraphicsSettings
{
ResolutionWidth = 1920,
ResolutionHeight = 1080,
Fullscreen = true
};
// 应用设置
await graphicsSettings.Apply();
```
#### 窗口模式切换
```csharp
public class DisplayManager : Node
{
private GodotGraphicsSettings _graphicsSettings;
public override void _Ready()
{
_graphicsSettings = new GodotGraphicsSettings();
}
public async Task ToggleFullscreen()
{
_graphicsSettings.Fullscreen = !_graphicsSettings.Fullscreen;
await _graphicsSettings.Apply();
}
public async Task SetResolution(int width, int height)
{
_graphicsSettings.ResolutionWidth = width;
_graphicsSettings.ResolutionHeight = height;
_graphicsSettings.Fullscreen = false; // 窗口化时自动关闭全屏
await _graphicsSettings.Apply();
}
}
```
#### 预设分辨率配置
```csharp
public class ResolutionPresets
{
public static readonly (int width, int height)[] CommonResolutions =
{
(1920, 1080), // Full HD
(2560, 1440), // QHD
(3840, 2160), // 4K
(1280, 720), // HD
(1366, 768), // 常见笔记本分辨率
};
public static async Task ApplyResolution(GodotGraphicsSettings settings, int width, int height)
{
settings.ResolutionWidth = width;
settings.ResolutionHeight = height;
settings.Fullscreen = false;
await settings.Apply();
}
}
```
## API 详细说明
### AudioBusMap
```csharp
public sealed class AudioBusMap
{
public string Master { get; init; } = "Master";
public string Bgm { get; init; } = "BGM";
public string Sfx { get; init; } = "SFX";
}
```
**特点:**
- 使用 `init` 属性,创建后不可修改
- 提供合理的默认值
- 支持对象初始化语法
### GodotAudioSettings
```csharp
public class GodotAudioSettings(AudioSettings settings, AudioBusMap busMap) : IApplyAbleSettings
{
public Task Apply();
}
```
**构造函数参数:**
- `settings` - AudioSettings 配置对象,包含音量设置
- `busMap` - AudioBusMap 对象,定义音频总线映射
**Apply 方法实现:**
```csharp
public Task Apply()
{
SetBus(busMap.Master, settings.MasterVolume);
SetBus(busMap.Bgm, settings.BgmVolume);
SetBus(busMap.Sfx, settings.SfxVolume);
return Task.CompletedTask;
}
```
### GodotGraphicsSettings
```csharp
public class GodotGraphicsSettings : GraphicsSettings, IApplyAbleSettings
{
public Task Apply();
}
```
**Apply 方法功能:**
- 设置窗口边框标志
- 切换窗口模式(窗口化/全屏)
- 调整窗口尺寸
- 自动居中窗口
## 技术实现细节
### 音频音量转换
Godot 音频系统使用分贝dB作为音量单位而我们通常使用线性值0-1
```csharp
// 线性值到分贝转换
float linearVolume = 0.5f; // 50% 音量
float dbVolume = Mathf.LinearToDb(linearVolume); // 转换为分贝
// 应用到音频总线
AudioServer.SetBusVolumeDb(busIndex, dbVolume);
```
### 音量限制和保护
为避免完全静音(-inf dB应用了最小音量限制
```csharp
float clampedVolume = Mathf.Clamp(linear, 0.0001f, 1f);
float dbVolume = Mathf.LinearToDb(clampedVolume);
```
### 窗口管理
#### 全屏模式
```csharp
// 设置全屏
DisplayServer.WindowSetMode(DisplayServer.WindowMode.ExclusiveFullscreen);
DisplayServer.WindowSetFlag(DisplayServer.WindowFlags.Borderless, true);
```
#### 窗口化模式
```csharp
// 设置窗口化
DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed);
DisplayServer.WindowSetSize(newSize);
// 居中窗口
var screen = DisplayServer.GetPrimaryScreen();
var screenSize = DisplayServer.ScreenGetSize(screen);
var position = (screenSize - newSize) / 2;
DisplayServer.WindowSetPosition(position);
```
## 最佳实践
### 1. 音频设置管理
#### 音量变化平滑过渡
```csharp
public class AudioManager : Node
{
private Tween _volumeTween;
public async Task SmoothVolumeTransition(float targetMasterVolume, float duration = 1.0f)
{
var currentVolume = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("Master"));
var currentLinear = Mathf.DbToLinear(currentVolume);
_volumeTween?.Kill();
_volumeTween = CreateTween();
_volumeTween.TweenMethod(
new Callable(this, nameof(SetMasterVolume)),
currentLinear,
targetMasterVolume,
duration
);
}
private void SetMasterVolume(float linearVolume)
{
var settings = new AudioSettings { MasterVolume = linearVolume };
var audioSettings = new GodotAudioSettings(settings, new AudioBusMap());
audioSettings.Apply();
}
}
// 使用自定义总线映射的平滑过渡
public class CustomAudioManager : Node
{
private Tween _volumeTween;
private AudioBusMap _customBusMap;
public override void _Ready()
{
_customBusMap = new AudioBusMap
{
Master = "Master_Bus",
Bgm = "Background_Music",
Sfx = "Sound_Effects"
};
}
public async Task SmoothVolumeTransition(float targetMasterVolume, float duration = 1.0f)
{
var settings = new AudioSettings { MasterVolume = targetMasterVolume };
var currentVolume = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex(_customBusMap.Master));
var currentLinear = Mathf.DbToLinear(currentVolume);
_volumeTween?.Kill();
_volumeTween = CreateTween();
_volumeTween.TweenMethod(
new Callable(this, nameof(SetMasterVolume)),
currentLinear,
targetMasterVolume,
duration
);
}
private void SetMasterVolume(float linearVolume)
{
var audioSettingsData = new AudioSettings { MasterVolume = linearVolume };
var audioSettings = new GodotAudioSettings(audioSettingsData, _customBusMap);
audioSettings.Apply();
}
}
```
#### 音频设置验证
```csharp
public static class AudioSettingsValidator
{
public static bool ValidateBusNames(AudioBusMap busMap)
{
var masterIndex = AudioServer.GetBusIndex(busMap.Master);
var bgmIndex = AudioServer.GetBusIndex(busMap.Bgm);
var sfxIndex = AudioServer.GetBusIndex(busMap.Sfx);
return masterIndex >= 0 && bgmIndex >= 0 && sfxIndex >= 0;
}
public static void LogMissingBuses(AudioBusMap busMap)
{
if (AudioServer.GetBusIndex(busMap.Master) < 0)
GD.PrintErr($"Master bus not found: {busMap.Master}");
if (AudioServer.GetBusIndex(busMap.Bgm) < 0)
GD.PrintErr($"BGM bus not found: {busMap.Bgm}");
if (AudioServer.GetBusIndex(busMap.Sfx) < 0)
GD.PrintErr($"SFX bus not found: {busMap.Sfx}");
}
}
```
### 2. 图形设置管理
#### 分辨率变更安全检查
```csharp
public static class DisplayValidator
{
public static bool IsResolutionSupported(int width, int height)
{
var screen = DisplayServer.GetPrimaryScreen();
var screenSize = DisplayServer.ScreenGetSize(screen);
return width <= screenSize.x && height <= screenSize.y;
}
public static (int width, int height) GetMaxSafeResolution()
{
var screen = DisplayServer.GetPrimaryScreen();
var screenSize = DisplayServer.ScreenGetSize(screen);
return ((int)screenSize.x, (int)screenSize.y);
}
}
```
#### 图形设置持久化
```csharp
public class GraphicsSettingsManager : Node
{
private const string SettingsKey = "graphics_settings";
private GodotGraphicsSettings _settings;
public override void _Ready()
{
LoadSettings();
}
private void LoadSettings()
{
var storage = new GodotFileStorage(new JsonSerializer());
try
{
_settings = storage.Read<GodotGraphicsSettings>(SettingsKey);
}
catch (FileNotFoundException)
{
_settings = new GodotGraphicsSettings
{
ResolutionWidth = 1920,
ResolutionHeight = 1080,
Fullscreen = false
};
SaveSettings();
}
}
public void SaveSettings()
{
var storage = new GodotFileStorage(new JsonSerializer());
storage.Write(SettingsKey, _settings);
}
public async Task ApplyAndSave()
{
await _settings.Apply();
SaveSettings();
}
}
```
## 性能考虑
### 1. 音频设置应用
- 音频总线查找是 O(1) 操作
- 音量转换计算开销很小
- 建议批量应用多个音量设置
### 2. 图形设置应用
- 窗口操作需要系统调用,相对较慢
- 分辨率变更可能触发窗口重建
- 避免频繁切换显示模式
### 3. 设置持久化
- 使用异步文件 I/O
- 考虑设置变更防抖机制
- 压缩设置文件以减少 I/O 开销
## 故障排除
### 常见问题
#### 1. 音频总线未找到
```
错误Audio bus not found: CustomBGM
解决:确保在 Godot 项目中创建了对应的音频总线
```
#### 2. 分辨率设置无效
```
错误:分辨率无法设置到指定值
解决:检查分辨率是否超出显示器支持范围
```
#### 3. 全屏模式问题
```
错误:全屏切换失败
解决:检查是否在调试器中运行,某些全屏模式在调试时可能不可用
```
### 调试技巧
#### 音频调试
```csharp
// 打印所有音频总线信息
for (int i = 0; i < AudioServer.GetBusCount(); i++)
{
var name = AudioServer.GetBusName(i);
var volume = AudioServer.GetBusVolumeDb(i);
GD.Print($"Bus {i}: {name} ({volume} dB)");
}
```
#### 图形调试
```csharp
// 打印当前显示信息
var screen = DisplayServer.GetPrimaryScreen();
var screenSize = DisplayServer.ScreenGetSize(screen);
var windowSize = DisplayServer.WindowGetSize();
var windowPos = DisplayServer.WindowGetPosition();
var windowMode = DisplayServer.WindowGetMode();
GD.Print($"Screen: {screenSize}");
GD.Print($"Window: {windowSize} at {windowPos}");
GD.Print($"Mode: {windowMode}");
```
## 相关链接
- [设置系统](../../GFramework.Game/setting/README.md) - 通用设置框架
- [存储模块](../storage/README.md) - 设置持久化存储
- [扩展方法](../extensions/README.md) - Godot 扩展功能
- [Godot 音频文档](https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html) - Godot 音频总线系统
- [Godot 显示文档](https://docs.godotengine.org/en/stable/tutorials/rendering/window_management.html) - Godot 窗口管理

View File

@ -1,281 +0,0 @@
# 存储模块 (Storage Module)
## 概述
存储模块是 GFramework.Godot 的核心存储实现,专门为 Godot 引擎设计的文件存储系统。该模块支持 Godot 的虚拟路径系统(如
`res://``user://`),并提供了按键级别的细粒度锁机制来保证线程安全。
## 核心类
### GodotFileStorage
Godot 特化的文件存储实现,实现了 `IStorage` 接口。
**主要特性:**
- ✅ Godot 虚拟路径支持(`res://`, `user://`
- ✅ 线程安全(按键级别的细粒度锁)
- ✅ 同步/异步读写操作
- ✅ 自动创建目录结构
- ❌ 删除操作Delete 方法未实现)
## 功能特性
### 路径处理
该存储系统支持三种路径类型:
#### 1. Godot 资源路径 (`res://`)
- **用途**:存储游戏资源文件
- **特点**:只读,包含在游戏构建中
- **示例**`res://config/game_settings.json`
#### 2. Godot 用户数据路径 (`user://`)
- **用途**:存储用户数据、存档、配置等
- **特点**:可读写,游戏可访问的用户目录
- **示例**`user://saves/save_001.dat`
#### 3. 普通文件系统路径
- **用途**:存储临时文件或调试数据
- **特点**:完整的文件系统访问
- **示例**`C:/Games/MyGame/logs/debug.log`
### 路径验证与清理
```mermaid
graph TD
A[输入路径] --> B{包含 ".." ?}
B -->|是| C[抛出异常]
B -->|否| D{是 Godot 路径?}
D -->|是| E[直接使用]
D -->|否| F[清理路径段]
F --> G[替换无效字符]
G --> H[创建目录结构]
H --> I[返回绝对路径]
C --> J[结束]
E --> J
I --> J
```
### 线程安全机制
每个文件路径都有独立的锁对象,确保:
1. **细粒度锁** - 不同文件可以并发访问
2. **避免死锁** - 锁的获取顺序一致
3. **高性能** - 减少锁竞争
## API 接口
### IStorage 接口
```csharp
public interface IStorage
{
// 读取操作
T Read<T>(string key);
T Read<T>(string key, T defaultValue);
Task<T> ReadAsync<T>(string key);
// 写入操作
void Write<T>(string key, T value);
Task WriteAsync<T>(string key, T value);
// 检查存在性
bool Exists(string key);
Task<bool> ExistsAsync(string key);
// 删除操作(未实现)
void Delete(string key);
}
```
## 使用示例
### 基本使用
```csharp
// 创建存储实例(需要序列化器)
var serializer = new JsonSerializer(); // 或其他序列化器
var storage = new GodotFileStorage(serializer);
// 写入用户数据
var userData = new UserData
{
PlayerName = "Alice",
Level = 5,
Score = 1000
};
storage.Write("user://player.dat", userData);
// 读取用户数据
var loadedData = storage.Read<UserData>("user://player.dat");
Console.WriteLine($"Player: {loadedData.PlayerName}, Level: {loadedData.Level}");
```
### 异步操作
```csharp
// 异步写入游戏配置
var config = new GameConfig
{
Resolution = "1920x1080",
Fullscreen = true,
Volume = 0.8f
};
await storage.WriteAsync("user://config.json", config);
// 异步读取配置
var loadedConfig = await storage.ReadAsync<GameConfig>("user://config.json");
```
### 不同路径类型使用
```csharp
// 读取游戏资源(只读)
var levelData = storage.Read<LevelData>("res://levels/level_001.json");
// 存储用户存档
var saveData = new SaveData { /* ... */ };
storage.Write("user://saves/slot_001.dat", saveData);
// 存储调试信息(普通路径)
var debugLog = new DebugLog { /* ... */ };
storage.Write("logs/debug_" + DateTime.Now.Ticks + ".json", debugLog);
```
### 存在性检查
```csharp
// 检查文件是否存在
if (storage.Exists("user://settings.json"))
{
var settings = storage.Read<AppSettings>("user://settings.json");
// 使用设置...
}
else
{
// 使用默认设置
var defaultSettings = new AppSettings();
storage.Write("user://settings.json", defaultSettings);
}
```
### 带默认值的读取
```csharp
// 尝试读取,如果文件不存在则返回默认值
var settings = storage.Read("user://user_prefs.json", new UserPrefs
{
Language = "en",
Volume = 1.0f,
Difficulty = 1
});
```
## 路径扩展
该模块使用了路径扩展方法:
```csharp
public static class GodotPathExtensions
{
public static bool IsUserPath(this string path);
public static bool IsResPath(this string path);
public static bool IsGodotPath(this string path);
}
```
**使用示例:**
```csharp
string path1 = "user://save.dat";
string path2 = "res://config.json";
string path3 = "C:/temp/file.txt";
Console.WriteLine(path1.IsGodotPath()); // true
Console.WriteLine(path1.IsUserPath()); // true
Console.WriteLine(path2.IsResPath()); // true
Console.WriteLine(path3.IsGodotPath()); // false
```
## 性能考虑
### 1. 锁机制
- 每个文件路径独立锁,减少锁竞争
- 读写操作串行化,避免数据损坏
### 2. 文件访问
- Godot 虚拟路径使用 `FileAccess` API
- 普通路径使用标准 .NET 文件 I/O
- 自动创建目录结构
### 3. 内存使用
- 锁对象使用 `ConcurrentDictionary` 管理
- 锁对象按需创建,避免内存泄漏
## 错误处理
### 常见异常
1. **ArgumentException** - 路径参数无效
- 空路径
- 包含 ".." 的路径
- 无效的存储键
2. **FileNotFoundException** - 文件不存在
- 读取不存在的文件时抛出
3. **IOException** - 文件操作失败
- 写入权限不足
- 磁盘空间不足
### 错误处理示例
```csharp
try
{
var data = storage.Read<UserData>("user://save.dat");
}
catch (FileNotFoundException)
{
Console.WriteLine("存档文件不存在,创建新的存档");
var newSave = new UserData();
storage.Write("user://save.dat", newSave);
}
catch (Exception ex)
{
Console.WriteLine($"读取存档失败: {ex.Message}");
}
```
## 最佳实践
1. **路径选择**
- 游戏资源使用 `res://`
- 用户数据使用 `user://`
- 调试/临时文件使用普通路径
2. **异常处理**
- 总是处理 `FileNotFoundException`
- 使用带默认值的 `Read` 重载方法
3. **性能优化**
- 批量读写时使用异步方法
- 避免频繁的小文件操作
4. **序列化器选择**
- JSON人类可读调试友好
- 二进制:性能更好,文件更小
## 相关链接
- [路径扩展](../extensions/README.md#godotpathextensions)
- [设置系统](../setting/README.md)
- [抽象接口定义](../../../GFramework.Core/Abstractions/storage/)