mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
docs(game): 更新游戏模块文档并优化整体结构
- 重写了 GFramework.Game 模块的完整文档 - 添加了详细的目录结构和使用示例 - 扩展了架构模块系统的说明和代码示例 - 增加了资产管理、存储系统和序列化系统的详细文档 - 提供了模块配置和高级用法的指导 - 完善了存储系统的分层存储和缓存实现 - 添加了序列化系统的自定义转换器示例 - 更新了核心特性和设计理念的描述 - 优化了文档的整体组织结构和可读性 - 刷新了 VitePress 依赖缓存以同步文档变更
This commit is contained in:
parent
88a528746d
commit
026d9964fe
@ -1,581 +0,0 @@
|
||||
# GFramework.Core 模块测试覆盖详细清单
|
||||
|
||||
> **生成日期**: 2026-01-18
|
||||
> **最后更新**: 2026-01-18
|
||||
> **当前版本**: Core测试覆盖率 ~79.2% (文件级别)
|
||||
> **目标**: 提升Core模块测试覆盖率至 95%+ 并补充缺失的单元测试
|
||||
|
||||
---
|
||||
|
||||
## 📊 总体统计
|
||||
|
||||
| 类别 | 源文件数 | 有测试文件数 | 缺失测试文件数 | 测试覆盖率 |
|
||||
|--------|--------|--------|---------|-----------|
|
||||
| 架构系统 | 6 | 4 | 1 | 83% |
|
||||
| 事件系统 | 8 | 5 | 0 | 100% |
|
||||
| 命令系统 | 4 | 1 | 3 | 25% |
|
||||
| 查询系统 | 5 | 1 | 3 | 20% |
|
||||
| 日志系统 | 5 | 2 | 0 | 100% |
|
||||
| 扩展方法 | 4 | 2 | 0 | 100% |
|
||||
| 状态系统 | 4 | 2 | 0 | 100% |
|
||||
| IOC容器 | 1 | 1 | 0 | 100% |
|
||||
| 模型系统 | 1 | 0 | 0 | 100% |
|
||||
| 系统基类 | 1 | 0 | 0 | 100% |
|
||||
| 对象池 | 1 | 1 | 0 | 100% |
|
||||
| 属性系统 | 2 | 1 | 0 | 100% |
|
||||
| 规则系统 | 1 | 0 | 0 | 100% |
|
||||
| 工具类 | 1 | 0 | 1 | 0% |
|
||||
| 环境系统 | 2 | 1 | 0 | 100% |
|
||||
| 常量 | 2 | 0 | 2 | 0% |
|
||||
| 协程系统 | 11 | 4 | 0 | 100% |
|
||||
| **总计** | **59** | **24** | **10** | **83.1%** |
|
||||
|
||||
> **注**: 标记为0个测试文件的模块通过间接测试(集成测试)实现了功能覆盖
|
||||
> **重要发现**: 命令系统和查询系统的异步功能完全缺失测试!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 测试补充优先级概览
|
||||
|
||||
| 优先级 | 任务数 | 预计测试数 | 描述 |
|
||||
|---------|-------|-------------|-------------|
|
||||
| 🔴 高优先级 | 5 | 34-44 | 异步核心功能和工具基类 |
|
||||
| 🟡 中优先级 | 2 | 6-10 | 常量验证测试 |
|
||||
| ✅ 已完成 | 1 | 61 | 协程系统完整测试 |
|
||||
| **总计** | **7** | **101-115** | - |
|
||||
|
||||
---
|
||||
|
||||
## 📋 详细源文件与测试文件对应关系
|
||||
|
||||
### Architecture 模块 (6个源文件)
|
||||
|
||||
| 源文件 | 对应测试文件 | 测试覆盖 |
|
||||
|------------------------------|-----------------------------------------------------|---------|
|
||||
| Architecture.cs | SyncArchitectureTests.cs, AsyncArchitectureTests.cs | ✅ 98个测试 |
|
||||
| ArchitectureConfiguration.cs | ArchitectureConfigurationTests.cs | ✅ 12个测试 |
|
||||
| ArchitectureConstants.cs | **缺失** | ❌ 需补充 |
|
||||
| ArchitectureContext.cs | ArchitectureContextTests.cs | ✅ 22个测试 |
|
||||
| ArchitectureServices.cs | ArchitectureServicesTests.cs | ✅ 15个测试 |
|
||||
| GameContext.cs | GameContextTests.cs | ✅ 已有测试 |
|
||||
|
||||
**测试用例总数**: 147个
|
||||
|
||||
---
|
||||
|
||||
### Command 模块 (4个源文件)
|
||||
|
||||
| 源文件 | 对应测试文件 | 测试覆盖 |
|
||||
|-----------------------------|-------------------------|------------|
|
||||
| **AbstractAsyncCommand.cs** | **缺失** | ❌ 需创建测试文件 |
|
||||
| AbstractCommand.cs | CommandBusTests.cs (间接) | ✅ 已覆盖 |
|
||||
| **CommandBus.cs** | CommandBusTests.cs | ⚠️ 需补充异步测试 |
|
||||
| EmptyCommandInput.cs | CommandBusTests.cs (间接) | ✅ 已覆盖 |
|
||||
|
||||
**测试用例总数**: 4个(需补充异步测试)
|
||||
|
||||
**需要补充**:
|
||||
|
||||
1. ❌ AbstractAsyncCommandTests.cs - 新建(高优先级)
|
||||
2. ❌ CommandBusTests.cs - 补充 SendAsync 方法测试(高优先级)
|
||||
|
||||
---
|
||||
|
||||
### Query 模块 (5个源文件)
|
||||
|
||||
| 源文件 | 对应测试文件 | 测试覆盖 |
|
||||
|---------------------------|-----------------------|-----------|
|
||||
| **AbstractAsyncQuery.cs** | **缺失** | ❌ 需创建测试文件 |
|
||||
| AbstractQuery.cs | QueryBusTests.cs (间接) | ✅ 已覆盖 |
|
||||
| **AsyncQueryBus.cs** | **缺失** | ❌ 需创建测试文件 |
|
||||
| EmptyQueryInput.cs | QueryBusTests.cs (间接) | ✅ 已覆盖 |
|
||||
| QueryBus.cs | QueryBusTests.cs | ✅ 3个测试 |
|
||||
|
||||
**测试用例总数**: 3个(需补充异步测试)
|
||||
|
||||
**需要补充**:
|
||||
|
||||
1. ❌ AbstractAsyncQueryTests.cs - 新建(高优先级)
|
||||
2. ❌ AsyncQueryBusTests.cs - 新建(高优先级)
|
||||
|
||||
---
|
||||
|
||||
### Constants 模块 (2个源文件)
|
||||
|
||||
| 源文件 | 对应测试文件 | 测试覆盖 |
|
||||
|--------------------------|--------|-------|
|
||||
| ArchitectureConstants.cs | **缺失** | ❌ 需补充 |
|
||||
| GFrameworkConstants.cs | **缺失** | ❌ 需补充 |
|
||||
|
||||
**测试用例总数**: 0个
|
||||
|
||||
**需要补充**:
|
||||
|
||||
1. ❌ ArchitectureConstantsTests.cs - 新建(中优先级)
|
||||
2. ❌ GFrameworkConstantsTests.cs - 新建(中优先级)
|
||||
|
||||
---
|
||||
|
||||
### Utility 模块 (1个源文件)
|
||||
|
||||
| 源文件 | 对应测试文件 | 测试覆盖 |
|
||||
|---------------------------|--------|-----------|
|
||||
| AbstractContextUtility.cs | **缺失** | ❌ 需创建测试文件 |
|
||||
|
||||
**测试用例总数**: 0个
|
||||
|
||||
**需要补充**:
|
||||
|
||||
1. ❌ AbstractContextUtilityTests.cs - 新建(高优先级)
|
||||
|
||||
---
|
||||
|
||||
### 协程系统 模块 (11个源文件)
|
||||
|
||||
| 源文件 | 对应测试文件 | 测试覆盖 |
|
||||
|-----------------------|---------------------------------|---------|
|
||||
| CoroutineState.cs | CoroutineStateTests.cs | ✅ 2个测试 |
|
||||
| ITimeSource.cs | CoroutineSchedulerTests.cs (间接) | ✅ 已覆盖 |
|
||||
| IYieldInstruction.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
| CoroutineHandle.cs | CoroutineHandleTests.cs | ✅ 15个测试 |
|
||||
| CoroutineHelper.cs | CoroutineHelperTests.cs | ✅ 19个测试 |
|
||||
| CoroutineMetadata.cs | CoroutineSchedulerTests.cs (间接) | ✅ 已覆盖 |
|
||||
| CoroutineScheduler.cs | CoroutineSchedulerTests.cs | ✅ 25个测试 |
|
||||
| CoroutineSlot.cs | CoroutineSchedulerTests.cs (间接) | ✅ 已覆盖 |
|
||||
| Delay.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
| WaitForCoroutine.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
| WaitForFrames.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
| WaitOneFrame.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
| WaitUntil.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
| WaitWhile.cs | YieldInstructionTests.cs (间接) | ✅ 已覆盖 |
|
||||
|
||||
**测试用例总数**: 61个
|
||||
|
||||
**需要补充**:
|
||||
|
||||
无需补充,协程系统测试覆盖率已达 100%
|
||||
|
||||
---
|
||||
|
||||
### 其他模块 (Events, Logging, IoC, etc.)
|
||||
|
||||
所有其他模块的测试覆盖率均达到 100%(包括间接测试覆盖),详见下文详细列表。
|
||||
|
||||
---
|
||||
|
||||
## 🔴 高优先级 - 异步核心功能(5个任务)
|
||||
|
||||
### 任务1: CommandBusTests.cs - 补充异步测试
|
||||
|
||||
**源文件路径**: `GFramework.Core/command/CommandBus.cs`
|
||||
|
||||
**优先级**: 🔴 高
|
||||
|
||||
**原因**: CommandBus 已实现 SendAsync 方法但没有任何测试
|
||||
|
||||
**需要补充的测试内容**:
|
||||
|
||||
- ✅ SendAsync(IAsyncCommand) 方法 - 执行无返回值的异步命令
|
||||
- ✅ SendAsync(IAsyncCommand) 方法 - 处理 null 异步命令
|
||||
- ✅ SendAsync<TResult>(IAsyncCommand<TResult>) 方法 - 执行有返回值的异步命令
|
||||
- ✅ SendAsync<TResult>(IAsyncCommand<TResult>) 方法 - 处理 null 异步命令
|
||||
|
||||
**预计测试数**: 4 个
|
||||
|
||||
**测试文件**: `GFramework.Core.Tests/command/CommandBusTests.cs`
|
||||
|
||||
**操作**: 在现有测试文件中补充异步测试方法
|
||||
|
||||
**状态**: ❌ 待补充
|
||||
|
||||
---
|
||||
|
||||
### 任务2: AbstractAsyncCommandTests.cs
|
||||
|
||||
**源文件路径**: `GFramework.Core/command/AbstractAsyncCommand.cs`
|
||||
|
||||
**优先级**: 🔴 高
|
||||
|
||||
**原因**: 异步命令基类没有任何单元测试,是核心功能
|
||||
|
||||
**需要测试的内容**:
|
||||
|
||||
- ✅ 异步命令无返回值版本的基础实现
|
||||
- ✅ 异步命令有返回值版本的基础实现
|
||||
- ✅ ExecuteAsync 方法调用
|
||||
- ✅ ExecuteAsync 方法的异常处理
|
||||
- ✅ 上下文感知功能(SetContext, GetContext)
|
||||
- ✅ 日志功能(Logger属性)
|
||||
- ✅ 子类继承行为验证(两个版本)
|
||||
- ✅ 命令执行前日志记录
|
||||
- ✅ 命令执行后日志记录
|
||||
- ✅ 错误情况下的日志记录
|
||||
|
||||
**预计测试数**: 10-12 个
|
||||
|
||||
**创建路径**: `GFramework.Core.Tests/command/AbstractAsyncCommandTests.cs`
|
||||
|
||||
**状态**: ❌ 待创建
|
||||
|
||||
---
|
||||
|
||||
### 任务3: AsyncQueryBusTests.cs
|
||||
|
||||
**源文件路径**: `GFramework.Core/query/AsyncQueryBus.cs`
|
||||
|
||||
**优先级**: 🔴 高
|
||||
|
||||
**原因**: 异步查询总线是核心组件,需要完整的单元测试
|
||||
|
||||
**需要测试的内容**:
|
||||
|
||||
- ✅ SendAsync 方法 - 正常查询发送
|
||||
- ✅ SendAsync 方法 - 空查询异常
|
||||
- ✅ 异步查询结果正确性
|
||||
- ✅ 不同返回类型的异步查询支持
|
||||
- ✅ 异步查询的异常处理
|
||||
- ✅ 异步查询的上下文传递
|
||||
|
||||
**预计测试数**: 6-8 个
|
||||
|
||||
**创建路径**: `GFramework.Core.Tests/query/AsyncQueryBusTests.cs`
|
||||
|
||||
**状态**: ❌ 待创建
|
||||
|
||||
---
|
||||
|
||||
### 任务4: AbstractAsyncQueryTests.cs
|
||||
|
||||
**源文件路径**: `GFramework.Core/query/AbstractAsyncQuery.cs`
|
||||
|
||||
**优先级**: 🔴 高
|
||||
|
||||
**原因**: 异步查询基类没有任何单元测试,是核心功能
|
||||
|
||||
**需要测试的内容**:
|
||||
|
||||
- ✅ 异步查询的基础实现
|
||||
- ✅ DoAsync 方法调用
|
||||
- ✅ DoAsync 方法的异常处理
|
||||
- ✅ 上下文感知功能(SetContext, GetContext)
|
||||
- ✅ 日志功能(Logger属性)
|
||||
- ✅ 子类继承行为验证
|
||||
- ✅ 查询执行前日志记录
|
||||
- ✅ 查询执行后日志记录
|
||||
- ✅ 返回值类型验证
|
||||
- ✅ 错误情况下的日志记录
|
||||
|
||||
**预计测试数**: 8-10 个
|
||||
|
||||
**创建路径**: `GFramework.Core.Tests/query/AbstractAsyncQueryTests.cs`
|
||||
|
||||
**状态**: ❌ 待创建
|
||||
|
||||
---
|
||||
|
||||
### 任务5: AbstractContextUtilityTests.cs
|
||||
|
||||
**源文件路径**: `GFramework.Core/utility/AbstractContextUtility.cs`
|
||||
|
||||
**优先级**: 🔴 高
|
||||
|
||||
**原因**: 工具基类需要直接的单元测试以确保其基础功能正确性
|
||||
|
||||
**需要测试的内容**:
|
||||
|
||||
- ✅ 抽象工具类实现
|
||||
- ✅ IContextUtility 接口实现
|
||||
- ✅ Init 方法调用
|
||||
- ✅ 日志初始化
|
||||
- ✅ 上下文感知功能(SetContext, GetContext)
|
||||
- ✅ 子类继承行为
|
||||
- ✅ 工具初始化日志记录
|
||||
- ✅ 工具生命周期完整性
|
||||
|
||||
**预计测试数**: 6-8 个
|
||||
|
||||
**创建路径**: `GFramework.Core.Tests/utility/AbstractContextUtilityTests.cs`
|
||||
|
||||
**状态**: ❌ 待创建
|
||||
|
||||
---
|
||||
|
||||
## 🟡 中优先级 - 常量验证(2个任务)
|
||||
|
||||
### 任务6: ArchitectureConstantsTests.cs
|
||||
|
||||
**源文件路径**: `GFramework.Core/architecture/ArchitectureConstants.cs`
|
||||
|
||||
**优先级**: 🟡 中
|
||||
|
||||
**原因**: 验证架构相关的常量定义是否正确
|
||||
|
||||
**需要测试的内容**:
|
||||
|
||||
- ✅ 常量值的正确性
|
||||
- ✅ 常量类型验证
|
||||
- ✅ 常量可访问性
|
||||
- ✅ 常量命名规范
|
||||
- ✅ 架构阶段定义常量
|
||||
|
||||
**预计测试数**: 3-5 个
|
||||
|
||||
**创建路径**: `GFramework.Core.Tests/architecture/ArchitectureConstantsTests.cs`
|
||||
|
||||
**状态**: ❌ 待创建
|
||||
|
||||
---
|
||||
|
||||
### 任务7: GFrameworkConstantsTests.cs
|
||||
|
||||
**源文件路径**: `GFramework.Core/constants/GFrameworkConstants.cs`
|
||||
|
||||
**优先级**: 🟡 中
|
||||
|
||||
**原因**: 验证框架级别的常量定义
|
||||
|
||||
**需要测试的内容**:
|
||||
|
||||
- ✅ 版本号常量格式正确性
|
||||
- ✅ 其他框架常量
|
||||
- ✅ 常量值正确性
|
||||
- ✅ 常量类型验证
|
||||
- ✅ 常量可访问性
|
||||
|
||||
**预计测试数**: 3-5 个
|
||||
|
||||
**创建路径**: `GFramework.Core.Tests/constants/GFrameworkConstantsTests.cs`
|
||||
|
||||
**状态**: ❌ 待创建
|
||||
|
||||
---
|
||||
|
||||
## 📊 测试执行计划
|
||||
|
||||
### 第一批:异步核心功能(4个任务,预计 1.5小时)
|
||||
|
||||
| 序号 | 测试任务 | 操作 | 预计测试数 | 优先级 | 预计时间 |
|
||||
|----|------------------------------|----|-------|------|------|
|
||||
| 1 | CommandBusTests.cs - 补充异步测试 | 补充 | 4 | 🔴 高 | 20分钟 |
|
||||
| 2 | AbstractAsyncCommandTests.cs | 新建 | 10-12 | 🔴 高 | 30分钟 |
|
||||
| 3 | AsyncQueryBusTests.cs | 新建 | 6-8 | 🔴 高 | 25分钟 |
|
||||
| 4 | AbstractAsyncQueryTests.cs | 新建 | 8-10 | 🔴 高 | 25分钟 |
|
||||
|
||||
**小计**: 28-34 个测试,约 1.5小时
|
||||
|
||||
---
|
||||
|
||||
### 第二批:工具基类(1个任务,预计 15分钟)
|
||||
|
||||
| 序号 | 测试文件 | 操作 | 预计测试数 | 优先级 | 预计时间 |
|
||||
|----|--------------------------------|----|-------|------|------|
|
||||
| 5 | AbstractContextUtilityTests.cs | 新建 | 6-8 | 🔴 高 | 15分钟 |
|
||||
|
||||
**小计**: 6-8 个测试
|
||||
|
||||
---
|
||||
|
||||
### 第三批:常量验证(2个任务,预计 20分钟)
|
||||
|
||||
| 序号 | 测试文件 | 操作 | 预计测试数 | 优先级 | 预计时间 |
|
||||
|----|-------------------------------|----|-------|------|------|
|
||||
| 6 | ArchitectureConstantsTests.cs | 新建 | 3-5 | 🟡 中 | 10分钟 |
|
||||
| 7 | GFrameworkConstantsTests.cs | 新建 | 3-5 | 🟡 中 | 10分钟 |
|
||||
|
||||
**小计**: 6-10 个测试
|
||||
|
||||
---
|
||||
|
||||
## 📊 最终统计
|
||||
|
||||
| 批次 | 任务数 | 操作 | 预计测试数 | 预计时间 |
|
||||
|----------|-------|-------------|-----------|---------|
|
||||
| 第一批(异步) | 4 | 3新建+1补充 | 28-34 | 1.5小时 |
|
||||
| 第二批(高优先) | 1 | 新建 | 6-8 | 15分钟 |
|
||||
| 第三批(中优先) | 2 | 新建 | 6-10 | 20分钟 |
|
||||
| **总计** | **7** | **6新建+1补充** | **40-54** | **2小时** |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 目标达成路径
|
||||
|
||||
### 当前状态(2026-01-18)
|
||||
|
||||
- **现有测试数**: 496 个
|
||||
- **文件覆盖率**: 79.2% (38/48个文件有测试覆盖)
|
||||
- **缺失测试**: 40-54 个
|
||||
- **已完成文件**: 38/48
|
||||
- **关键发现**: 异步命令和查询功能完全缺失测试
|
||||
|
||||
### 协程模块新增后状态(2026-01-21)
|
||||
|
||||
- **现有测试数**: 496 + 61 = 557 个
|
||||
- **文件覆盖率**: 83.1% (49/59个文件有测试覆盖)
|
||||
- **协程模块测试**: 61 个(已完成)
|
||||
- **协程模块文件**: 11 个
|
||||
- **关键发现**: 协程系统测试覆盖率已达 100%
|
||||
|
||||
### 补充测试完成后预计
|
||||
|
||||
- **预计测试数**: 557 + 40-54 = 597-611 个
|
||||
- **预计文件覆盖率**: ~95% (56/59)
|
||||
- **代码行覆盖率**: 预计 90%+ (需通过覆盖率工具精确测量)
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
### 注释规范
|
||||
|
||||
- ✅ 生成的测试类需要有注释说明这个测试类具体有哪些测试
|
||||
- ✅ 测试方法需要有注释说明具体测试的是什么
|
||||
- ✅ 对于复杂逻辑的测试方法,需要有标准的行注释说明逻辑,不要使用行尾注释
|
||||
- ✅ 对于类与方法的测试,需要标准的C#文档注释
|
||||
|
||||
### 测试隔离性
|
||||
|
||||
1. ✅ 每个测试文件使用独立的测试辅助类(TestXxxV2, TestXxxV3等)
|
||||
2. ✅ 避免与现有测试类(TestSystem, TestModel)命名冲突
|
||||
3. ✅ 使用 `[SetUp]` 和 `[TearDown]` 确保测试隔离
|
||||
4. ✅ 必要时使用 `[NonParallelizable]` 特性
|
||||
5. ✅ 异步测试需要正确使用 `async/await` 模式
|
||||
|
||||
### 测试命名规范
|
||||
|
||||
- 测试类:`{Component}Tests`
|
||||
- 测试方法:`{Scenario}_Should_{ExpectedOutcome}`
|
||||
- 测试辅助类:`Test{Component}V{Version}`
|
||||
- 异步测试方法建议包含 `Async` 关键字
|
||||
|
||||
### 构建和验证流程
|
||||
|
||||
1. 编写测试代码
|
||||
2. 运行 `dotnet build` 验证编译
|
||||
3. 运行 `dotnet test` 执行测试
|
||||
4. 检查测试通过率
|
||||
5. 修复失败或隔离性问题
|
||||
|
||||
### 异步测试最佳实践
|
||||
|
||||
1. **正确使用 async/await**
|
||||
- 测试方法标记为 `async Task`
|
||||
- 所有异步操作使用 `await`
|
||||
- 不要使用 `.Result` 或 `.Wait()` 导致死锁
|
||||
|
||||
2. **异常测试**
|
||||
- 使用 `Assert.ThrowsAsync<T>` 测试异步异常
|
||||
- 确保异常在正确的位置抛出
|
||||
|
||||
3. **测试辅助类**
|
||||
- 创建模拟的异步命令/查询类
|
||||
- 验证异步操作是否正确执行
|
||||
- 测试并发场景(如需要)
|
||||
|
||||
### 代码覆盖率工具建议
|
||||
|
||||
建议添加 Coverlet 代码覆盖率工具以获得精确的覆盖率数据:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
运行覆盖率命令:
|
||||
|
||||
```bash
|
||||
dotnet test --collect:"XPlat Code Coverage"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新日志
|
||||
|
||||
| 日期 | 操作 | 说明 |
|
||||
|------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 2026-01-16 | 初始创建 | 生成原始测试覆盖清单(包含错误) |
|
||||
| 2026-01-18 | 全面更新(第1版) | 重新检查框架和测试,修正以下问题:<br>1. 删除不存在的ContextAwareStateMachineTests.cs<br>2. 更新实际测试数量为496个<br>3. 添加新增源文件<br>4. 修正文件覆盖率从41%提升至91.5%<br>5. 调整优先级,从26个减少到3个缺失测试文件 |
|
||||
| 2026-01-18 | 全面更新(第2版) | 补充异步命令和异步查询测试计划:<br>1. 发现CommandBus已有SendAsync实现但无测试<br>2. 发现AbstractAsyncCommand、AsyncQueryBus、AbstractAsyncQuery无测试<br>3. 新增4个高优先级异步测试任务<br>4. 更新文件覆盖率从91.5%调整为79.2%(补充异步后)<br>5. 总测试数从40-54调整为目标 |
|
||||
| 2026-01-21 | 协程模块测试完成 | 新增协程系统模块测试:<br>1. 新增协程源文件11个(GFramework.Core和GFramework.Core.Abstractions)<br>2. 创建协程测试文件4个,测试用例61个<br>3. 协程系统测试覆盖率达到100%<br>4. 更新文件覆盖率从79.2%提升至83.1%<br>5. 总测试数从496增加至557个 |
|
||||
|
||||
---
|
||||
|
||||
## 📌 待确认事项
|
||||
|
||||
- [x] 确认优先级划分是否合理
|
||||
- [x] 确认执行计划是否可行
|
||||
- [x] 确认测试用例数量估算是否准确
|
||||
- [x] 确认测试隔离策略是否完整
|
||||
- [ ] 添加代码覆盖率工具配置
|
||||
- [ ] 确定是否需要补充间接测试为直接测试
|
||||
|
||||
---
|
||||
|
||||
## 🎉 成就解锁
|
||||
|
||||
### 已完成的测试覆盖
|
||||
|
||||
✅ **架构系统核心功能** - 147个测试覆盖
|
||||
✅ **事件系统完整功能** - 37个测试覆盖
|
||||
✅ **日志系统完整功能** - 69个测试覆盖
|
||||
✅ **IoC容器** - 21个测试覆盖
|
||||
✅ **状态机系统** - 33个测试覆盖
|
||||
✅ **对象池系统** - 6个测试覆盖
|
||||
✅ **属性系统** - 8个测试覆盖
|
||||
✅ **扩展方法** - 17个测试覆盖
|
||||
✅ **同步命令查询系统** - 通过集成测试覆盖
|
||||
✅ **模型系统** - 通过架构集成测试覆盖
|
||||
✅ **系统基类** - 通过架构集成测试覆盖
|
||||
✅ **协程系统完整功能** - 61个测试覆盖
|
||||
|
||||
### 待补充的异步功能
|
||||
|
||||
❌ **异步命令系统** - AbstractAsyncCommand、CommandBus.SendAsync
|
||||
❌ **异步查询系统** - AsyncQueryBus、AbstractAsyncQuery
|
||||
❌ **工具基类** - AbstractContextUtility
|
||||
❌ **常量验证** - ArchitectureConstants、GFrameworkConstants
|
||||
|
||||
### 测试质量指标
|
||||
|
||||
- **测试用例总数**: 557个
|
||||
- **文件级别覆盖率**: 83.1%
|
||||
- **支持测试的.NET版本**: .NET 8.0, .NET 10.0
|
||||
- **测试框架**: NUnit 3.x
|
||||
- **测试隔离性**: 良好
|
||||
- **测试组织结构**: 清晰(按模块分类)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 实施进度
|
||||
|
||||
### 协程系统测试(已完成)
|
||||
|
||||
- [x] 协程系统测试模块 (61个测试)
|
||||
- [x] CoroutineStateTests.cs (2个测试)
|
||||
- [x] CoroutineHandleTests.cs (15个测试)
|
||||
- [x] CoroutineHelperTests.cs (19个测试)
|
||||
- [x] YieldInstructionTests.cs (25个测试)
|
||||
- [x] CoroutineSchedulerTests.cs (25个测试,包含TestTimeSource辅助类)
|
||||
|
||||
### 第一批:异步核心功能
|
||||
|
||||
- [ ] 任务1: CommandBusTests.cs - 补充异步测试 (4个测试)
|
||||
- [ ] 任务2: AbstractAsyncCommandTests.cs (10-12个测试)
|
||||
- [ ] 任务3: AsyncQueryBusTests.cs (6-8个测试)
|
||||
- [ ] 任务4: AbstractAsyncQueryTests.cs (8-10个测试)
|
||||
|
||||
### 第二批:工具基类
|
||||
|
||||
- [ ] 任务5: AbstractContextUtilityTests.cs (6-8个测试)
|
||||
|
||||
### 第三批:常量验证
|
||||
|
||||
- [ ] 任务6: ArchitectureConstantsTests.cs (3-5个测试)
|
||||
- [ ] 任务7: GFrameworkConstantsTests.cs (3-5个测试)
|
||||
|
||||
---
|
||||
|
||||
**文档维护**: 请在完成每个测试任务后更新本文档的状态和实施进度
|
||||
@ -1,507 +0,0 @@
|
||||
# GFramework.Core 核心框架
|
||||
|
||||
> 一个基于 CQRS、MVC 和事件驱动的轻量级游戏开发架构框架
|
||||
|
||||
## 目录
|
||||
|
||||
- [框架概述](#框架概述)
|
||||
- [核心概念](#核心概念)
|
||||
- [架构图](#架构图)
|
||||
- [快速开始](#快速开始)
|
||||
- [包说明](#包说明)
|
||||
- [组件联动](#组件联动)
|
||||
- [最佳实践](#最佳实践)
|
||||
- [设计理念](#设计理念)
|
||||
|
||||
## 框架概述
|
||||
|
||||
本框架是一个与平台无关的轻量级架构,它结合了多种经典设计模式:
|
||||
|
||||
- **MVC 架构模式** - 清晰的层次划分
|
||||
- **CQRS 模式** - 命令查询职责分离
|
||||
- **IoC/DI** - 依赖注入和控制反转
|
||||
- **事件驱动** - 松耦合的组件通信
|
||||
- **响应式编程** - 可绑定属性和数据流
|
||||
- **阶段式生命周期管理** - 精细化的架构状态控制
|
||||
|
||||
**重要说明**:GFramework.Core 是与平台无关的核心模块,不包含任何 Godot 特定代码。Godot 集成功能在 GFramework.Godot 包中实现。
|
||||
|
||||
### 核心特性
|
||||
|
||||
- **清晰的分层架构** - Model、View、Controller、System、Utility 各司其职
|
||||
- **类型安全** - 基于泛型的组件获取和事件系统
|
||||
- **松耦合** - 通过事件和接口实现组件解耦
|
||||
- **易于测试** - 依赖注入和纯函数设计
|
||||
- **可扩展** - 基于接口的规则体系
|
||||
- **生命周期管理** - 自动的注册和注销机制
|
||||
- **模块化** - 支持架构模块安装
|
||||
- **平台无关** - Core 模块可以在任何 .NET 环境中使用
|
||||
|
||||
## 核心概念
|
||||
|
||||
### 五层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ View / UI │ UI 层:用户界面
|
||||
├─────────────────────────────────────────┤
|
||||
│ Controller │ 控制层:处理用户输入
|
||||
├─────────────────────────────────────────┤
|
||||
│ System │ 逻辑层:业务逻辑
|
||||
├─────────────────────────────────────────┤
|
||||
│ Model │ 数据层:游戏状态
|
||||
├─────────────────────────────────────────┤
|
||||
│ Utility │ 工具层:无状态工具
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 横切关注点
|
||||
|
||||
```
|
||||
Command ──┐
|
||||
Query ──┼──→ 跨层操作(修改/查询数据)
|
||||
Event ──┘
|
||||
```
|
||||
|
||||
### 架构阶段
|
||||
|
||||
```
|
||||
初始化:Init → BeforeUtilityInit → AfterUtilityInit → BeforeModelInit → AfterModelInit → BeforeSystemInit → AfterSystemInit → Ready
|
||||
销毁:Destroy → Destroying → Destroyed
|
||||
```
|
||||
|
||||
## 架构图
|
||||
|
||||
### 整体架构
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ Architecture │ ← 管理所有组件
|
||||
└────────┬─────────┘
|
||||
│
|
||||
┌────────────────────┼────────────────────┐
|
||||
│ │ │
|
||||
┌───▼────┐ ┌───▼────┐ ┌───▼─────┐
|
||||
│ Model │ │ System │ │ Utility │
|
||||
│ 层 │ │ 层 │ │ 层 │
|
||||
└───┬────┘ └───┬────┘ └────────┘
|
||||
│ │
|
||||
│ ┌─────────────┤
|
||||
│ │ │
|
||||
┌───▼────▼───┐ ┌───▼──────┐
|
||||
│ Controller │ │ Command/ │
|
||||
│ 层 │ │ Query │
|
||||
└─────┬──────┘ └──────────┘
|
||||
│
|
||||
┌─────▼─────┐
|
||||
│ View │
|
||||
│ UI │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
### 数据流向
|
||||
|
||||
```
|
||||
用户输入 → Controller → Command → System → Model → Event → Controller → View 更新
|
||||
|
||||
查询流程:Controller → Query → Model → 返回数据
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
本框架采用"约定优于配置"的设计理念,只需 4 步即可搭建完整的架构。
|
||||
|
||||
### 为什么需要这个框架?
|
||||
|
||||
在传统开发中,我们经常遇到这些问题:
|
||||
|
||||
- 代码耦合严重:UI 直接访问游戏逻辑,逻辑直接操作 UI
|
||||
- 难以维护:修改一个功能需要改动多个文件
|
||||
- 难以测试:业务逻辑和 UI 混在一起无法独立测试
|
||||
- 难以复用:代码紧密耦合,无法在其他项目中复用
|
||||
|
||||
本框架通过清晰的分层解决这些问题。
|
||||
|
||||
### 1. 定义架构(Architecture)
|
||||
|
||||
**作用**:Architecture 是整个应用的"中央调度器",负责管理所有组件的生命周期。
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.architecture;
|
||||
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
// 注册 Model - 游戏数据
|
||||
RegisterModel(new PlayerModel());
|
||||
|
||||
// 注册 System - 业务逻辑
|
||||
RegisterSystem(new CombatSystem());
|
||||
|
||||
// 注册 Utility - 工具类
|
||||
RegisterUtility(new StorageUtility());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
|
||||
- **依赖注入**:组件通过上下文获取架构引用
|
||||
- **集中管理**:所有组件注册在一处,一目了然
|
||||
- **生命周期管理**:自动初始化和销毁
|
||||
- **平台无关**:可以在任何 .NET 环境中使用
|
||||
|
||||
### 2. 定义 Model(数据层)
|
||||
|
||||
**作用**:Model 是应用的"数据库",只负责存储和管理状态。
|
||||
|
||||
```csharp
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
// 使用 BindableProperty 实现响应式数据
|
||||
public BindableProperty<int> Health { get; } = new(100);
|
||||
public BindableProperty<int> Gold { get; } = new(0);
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
// Model 中可以监听自己的数据变化
|
||||
Health.Register(hp =>
|
||||
{
|
||||
if (hp <= 0) this.SendEvent(new PlayerDiedEvent());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 也可以不使用 BindableProperty
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public int Health { get; private set; }
|
||||
public int Gold { get; private set; }
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
Health = 100;
|
||||
Gold = 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
|
||||
- **数据响应式**:BindableProperty 让数据变化自动通知监听者
|
||||
- **职责单一**:只存储数据,不包含复杂业务逻辑
|
||||
- **易于测试**:可以独立测试数据逻辑
|
||||
|
||||
### 3. 定义 System(业务逻辑层)
|
||||
|
||||
**作用**:System 是应用的"大脑",处理所有业务逻辑。
|
||||
|
||||
```csharp
|
||||
public class CombatSystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
// System 通过事件驱动,响应游戏中的各种事件
|
||||
this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
|
||||
}
|
||||
|
||||
private void OnEnemyAttack(EnemyAttackEvent e)
|
||||
{
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
|
||||
// 处理业务逻辑:计算伤害、更新数据
|
||||
playerModel.Health.Value -= e.Damage;
|
||||
|
||||
// 发送事件通知其他组件
|
||||
this.SendEvent(new PlayerTookDamageEvent { Damage = e.Damage });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
|
||||
- **事件驱动**:通过事件解耦,不同 System 之间松耦合
|
||||
- **可组合**:多个 System 协同工作,每个专注自己的领域
|
||||
- **易于扩展**:新增功能只需添加新的 System 和事件监听
|
||||
|
||||
### 4. 定义 Controller(控制层)
|
||||
|
||||
**作用**:Controller 是"桥梁",连接 UI 和业务逻辑。
|
||||
|
||||
```csharp
|
||||
public class PlayerController : IController
|
||||
{
|
||||
// 通过依赖注入获取架构
|
||||
private readonly IArchitecture _architecture;
|
||||
|
||||
public PlayerController(IArchitecture architecture)
|
||||
{
|
||||
_architecture = architecture;
|
||||
}
|
||||
|
||||
// 监听模型变化
|
||||
public void Initialize()
|
||||
{
|
||||
var playerModel = _architecture.GetModel<PlayerModel>();
|
||||
|
||||
// 数据绑定:Model 数据变化自动更新 UI
|
||||
playerModel.Health.RegisterWithInitValue(OnHealthChanged);
|
||||
}
|
||||
|
||||
private void OnHealthChanged(int hp)
|
||||
{
|
||||
// 更新 UI 显示
|
||||
UpdateHealthDisplay(hp);
|
||||
}
|
||||
|
||||
private void UpdateHealthDisplay(int hp) { /* UI 更新逻辑 */ }
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
|
||||
- **自动更新 UI**:通过 BindableProperty,数据变化自动反映到界面
|
||||
- **分离关注点**:UI 逻辑和业务逻辑完全分离
|
||||
- **易于测试**:可以通过依赖注入模拟架构进行测试
|
||||
|
||||
### 完成!现在你有了一个完整的架构
|
||||
|
||||
这 4 步完成后,你就拥有了:
|
||||
|
||||
- **清晰的数据层**(Model)
|
||||
- **独立的业务逻辑**(System)
|
||||
- **灵活的控制层**(Controller)
|
||||
- **统一的生命周期管理**(Architecture)
|
||||
|
||||
### 下一步该做什么?
|
||||
|
||||
1. **添加 Command**:封装用户操作(如购买物品、使用技能)
|
||||
2. **添加 Query**:封装数据查询(如查询背包物品数量)
|
||||
3. **添加更多 System**:如任务系统、背包系统、商店系统
|
||||
4. **使用 Utility**:添加工具类(如存档工具、数学工具)
|
||||
5. **使用模块**:通过 IArchitectureModule 扩展架构功能
|
||||
|
||||
## 包说明
|
||||
|
||||
| 包名 | 职责 | 文档 |
|
||||
|------------------|-----------------|------------------------------|
|
||||
| **architecture** | 架构核心,管理所有组件生命周期 | [查看](architecture/README.md) |
|
||||
| **constants** | 框架常量定义 | 本文档 |
|
||||
| **model** | 数据模型层,存储状态 | [查看](model/README.md) |
|
||||
| **system** | 业务逻辑层,处理业务规则 | [查看](system/README.md) |
|
||||
| **controller** | 控制器层,连接视图和逻辑 | (在 Abstractions 中) |
|
||||
| **utility** | 工具类层,提供无状态工具 | [查看](utility/README.md) |
|
||||
| **command** | 命令模式,封装写操作 | [查看](command/README.md) |
|
||||
| **query** | 查询模式,封装读操作 | [查看](query/README.md) |
|
||||
| **events** | 事件系统,组件间通信 | [查看](events/README.md) |
|
||||
| **property** | 可绑定属性,响应式编程 | [查看](property/README.md) |
|
||||
| **ioc** | IoC 容器,依赖注入 | [查看](ioc/README.md) |
|
||||
| **rule** | 规则接口,定义组件约束 | [查看](rule/README.md) |
|
||||
| **extensions** | 扩展方法,简化 API 调用 | [查看](extensions/README.md) |
|
||||
| **logging** | 日志系统,记录运行日志 | [查看](logging/README.md) |
|
||||
| **environment** | 环境接口,提供运行环境信息 | [查看](environment/README.md) |
|
||||
|
||||
## 组件联动
|
||||
|
||||
### 1. 初始化流程
|
||||
|
||||
```
|
||||
创建 Architecture 实例
|
||||
└─> Init()
|
||||
├─> RegisterModel → Model.SetContext() → Model.Init()
|
||||
├─> RegisterSystem → System.SetContext() → System.Init()
|
||||
└─> RegisterUtility → Utility 注册到容器
|
||||
```
|
||||
|
||||
### 2. Command 执行流程
|
||||
|
||||
```
|
||||
Controller.SendCommand(command)
|
||||
└─> command.Execute()
|
||||
└─> command.OnDo() // 子类实现
|
||||
├─> GetModel<T>() // 获取数据
|
||||
├─> 修改 Model 数据
|
||||
└─> SendEvent() // 发送事件
|
||||
```
|
||||
|
||||
### 3. Event 传播流程
|
||||
|
||||
```
|
||||
组件.SendEvent(event)
|
||||
└─> TypeEventSystem.Send(event)
|
||||
└─> 通知所有订阅者
|
||||
├─> Controller 响应 → 更新 UI
|
||||
├─> System 响应 → 执行逻辑
|
||||
└─> Model 响应 → 更新状态
|
||||
```
|
||||
|
||||
### 4. BindableProperty 数据绑定
|
||||
|
||||
```
|
||||
Model: BindableProperty<int> Health = new(100);
|
||||
Controller: Health.RegisterWithInitValue(hp => UpdateUI(hp))
|
||||
修改值: Health.Value = 50 → 触发所有回调 → 更新 UI
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 分层职责原则
|
||||
|
||||
每一层都有明确的职责边界,遵循这些原则能让代码更清晰、更易维护。
|
||||
|
||||
**Model 层**:
|
||||
|
||||
```csharp
|
||||
// 好:只存储数据
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public BindableProperty<int> Health { get; } = new(100);
|
||||
protected override void OnInit() { }
|
||||
}
|
||||
|
||||
// 坏:包含业务逻辑
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public void TakeDamage(int damage) // 业务逻辑应在 System
|
||||
{
|
||||
Health.Value -= damage;
|
||||
if (Health.Value <= 0) Die();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**System 层**:
|
||||
|
||||
```csharp
|
||||
// 好:处理业务逻辑
|
||||
public class CombatSystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
this.RegisterEvent<AttackEvent>(OnAttack);
|
||||
}
|
||||
|
||||
private void OnAttack(AttackEvent e)
|
||||
{
|
||||
var target = this.GetModel<PlayerModel>();
|
||||
int finalDamage = CalculateDamage(e.BaseDamage, target);
|
||||
target.Health.Value -= finalDamage;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 通信方式选择指南
|
||||
|
||||
| 通信方式 | 使用场景 | 优势 |
|
||||
|----------------------|-----------|----------|
|
||||
| **Command** | 用户操作、修改状态 | 可撤销、可记录 |
|
||||
| **Query** | 查询数据、检查条件 | 明确只读意图 |
|
||||
| **Event** | 通知其他组件 | 松耦合、可扩展 |
|
||||
| **BindableProperty** | 数据变化通知 | 自动化、不会遗漏 |
|
||||
|
||||
### 3. 生命周期管理
|
||||
|
||||
**为什么需要注销?**
|
||||
|
||||
忘记注销监听器会导致:
|
||||
|
||||
- **内存泄漏**:对象无法被 GC 回收
|
||||
- **逻辑错误**:已销毁的对象仍在响应事件
|
||||
|
||||
```csharp
|
||||
// 使用 UnRegisterList 统一管理
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
this.RegisterEvent<Event1>(OnEvent1)
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
|
||||
model.Property.Register(OnPropertyChanged)
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_unregisterList.UnRegisterAll();
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 性能优化技巧
|
||||
|
||||
```csharp
|
||||
// 低效:每帧都查询
|
||||
var model = _architecture.GetModel<PlayerModel>(); // 频繁调用
|
||||
|
||||
// 高效:缓存引用
|
||||
private PlayerModel _playerModel;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_playerModel = _architecture.GetModel<PlayerModel>(); // 只查询一次
|
||||
}
|
||||
```
|
||||
|
||||
## 设计理念
|
||||
|
||||
框架的设计遵循 SOLID 原则和经典设计模式。
|
||||
|
||||
### 1. 单一职责原则(SRP)
|
||||
|
||||
- **Model**:只负责存储数据
|
||||
- **System**:只负责处理业务逻辑
|
||||
- **Controller**:只负责协调和输入处理
|
||||
- **Utility**:只负责提供工具方法
|
||||
|
||||
### 2. 开闭原则(OCP)
|
||||
|
||||
- 通过**事件系统**添加新功能,无需修改现有代码
|
||||
- 新的 System 可以监听现有事件,插入自己的逻辑
|
||||
|
||||
### 3. 依赖倒置原则(DIP)
|
||||
|
||||
- 所有组件通过接口交互
|
||||
- 通过 IoC 容器注入依赖
|
||||
- 易于替换实现和编写测试
|
||||
|
||||
### 4. 接口隔离原则(ISP)
|
||||
|
||||
```csharp
|
||||
// 小而专注的接口
|
||||
public interface ICanGetModel : IBelongToArchitecture { }
|
||||
public interface ICanSendCommand : IBelongToArchitecture { }
|
||||
public interface ICanRegisterEvent : IBelongToArchitecture { }
|
||||
|
||||
// 组合需要的能力
|
||||
public interface IController :
|
||||
ICanGetModel,
|
||||
ICanSendCommand,
|
||||
ICanRegisterEvent { }
|
||||
```
|
||||
|
||||
### 5. 组合优于继承
|
||||
|
||||
通过接口组合获得能力,而不是通过继承。
|
||||
|
||||
### 框架核心设计模式
|
||||
|
||||
| 设计模式 | 应用位置 | 解决的问题 | 带来的好处 |
|
||||
|-----------|------------|----------|--------|
|
||||
| **工厂模式** | IoC 容器 | 组件的创建和管理 | 解耦创建逻辑 |
|
||||
| **观察者模式** | Event 系统 | 组件间的通信 | 松耦合通信 |
|
||||
| **命令模式** | Command | 封装操作请求 | 支持撤销重做 |
|
||||
| **策略模式** | System | 不同的业务逻辑 | 易于切换策略 |
|
||||
| **依赖注入** | 整体架构 | 组件间的依赖 | 自动管理依赖 |
|
||||
| **模板方法** | Abstract 类 | 定义算法骨架 | 统一流程规范 |
|
||||
|
||||
### 平台无关性
|
||||
|
||||
- **GFramework.Core**:纯 .NET 库,无任何平台特定代码
|
||||
- **GFramework.Godot**:Godot 特定实现,包含 Node 扩展、GodotLogger 等
|
||||
- 可以轻松将 Core 框架移植到其他平台(Unity、.NET MAUI 等)
|
||||
|
||||
---
|
||||
|
||||
**版本**: 1.0.0
|
||||
**许可证**: Apache 2.0
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,908 +0,0 @@
|
||||
# GFramework.Godot
|
||||
|
||||
> Godot 引擎深度集成 - 为 GFramework 框架提供原生的 Godot 支持
|
||||
|
||||
GFramework.Godot 是 GFramework 框架的 Godot 特定实现,将框架的架构优势与 Godot 引擎的强大功能完美结合。
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [概述](#概述)
|
||||
- [核心特性](#核心特性)
|
||||
- [架构集成](#架构集成)
|
||||
- [Node 扩展方法](#node-扩展方法)
|
||||
- [信号系统](#信号系统)
|
||||
- [节点池化](#节点池化)
|
||||
- [资源管理](#资源管理)
|
||||
- [日志系统](#日志系统)
|
||||
- [完整示例](#完整示例)
|
||||
- [最佳实践](#最佳实践)
|
||||
- [性能特性](#性能特性)
|
||||
|
||||
## 概述
|
||||
|
||||
GFramework.Godot 提供了与 Godot 引擎的深度集成,让开发者能够在保持 GFramework 架构优势的同时,充分利用 Godot
|
||||
的节点系统、信号机制和场景管理功能。
|
||||
|
||||
### 核心设计理念
|
||||
|
||||
- **无缝集成**:框架生命周期与 Godot 节点生命周期自动同步
|
||||
- **类型安全**:保持 GFramework 的强类型特性
|
||||
- **性能优化**:零额外开销的 Godot 集成
|
||||
- **开发效率**:丰富的扩展方法简化常见操作
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 🎯 架构生命周期绑定
|
||||
|
||||
- 自动将框架初始化与 Godot 场景树绑定
|
||||
- 支持节点销毁时的自动清理
|
||||
- 阶段式架构初始化与 Godot `_Ready` 周期同步
|
||||
|
||||
### 🔧 丰富的 Node 扩展方法
|
||||
|
||||
- **50+** 个实用扩展方法
|
||||
- 安全的节点操作和验证
|
||||
- 流畅的场景树遍历和查找
|
||||
- 简化的输入处理
|
||||
|
||||
### 📡 流畅的信号 API
|
||||
|
||||
- 类型安全的信号连接
|
||||
- 链式调用支持
|
||||
- 自动生命周期管理
|
||||
- Godot 信号与框架事件系统的桥接
|
||||
|
||||
### 🏊♂️ 高效的节点池化
|
||||
|
||||
- 专用的 Node 对象池
|
||||
- 自动回收和重用机制
|
||||
- 内存友好的高频节点创建/销毁
|
||||
|
||||
### 📦 智能资源管理
|
||||
|
||||
- 简化的 Godot 资源加载
|
||||
- 类型安全的资源工厂
|
||||
- 缓存和预加载支持
|
||||
|
||||
### 📝 Godot 原生日志
|
||||
|
||||
- 与 Godot 日志系统完全集成
|
||||
- 框架日志自动输出到 Godot 控制台
|
||||
- 可配置的日志级别
|
||||
|
||||
## 架构集成
|
||||
|
||||
### Architecture 基类
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.architecture;
|
||||
|
||||
public class GameArchitecture : AbstractArchitecture
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
// 注册核心模型
|
||||
RegisterModel(new PlayerModel());
|
||||
RegisterModel(new GameModel());
|
||||
|
||||
// 注册系统
|
||||
RegisterSystem(new CombatSystem());
|
||||
RegisterSystem(new AudioSystem());
|
||||
|
||||
// 注册工具类
|
||||
RegisterUtility(new StorageUtility());
|
||||
RegisterUtility(new ResourceLoadUtility());
|
||||
}
|
||||
|
||||
protected override void InstallModules()
|
||||
{
|
||||
// 安装 Godot 特定模块
|
||||
InstallGodotModule(new InputModule());
|
||||
InstallGodotModule(new AudioModule());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Godot 模块系统
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.architecture;
|
||||
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class AudioModule : AbstractGodotModule
|
||||
{
|
||||
// 模块节点本身可以作为 Godot 节点
|
||||
public override Node Node => this;
|
||||
|
||||
public override void Install(IArchitecture architecture)
|
||||
{
|
||||
// 注册音频相关系统
|
||||
architecture.RegisterSystem(new AudioSystem());
|
||||
architecture.RegisterUtility(new AudioUtility());
|
||||
}
|
||||
|
||||
public override void OnAttach(Architecture architecture)
|
||||
{
|
||||
// 模块附加时的初始化
|
||||
Logger.Info("Audio module attached to architecture");
|
||||
}
|
||||
|
||||
public override void OnDetach(Architecture architecture)
|
||||
{
|
||||
// 模块分离时的清理
|
||||
Logger.Info("Audio module detached from architecture");
|
||||
}
|
||||
|
||||
// 响应架构生命周期阶段
|
||||
public override void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case ArchitecturePhase.Ready:
|
||||
// 架构准备就绪,可以开始播放背景音乐
|
||||
PlayBackgroundMusic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Controller 集成
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.extensions;
|
||||
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class PlayerController : Node, IController
|
||||
{
|
||||
private PlayerModel _playerModel;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// 获取模型引用
|
||||
_playerModel = Context.GetModel<PlayerModel>();
|
||||
|
||||
// 注册事件监听,自动与节点生命周期绑定
|
||||
this.RegisterEvent<PlayerInputEvent>(OnPlayerInput)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
// 监听属性变化
|
||||
_playerModel.Health.Register(OnHealthChanged)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
|
||||
private void OnPlayerInput(PlayerInputEvent e)
|
||||
{
|
||||
// 处理玩家输入
|
||||
switch (e.Action)
|
||||
{
|
||||
case "move_left":
|
||||
MovePlayer(-1, 0);
|
||||
break;
|
||||
case "move_right":
|
||||
MovePlayer(1, 0);
|
||||
break;
|
||||
case "attack":
|
||||
Context.SendCommand(new AttackCommand());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHealthChanged(int newHealth)
|
||||
{
|
||||
// 更新 UI
|
||||
var healthBar = GetNode<ProgressBar>("UI/HealthBar");
|
||||
healthBar.Value = newHealth;
|
||||
|
||||
// 播放音效
|
||||
if (newHealth < _playerModel.PreviousHealth)
|
||||
PlayHurtSound();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Node 扩展方法
|
||||
|
||||
GFramework.Godot 提供了 50+ 个 Node 扩展方法,大大简化了 Godot 开发中的常见操作。
|
||||
|
||||
### 🔍 节点查找与验证
|
||||
|
||||
```csharp
|
||||
// 安全的节点获取
|
||||
var player = GetNodeX<Player>("Player"); // 自动 null 检查和类型转换
|
||||
var child = FindChildX<Player>("Player"); // 递归查找子节点
|
||||
|
||||
// 节点验证
|
||||
if (IsValidNode(player))
|
||||
{
|
||||
// 节点有效且在场景树中
|
||||
}
|
||||
|
||||
// 安全的节点遍历
|
||||
this.ForEachChild<Node>(child => {
|
||||
GD.Print($"Found child: {child.Name}");
|
||||
});
|
||||
```
|
||||
|
||||
### 🌊 流畅的场景树操作
|
||||
|
||||
```csharp
|
||||
// 安全的添加子节点
|
||||
var bullet = bulletScene.Instantiate<Bullet>();
|
||||
AddChildX(bullet);
|
||||
|
||||
// 等待节点准备就绪
|
||||
await bullet.WaitUntilReady();
|
||||
|
||||
// 获取父节点
|
||||
var parent = GetParentX<GameLevel>();
|
||||
|
||||
// 安全的节点移除
|
||||
bullet.QueueFreeX(); // 等效于 QueueFree() 但带有验证
|
||||
bullet.FreeX(); // 立即释放(谨慎使用)
|
||||
```
|
||||
|
||||
### 🎮 输入处理简化
|
||||
|
||||
```csharp
|
||||
// 输入处理
|
||||
SetInputAsHandled(); // 标记输入已处理
|
||||
DisableInput(); // 禁用输入
|
||||
EnableInput(); // 启用输入
|
||||
|
||||
// 输入状态检查
|
||||
if (Input.IsActionJustPressed("jump"))
|
||||
{
|
||||
Jump();
|
||||
}
|
||||
```
|
||||
|
||||
### 🔄 异步操作支持
|
||||
|
||||
```csharp
|
||||
// 等待信号
|
||||
await ToSignal(this, SignalName.Ready);
|
||||
|
||||
// 等待条件满足
|
||||
await WaitUntil(() => IsReady);
|
||||
|
||||
// 等待帧结束
|
||||
await WaitUntilProcessFrame();
|
||||
|
||||
// 延迟执行
|
||||
await WaitUntilTimeout(2.0f);
|
||||
```
|
||||
|
||||
## 信号系统
|
||||
|
||||
### SignalBuilder 流畅 API
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.extensions;
|
||||
|
||||
// 基础信号连接
|
||||
this.ConnectSignal(Button.SignalName.Pressed, OnButtonPressed);
|
||||
|
||||
// 流畅的信号构建
|
||||
this.CreateSignalBuilder(Timer.SignalName.Timeout)
|
||||
.WithFlags(ConnectFlags.OneShot) // 单次触发
|
||||
.CallImmediately() // 立即调用一次
|
||||
.Connect(OnTimerTimeout)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
// 多信号连接
|
||||
this.CreateSignalBuilder()
|
||||
.AddSignal(Button.SignalName.Pressed, OnButtonPressed)
|
||||
.AddSignal(Button.SignalName.MouseEntered, OnButtonHover)
|
||||
.AddSignal(Button.SignalName.MouseExited, OnButtonExit)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
```
|
||||
|
||||
### 信号与框架事件桥接
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class UIController : Node, IController
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
// Godot 信号 -> 框架事件
|
||||
this.CreateSignalBuilder(Button.SignalName.Pressed)
|
||||
.Connect(() => {
|
||||
Context.SendEvent(new UIButtonClickEvent { ButtonId = "start_game" });
|
||||
})
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
// 框架事件 -> Godot 信号
|
||||
this.RegisterEvent<HealthChangeEvent>(OnHealthChanged)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
|
||||
private void OnHealthChanged(HealthChangeEvent e)
|
||||
{
|
||||
// 更新 Godot UI
|
||||
var healthBar = GetNode<ProgressBar>("HealthBar");
|
||||
healthBar.Value = e.NewHealth;
|
||||
|
||||
// 发送 Godot 信号
|
||||
EmitSignal(SignalName.HealthUpdated, e.NewHealth);
|
||||
}
|
||||
|
||||
[Signal]
|
||||
public delegate void HealthUpdatedEventHandler(int newHealth);
|
||||
}
|
||||
```
|
||||
|
||||
## 节点池化
|
||||
|
||||
### AbstractNodePoolSystem 使用
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.pool;
|
||||
|
||||
public class BulletPoolSystem : AbstractNodePoolSystem<string, Bullet>
|
||||
{
|
||||
private PackedScene _bulletScene;
|
||||
|
||||
public BulletPoolSystem()
|
||||
{
|
||||
_bulletScene = GD.Load<PackedScene>("res://scenes/Bullet.tscn");
|
||||
}
|
||||
|
||||
protected override Bullet CreateItem(string key)
|
||||
{
|
||||
return _bulletScene.Instantiate<Bullet>();
|
||||
}
|
||||
|
||||
protected override void OnSpawn(Bullet item, string key)
|
||||
{
|
||||
// 重置子弹状态
|
||||
item.Reset();
|
||||
item.Position = Vector3.Zero;
|
||||
item.Visible = true;
|
||||
}
|
||||
|
||||
protected override void OnDespawn(Bullet item)
|
||||
{
|
||||
// 隐藏子弹
|
||||
item.Visible = false;
|
||||
// 移除父节点
|
||||
item.GetParent()?.RemoveChild(item);
|
||||
}
|
||||
|
||||
protected override bool CanDespawn(Bullet item)
|
||||
{
|
||||
// 只有不在使用中的子弹才能回收
|
||||
return !item.IsActive;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 池化系统使用
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class WeaponController : Node, IController
|
||||
{
|
||||
private BulletPoolSystem _bulletPool;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_bulletPool = Context.GetSystem<BulletPoolSystem>();
|
||||
}
|
||||
|
||||
public void Shoot(Vector3 direction)
|
||||
{
|
||||
// 从池中获取子弹
|
||||
var bullet = _bulletPool.Spawn("standard");
|
||||
|
||||
if (bullet != null)
|
||||
{
|
||||
// 设置子弹参数
|
||||
bullet.Direction = direction;
|
||||
bullet.Speed = 10.0f;
|
||||
|
||||
// 添加到场景
|
||||
GetTree().Root.AddChild(bullet);
|
||||
|
||||
// 注册碰撞检测
|
||||
this.RegisterEvent<BulletCollisionEvent>(e => {
|
||||
if (e.Bullet == bullet)
|
||||
{
|
||||
// 回收子弹
|
||||
_bulletPool.Despawn(bullet);
|
||||
}
|
||||
}).UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 资源管理
|
||||
|
||||
### ResourceLoadUtility 使用
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.assets;
|
||||
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class ResourceManager : Node, IController
|
||||
{
|
||||
private ResourceLoadUtility _resourceLoader;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_resourceLoader = new ResourceLoadUtility();
|
||||
}
|
||||
|
||||
public T LoadResource<T>(string path) where T : Resource
|
||||
{
|
||||
return _resourceLoader.LoadResource<T>(path);
|
||||
}
|
||||
|
||||
public async Task<T> LoadResourceAsync<T>(string path) where T : Resource
|
||||
{
|
||||
return await _resourceLoader.LoadResourceAsync<T>(path);
|
||||
}
|
||||
|
||||
public void PreloadResources()
|
||||
{
|
||||
// 预加载常用资源
|
||||
_resourceLoader.PreloadResource<Texture2D>("res://textures/player.png");
|
||||
_resourceLoader.PreloadResource<AudioStream>("res://audio/shoot.wav");
|
||||
_resourceLoader.PreloadResource<PackedScene>("res://scenes/enemy.tscn");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义资源工厂
|
||||
|
||||
```csharp
|
||||
public class GameResourceFactory : AbstractResourceFactoryUtility
|
||||
{
|
||||
protected override void RegisterFactories()
|
||||
{
|
||||
RegisterFactory<PlayerData>(CreatePlayerData);
|
||||
RegisterFactory<WeaponConfig>(CreateWeaponConfig);
|
||||
RegisterFactory<LevelData>(CreateLevelData);
|
||||
}
|
||||
|
||||
private PlayerData CreatePlayerData(string path)
|
||||
{
|
||||
var config = LoadJson<PlayerConfig>(path);
|
||||
return new PlayerData
|
||||
{
|
||||
MaxHealth = config.MaxHealth,
|
||||
Speed = config.Speed,
|
||||
JumpForce = config.JumpForce
|
||||
};
|
||||
}
|
||||
|
||||
private WeaponConfig CreateWeaponConfig(string path)
|
||||
{
|
||||
var data = LoadJson<WeaponJsonData>(path);
|
||||
return new WeaponConfig
|
||||
{
|
||||
Damage = data.Damage,
|
||||
FireRate = data.FireRate,
|
||||
BulletPrefab = LoadResource<PackedScene>(data.BulletPath)
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 日志系统
|
||||
|
||||
### GodotLogger 使用
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.logging;
|
||||
|
||||
[ContextAware]
|
||||
[Log] // 自动生成 Logger 字段
|
||||
public partial class GameController : Node, IController
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
// 使用自动生成的 Logger
|
||||
Logger.Info("Game controller ready");
|
||||
|
||||
try
|
||||
{
|
||||
InitializeGame();
|
||||
Logger.Info("Game initialized successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Failed to initialize game: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
Logger.Debug("Starting game");
|
||||
|
||||
// 发送游戏开始事件
|
||||
Context.SendEvent(new GameStartEvent());
|
||||
|
||||
Logger.Info("Game started");
|
||||
}
|
||||
|
||||
public void PauseGame()
|
||||
{
|
||||
Logger.Info("Game paused");
|
||||
Context.SendEvent(new GamePauseEvent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 日志配置
|
||||
|
||||
```csharp
|
||||
public class GodotLoggerFactoryProvider : ILoggerFactoryProvider
|
||||
{
|
||||
public ILoggerFactory CreateFactory()
|
||||
{
|
||||
return new GodotLoggerFactory(new LoggerProperties
|
||||
{
|
||||
MinLevel = LogLevel.Debug,
|
||||
IncludeTimestamp = true,
|
||||
IncludeCallerInfo = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 在架构初始化时配置日志
|
||||
public class GameArchitecture : AbstractArchitecture
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
// 配置 Godot 日志工厂
|
||||
LoggerProperties = new LoggerProperties
|
||||
{
|
||||
LoggerFactoryProvider = new GodotLoggerFactoryProvider(),
|
||||
MinLevel = LogLevel.Info
|
||||
};
|
||||
|
||||
// 注册组件...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 简单射击游戏示例
|
||||
|
||||
```csharp
|
||||
// 1. 定义架构
|
||||
public class ShooterGameArchitecture : AbstractArchitecture
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
// 注册模型
|
||||
RegisterModel(new PlayerModel());
|
||||
RegisterModel(new GameModel());
|
||||
RegisterModel(new ScoreModel());
|
||||
|
||||
// 注册系统
|
||||
RegisterSystem(new PlayerControllerSystem());
|
||||
RegisterSystem(new BulletPoolSystem());
|
||||
RegisterSystem(new EnemySpawnSystem());
|
||||
RegisterSystem(new CollisionSystem());
|
||||
|
||||
// 注册工具
|
||||
RegisterUtility(new StorageUtility());
|
||||
RegisterUtility(new ResourceLoadUtility());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 玩家控制器
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class PlayerController : CharacterBody2D, IController
|
||||
{
|
||||
private PlayerModel _playerModel;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_playerModel = Context.GetModel<PlayerModel>();
|
||||
|
||||
// 输入处理
|
||||
SetProcessInput(true);
|
||||
|
||||
// 注册事件
|
||||
this.RegisterEvent<PlayerDamageEvent>(OnDamage)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
var inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
|
||||
Velocity = inputDir * _playerModel.Speed.Value;
|
||||
MoveAndSlide();
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (@event.IsActionPressed("shoot"))
|
||||
{
|
||||
Shoot();
|
||||
}
|
||||
}
|
||||
|
||||
private void Shoot()
|
||||
{
|
||||
if (CanShoot())
|
||||
{
|
||||
var bulletPool = Context.GetSystem<BulletPoolSystem>();
|
||||
var bullet = bulletPool.Spawn("player");
|
||||
|
||||
if (bullet != null)
|
||||
{
|
||||
var direction = GetGlobalMousePosition() - GlobalPosition;
|
||||
bullet.Initialize(GlobalPosition, direction.Normalized());
|
||||
GetTree().Root.AddChild(bullet);
|
||||
|
||||
Context.SendEvent(new BulletFiredEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDamage(PlayerDamageEvent e)
|
||||
{
|
||||
_playerModel.Health.Value -= e.Damage;
|
||||
|
||||
if (_playerModel.Health.Value <= 0)
|
||||
{
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
Logger.Info("Player died");
|
||||
Context.SendEvent(new PlayerDeathEvent());
|
||||
QueueFreeX();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 主场景
|
||||
[ContextAware]
|
||||
[Log]
|
||||
public partial class MainScene : Node2D
|
||||
{
|
||||
private ShooterGameArchitecture _architecture;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// 初始化架构
|
||||
_architecture = new ShooterGameArchitecture();
|
||||
_architecture.Initialize();
|
||||
|
||||
// 创建玩家
|
||||
var playerScene = GD.Load<PackedScene>("res://scenes/Player.tscn");
|
||||
var player = playerScene.Instantiate<PlayerController>();
|
||||
AddChild(player);
|
||||
|
||||
// 注册全局事件
|
||||
this.RegisterEvent<PlayerDeathEvent>(OnPlayerDeath)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
this.RegisterEvent<GameWinEvent>(OnGameWin)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
Logger.Info("Game started");
|
||||
}
|
||||
|
||||
private void OnPlayerDeath(PlayerDeathEvent e)
|
||||
{
|
||||
Logger.Info("Game over");
|
||||
ShowGameOverScreen();
|
||||
}
|
||||
|
||||
private void OnGameWin(GameWinEvent e)
|
||||
{
|
||||
Logger.Info("Victory!");
|
||||
ShowVictoryScreen();
|
||||
}
|
||||
|
||||
private void ShowGameOverScreen()
|
||||
{
|
||||
var gameOverScene = GD.Load<PackedScene>("res://ui/GameOver.tscn");
|
||||
var gameOverUI = gameOverScene.Instantiate<Control>();
|
||||
AddChild(gameOverUI);
|
||||
}
|
||||
|
||||
private void ShowVictoryScreen()
|
||||
{
|
||||
var victoryScene = GD.Load<PackedScene>("res://ui/Victory.tscn");
|
||||
var victoryUI = victoryScene.Instantiate<Control>();
|
||||
AddChild(victoryUI);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 🏗️ 架构设计最佳实践
|
||||
|
||||
#### 1. 模块化设计
|
||||
|
||||
```csharp
|
||||
// 好的做法:按功能分组模块
|
||||
public class AudioModule : AbstractGodotModule { }
|
||||
public class InputModule : AbstractGodotModule { }
|
||||
public class UIModule : AbstractGodotModule { }
|
||||
|
||||
// 避免的功能过于庞大的单一模块
|
||||
public class GameModule : AbstractGodotModule // ❌ 太大
|
||||
{
|
||||
// 音频、输入、UI、逻辑全部混在一起
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 生命周期管理
|
||||
|
||||
```csharp
|
||||
// 好的做法:使用自动清理
|
||||
this.RegisterEvent<GameEvent>(OnGameEvent)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
model.Property.Register(OnPropertyChange)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
// 避免手动管理清理
|
||||
private IUnRegister _eventRegister;
|
||||
public override void _Ready()
|
||||
{
|
||||
_eventRegister = this.RegisterEvent<GameEvent>(OnGameEvent);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
_eventRegister?.UnRegister(); // 容易忘记
|
||||
}
|
||||
```
|
||||
|
||||
### 🎮 Godot 集成最佳实践
|
||||
|
||||
#### 1. 节点安全操作
|
||||
|
||||
```csharp
|
||||
// 好的做法:使用安全扩展
|
||||
var player = GetNodeX<Player>("Player");
|
||||
var child = FindChildX<HealthBar>("HealthBar");
|
||||
|
||||
// 避免的直接节点访问
|
||||
var player = GetNode<Player>("Player"); // 可能抛出异常
|
||||
```
|
||||
|
||||
#### 2. 信号连接模式
|
||||
|
||||
```csharp
|
||||
// 好的做法:使用 SignalBuilder
|
||||
this.CreateSignalBuilder(Button.SignalName.Pressed)
|
||||
.UnRegisterWhenNodeExitTree(this)
|
||||
.Connect(OnButtonPressed);
|
||||
|
||||
// 避免的原始方式
|
||||
Button.Pressed += OnButtonPressed; // 容易忘记清理
|
||||
```
|
||||
|
||||
### 🏊♂️ 性能优化最佳实践
|
||||
|
||||
#### 1. 节点池化策略
|
||||
|
||||
```csharp
|
||||
// 好的做法:高频创建对象使用池化
|
||||
public class BulletPool : AbstractNodePoolSystem<string, Bullet>
|
||||
{
|
||||
// 为不同类型的子弹创建不同的池
|
||||
}
|
||||
|
||||
// 避免的频繁创建销毁
|
||||
public void Shoot()
|
||||
{
|
||||
var bullet = bulletScene.Instantiate(); // ❌ 性能问题
|
||||
// ...
|
||||
bullet.QueueFree();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 资源预加载
|
||||
|
||||
```csharp
|
||||
// 好的做法:预加载常用资源
|
||||
public override void _Ready()
|
||||
{
|
||||
var resourceLoader = new ResourceLoadUtility();
|
||||
resourceLoader.PreloadResource<Texture2D>("res://textures/bullet.png");
|
||||
resourceLoader.PreloadResource<AudioStream>("res://audio/shoot.wav");
|
||||
}
|
||||
```
|
||||
|
||||
### 🔧 调试和错误处理
|
||||
|
||||
#### 1. 日志使用策略
|
||||
|
||||
```csharp
|
||||
// 好的做法:分级别记录
|
||||
Logger.Debug($"Player position: {Position}"); // 调试信息
|
||||
Logger.Info("Game started"); // 重要状态
|
||||
Logger.Warning($"Low health: {_playerModel.Health}"); // 警告
|
||||
Logger.Error($"Failed to load resource: {path}"); // 错误
|
||||
|
||||
// 避免的过度日志
|
||||
Logger.Debug($"Frame: {Engine.GetProcessFrames()}"); // 太频繁
|
||||
```
|
||||
|
||||
#### 2. 异常处理
|
||||
|
||||
```csharp
|
||||
// 好的做法:优雅的错误处理
|
||||
public T LoadResource<T>(string path) where T : Resource
|
||||
{
|
||||
try
|
||||
{
|
||||
return GD.Load<T>(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Failed to load resource {path}: {ex.Message}");
|
||||
return GetDefaultResource<T>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能特性
|
||||
|
||||
### 📊 内存管理
|
||||
|
||||
- **节点池化**:减少 GC 压力,提高频繁创建/销毁对象的性能
|
||||
- **资源缓存**:自动缓存已加载的 Godot 资源
|
||||
- **生命周期管理**:自动清理事件监听器和资源引用
|
||||
|
||||
### ⚡ 运行时性能
|
||||
|
||||
- **零分配**:扩展方法避免不必要的对象分配
|
||||
- **编译时优化**:Source Generators 减少运行时开销
|
||||
- **类型安全**:编译时类型检查,避免运行时错误
|
||||
|
||||
### 🔄 异步支持
|
||||
|
||||
- **信号等待**:使用 `await ToSignal()` 简化异步代码
|
||||
- **条件等待**:`WaitUntil()` 和 `WaitUntilTimeout()` 简化异步逻辑
|
||||
- **场景树等待**:`WaitUntilReady()` 确保节点准备就绪
|
||||
|
||||
---
|
||||
|
||||
## 依赖关系
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[GFramework.Godot] --> B[GFramework.Game]
|
||||
A --> C[GFramework.Game.Abstractions]
|
||||
A --> D[GFramework.Core.Abstractions]
|
||||
A --> E[Godot.SourceGenerators]
|
||||
A --> F[GodotSharpEditor]
|
||||
```
|
||||
|
||||
## 版本兼容性
|
||||
|
||||
- **Godot**: 4.5.1+
|
||||
- **.NET**: 6.0+
|
||||
- **GFramework.Core**: 与 Core 模块版本保持同步
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目基于 Apache 2.0 许可证 - 详情请参阅 [LICENSE](../LICENSE) 文件。
|
||||
|
||||
---
|
||||
|
||||
**版本**: 1.0.0
|
||||
**更新日期**: 2026-01-12
|
||||
@ -1,997 +0,0 @@
|
||||
# GFramework.SourceGenerators
|
||||
|
||||
> 编译时代码生成 - 零运行时开销的代码增强工具
|
||||
|
||||
GFramework.SourceGenerators 是 GFramework 框架的源代码生成器包,通过编译时分析自动生成样板代码,显著提升开发效率并减少运行时开销。
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [概述](#概述)
|
||||
- [核心特性](#核心特性)
|
||||
- [安装配置](#安装配置)
|
||||
- [Log 属性生成器](#log-属性生成器)
|
||||
- [ContextAware 属性生成器](#contextaware-属性生成器)
|
||||
- [GenerateEnumExtensions 属性生成器](#generateenumextensions-属性生成器)
|
||||
- [诊断信息](#诊断信息)
|
||||
- [性能优势](#性能优势)
|
||||
- [使用示例](#使用示例)
|
||||
- [最佳实践](#最佳实践)
|
||||
- [常见问题](#常见问题)
|
||||
|
||||
## 概述
|
||||
|
||||
GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译时分析你的代码并自动生成常用的样板代码,让开发者专注于业务逻辑而不是重复的模板代码。
|
||||
|
||||
### 核心设计理念
|
||||
|
||||
- **零运行时开销**:代码在编译时生成,无反射或动态调用
|
||||
- **类型安全**:编译时类型检查,避免运行时错误
|
||||
- **开发效率**:自动生成样板代码,减少重复工作
|
||||
- **可配置性**:支持多种配置选项满足不同需求
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 🎯 主要生成器
|
||||
|
||||
- **[Log] 属性**:自动生成 ILogger 字段和日志方法
|
||||
- **[ContextAware] 属性**:自动实现 IContextAware 接口
|
||||
- **[GenerateEnumExtensions] 属性**:自动生成枚举扩展方法
|
||||
|
||||
### 🔧 高级特性
|
||||
|
||||
- **智能诊断**:生成器包含详细的错误诊断信息
|
||||
- **增量编译**:只生成修改过的代码,提高编译速度
|
||||
- **命名空间控制**:灵活控制生成代码的命名空间
|
||||
- **可访问性控制**:支持不同的访问修饰符
|
||||
|
||||
## 安装配置
|
||||
|
||||
### NuGet 包安装
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GeWuYou.GFramework.SourceGenerators" Version="1.0.0" />
|
||||
<PackageReference Include="GeWuYou.GFramework.SourceGenerators.Attributes" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
### 项目文件配置
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
## Log 属性生成器
|
||||
|
||||
[Log] 属性自动为标记的类生成日志记录功能,包括 ILogger 字段和便捷的日志方法。
|
||||
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
|
||||
[Log]
|
||||
public partial class PlayerController
|
||||
{
|
||||
public void DoSomething()
|
||||
{
|
||||
Logger.Info("Doing something"); // 自动生成的 Logger 字段
|
||||
Logger.Debug("Debug information");
|
||||
Logger.Warning("Warning message");
|
||||
Logger.Error("Error occurred");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 生成的代码
|
||||
|
||||
编译器会自动生成如下代码:
|
||||
|
||||
```csharp
|
||||
// <auto-generated/>
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace YourNamespace
|
||||
{
|
||||
public partial class PlayerController
|
||||
{
|
||||
private static readonly ILogger Logger =
|
||||
LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger<PlayerController>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 高级配置
|
||||
|
||||
```csharp
|
||||
[Log(
|
||||
fieldName = "CustomLogger", // 自定义字段名
|
||||
accessModifier = AccessModifier.Public, // 访问修饰符
|
||||
isStatic = false, // 是否为静态字段
|
||||
loggerName = "Custom.PlayerLogger", // 自定义日志器名称
|
||||
includeLoggerInterface = true // 是否包含 ILogger 接口
|
||||
)]
|
||||
public partial class CustomLoggerExample
|
||||
{
|
||||
public void LogSomething()
|
||||
{
|
||||
CustomLogger.LogInformation("Custom logger message");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 配置选项说明
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|--------------------------|----------------|----------|---------------------|
|
||||
| `fieldName` | string | "Logger" | 生成的日志字段名称 |
|
||||
| `accessModifier` | AccessModifier | Private | 字段访问修饰符 |
|
||||
| `isStatic` | bool | true | 是否生成静态字段 |
|
||||
| `loggerName` | string | null | 自定义日志器名称,null 时使用类名 |
|
||||
| `includeLoggerInterface` | bool | false | 是否包含 ILogger 接口实现 |
|
||||
|
||||
### 静态类支持
|
||||
|
||||
```csharp
|
||||
[Log]
|
||||
public static partial class MathHelper
|
||||
{
|
||||
public static int Add(int a, int b)
|
||||
{
|
||||
Logger.Debug($"Adding {a} and {b}");
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 日志级别控制
|
||||
|
||||
```csharp
|
||||
[Log(minLevel = LogLevel.Warning)]
|
||||
public partial class WarningOnlyLogger
|
||||
{
|
||||
public void ProcessData()
|
||||
{
|
||||
Logger.Debug("This won't be logged"); // 低于最小级别,被过滤
|
||||
Logger.Warning("This will be logged");
|
||||
Logger.Error("This will also be logged");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ContextAware 属性生成器
|
||||
|
||||
[ContextAware] 属性自动实现 IContextAware 接口,提供便捷的架构上下文访问能力。
|
||||
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
using GFramework.Core.Abstractions;
|
||||
|
||||
[ContextAware]
|
||||
public partial class PlayerController : IController
|
||||
{
|
||||
public void Initialize()
|
||||
{
|
||||
// Context 属性自动生成,提供架构上下文访问
|
||||
var playerModel = Context.GetModel<PlayerModel>();
|
||||
var combatSystem = Context.GetSystem<CombatSystem>();
|
||||
|
||||
Context.SendEvent(new PlayerInitializedEvent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 生成的代码
|
||||
|
||||
编译器会自动生成如下代码:
|
||||
|
||||
```csharp
|
||||
// <auto-generated/>
|
||||
using GFramework.Core.Abstractions;
|
||||
|
||||
namespace YourNamespace
|
||||
{
|
||||
public partial class PlayerController : IContextAware
|
||||
{
|
||||
private IContextAware.Context _context;
|
||||
|
||||
public IContextAware.Context Context => _context ??= new LazyContext(this);
|
||||
|
||||
public void SetContext(IContextAware.Context context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public IContextAware.Context GetContext()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 延迟初始化
|
||||
|
||||
```csharp
|
||||
[ContextAware(useLazy = true)]
|
||||
public partial class LazyContextExample
|
||||
{
|
||||
public void AccessContext()
|
||||
{
|
||||
// Context 会延迟初始化,直到第一次访问
|
||||
var model = Context.GetModel<SomeModel>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 上下文验证
|
||||
|
||||
```csharp
|
||||
[ContextAware(validateContext = true)]
|
||||
public partial class ValidatedContextExample
|
||||
{
|
||||
public void AccessContext()
|
||||
{
|
||||
// 每次访问都会验证上下文的有效性
|
||||
var model = Context.GetModel<SomeModel>();
|
||||
if (Context.IsInvalid)
|
||||
{
|
||||
throw new InvalidOperationException("Context is invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 与其他属性组合
|
||||
|
||||
```csharp
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class AdvancedController : IController
|
||||
{
|
||||
public void ProcessRequest()
|
||||
{
|
||||
Logger.Info("Processing request");
|
||||
|
||||
var model = Context.GetModel<PlayerModel>();
|
||||
Logger.Info($"Player health: {model.Health}");
|
||||
|
||||
Context.SendCommand(new ProcessCommand());
|
||||
Logger.Debug("Command sent");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## GenerateEnumExtensions 属性生成器
|
||||
|
||||
[GenerateEnumExtensions] 属性为枚举类型生成便捷的扩展方法,提高代码可读性和类型安全性。
|
||||
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
|
||||
[GenerateEnumExtensions]
|
||||
public enum GameState
|
||||
{
|
||||
Playing,
|
||||
Paused,
|
||||
GameOver,
|
||||
Menu
|
||||
}
|
||||
|
||||
// 自动生成的扩展方法:
|
||||
public static class GameStateExtensions
|
||||
{
|
||||
public static bool IsPlaying(this GameState state) => state == GameState.Playing;
|
||||
public static bool IsPaused(this GameState state) => state == GameState.Paused;
|
||||
public static bool IsGameOver(this GameState state) => state == GameState.GameOver;
|
||||
public static bool IsMenu(this GameState state) => state == GameState.Menu;
|
||||
|
||||
public static bool IsIn(this GameState state, params GameState[] values)
|
||||
{
|
||||
return values.Contains(state);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
public class GameManager
|
||||
{
|
||||
private GameState _currentState = GameState.Menu;
|
||||
|
||||
public bool CanProcessInput()
|
||||
{
|
||||
return _currentState.IsPlaying() || _currentState.IsMenu();
|
||||
}
|
||||
|
||||
public bool IsGameOver()
|
||||
{
|
||||
return _currentState.IsGameOver();
|
||||
}
|
||||
|
||||
public bool IsActiveState()
|
||||
{
|
||||
return _currentState.IsIn(GameState.Playing, GameState.Menu);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义扩展方法
|
||||
|
||||
```csharp
|
||||
[GenerateEnumExtensions(
|
||||
generateIsMethods = true,
|
||||
generateHasMethod = true,
|
||||
generateInMethod = true,
|
||||
customPrefix = "Is",
|
||||
includeToString = true
|
||||
)]
|
||||
public enum PlayerState
|
||||
{
|
||||
Idle,
|
||||
Walking,
|
||||
Running,
|
||||
Jumping,
|
||||
Attacking
|
||||
}
|
||||
|
||||
// 生成更多扩展方法
|
||||
public static class PlayerStateExtensions
|
||||
{
|
||||
public static bool IsIdle(this PlayerState state) => state == PlayerState.Idle;
|
||||
public static bool IsWalking(this PlayerState state) => state == PlayerState.Walking;
|
||||
public static bool IsRunning(this PlayerState state) => state == PlayerState.Running;
|
||||
public static bool IsJumping(this PlayerState state) => state == PlayerState.Jumping;
|
||||
public static bool IsAttacking(this PlayerState state) => state == PlayerState.Attacking;
|
||||
|
||||
public static bool HasIdle(this PlayerState state) => state == PlayerState.Idle;
|
||||
public static bool HasWalking(this PlayerState state) => state == PlayerState.Walking;
|
||||
// ... 其他 Has 方法
|
||||
|
||||
public static bool In(this PlayerState state, params PlayerState[] values)
|
||||
{
|
||||
return values.Contains(state);
|
||||
}
|
||||
|
||||
public static string ToDisplayString(this PlayerState state)
|
||||
{
|
||||
return state switch
|
||||
{
|
||||
PlayerState.Idle => "Idle",
|
||||
PlayerState.Walking => "Walking",
|
||||
PlayerState.Running => "Running",
|
||||
PlayerState.Jumping => "Jumping",
|
||||
PlayerState.Attacking => "Attacking",
|
||||
_ => state.ToString()
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 位标志枚举支持
|
||||
|
||||
```csharp
|
||||
[GenerateEnumExtensions]
|
||||
[Flags]
|
||||
public enum PlayerAbilities
|
||||
{
|
||||
None = 0,
|
||||
Jump = 1 << 0,
|
||||
Run = 1 << 1,
|
||||
Attack = 1 << 2,
|
||||
Defend = 1 << 3,
|
||||
Magic = 1 << 4
|
||||
}
|
||||
|
||||
// 生成位标志扩展方法
|
||||
public static class PlayerAbilitiesExtensions
|
||||
{
|
||||
public static bool HasJump(this PlayerAbilities abilities) => abilities.HasFlag(PlayerAbilities.Jump);
|
||||
public static bool HasRun(this PlayerAbilities abilities) => abilities.HasFlag(PlayerAbilities.Run);
|
||||
// ... 其他位标志方法
|
||||
|
||||
public static bool HasAny(this PlayerAbilities abilities, params PlayerAbilities[] flags)
|
||||
{
|
||||
return flags.Any(flag => abilities.HasFlag(flag));
|
||||
}
|
||||
|
||||
public static bool HasAll(this PlayerAbilities abilities, params PlayerAbilities[] flags)
|
||||
{
|
||||
return flags.All(flag => abilities.HasFlag(flag));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 配置选项说明
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|---------------------|--------|-------|------------------------|
|
||||
| `generateIsMethods` | bool | true | 是否生成 IsX() 方法 |
|
||||
| `generateHasMethod` | bool | true | 是否生成 HasX() 方法 |
|
||||
| `generateInMethod` | bool | true | 是否生成 In(params T[]) 方法 |
|
||||
| `customPrefix` | string | "Is" | 方法名前缀 |
|
||||
| `includeToString` | bool | false | 是否生成 ToString 扩展 |
|
||||
| `namespace` | string | null | 生成扩展类的命名空间 |
|
||||
|
||||
## 诊断信息
|
||||
|
||||
GFramework.SourceGenerators 提供详细的编译时诊断信息,帮助开发者快速定位和解决问题。
|
||||
|
||||
### GF_Logging_001 - 日志字段名冲突
|
||||
|
||||
```csharp
|
||||
[Log(fieldName = "Logger")]
|
||||
public partial class ClassWithLogger
|
||||
{
|
||||
private readonly ILogger Logger; // ❌ 冲突!
|
||||
}
|
||||
```
|
||||
|
||||
**错误信息**: `GF_Logging_001: Logger field name 'Logger' conflicts with existing field`
|
||||
|
||||
**解决方案**: 更改字段名或移除冲突字段
|
||||
|
||||
```csharp
|
||||
[Log(fieldName = "CustomLogger")]
|
||||
public partial class ClassWithLogger
|
||||
{
|
||||
private readonly ILogger Logger; // ✅ 不冲突
|
||||
private static readonly ILogger CustomLogger; // ✅ 生成器使用 CustomLogger
|
||||
}
|
||||
```
|
||||
|
||||
### GF_Rule_001 - ContextAware 接口冲突
|
||||
|
||||
```csharp
|
||||
[ContextAware]
|
||||
public partial class AlreadyContextAware : IContextAware // ❌ 冲突!
|
||||
{
|
||||
// 已实现 IContextAware
|
||||
}
|
||||
```
|
||||
|
||||
**错误信息**: `GF_Rule_001: Type already implements IContextAware interface`
|
||||
|
||||
**解决方案**: 移除 [ContextAware] 属性或移除手动实现
|
||||
|
||||
```csharp
|
||||
// 方案1:移除属性
|
||||
public partial class AlreadyContextAware : IContextAware
|
||||
{
|
||||
// 手动实现
|
||||
}
|
||||
|
||||
// 方案2:移除手动实现,使用生成器
|
||||
[ContextAware]
|
||||
public partial class AlreadyContextAware
|
||||
{
|
||||
// 生成器自动实现
|
||||
}
|
||||
```
|
||||
|
||||
### GF_Enum_001 - 枚举成员命名冲突
|
||||
|
||||
```csharp
|
||||
[GenerateEnumExtensions]
|
||||
public enum ConflictEnum
|
||||
{
|
||||
IsPlaying, // ❌ 冲突!会生成 IsIsPlaying()
|
||||
HasJump // ❌ 冲突!会生成 HasHasJump()
|
||||
}
|
||||
```
|
||||
|
||||
**错误信息**: `GF_Enum_001: Enum member name conflicts with generated method`
|
||||
|
||||
**解决方案**: 重命名枚举成员或自定义前缀
|
||||
|
||||
```csharp
|
||||
[GenerateEnumExtensions(customPrefix = "IsState")]
|
||||
public enum ConflictEnum
|
||||
{
|
||||
Playing, // ✅ 生成 IsStatePlaying()
|
||||
Jump // ✅ 生成 IsStateJump()
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优势
|
||||
|
||||
### 编译时 vs 运行时对比
|
||||
|
||||
| 特性 | 手动实现 | 反射实现 | 源码生成器 |
|
||||
|-----------|------|------|-------|
|
||||
| **运行时性能** | 最优 | 最差 | 最优 |
|
||||
| **内存开销** | 最小 | 最大 | 最小 |
|
||||
| **类型安全** | 编译时 | 运行时 | 编译时 |
|
||||
| **开发效率** | 低 | 中 | 高 |
|
||||
| **调试友好** | 好 | 差 | 好 |
|
||||
|
||||
### 基准测试结果
|
||||
|
||||
```csharp
|
||||
// 日志性能对比 (100,000 次调用)
|
||||
// 手动实现: 0.23ms
|
||||
// 反射实现: 45.67ms
|
||||
// 源码生成器: 0.24ms (几乎无差异)
|
||||
|
||||
// 上下文访问性能对比 (1,000,000 次访问)
|
||||
// 手动实现: 0.12ms
|
||||
// 反射实现: 23.45ms
|
||||
// 源码生成器: 0.13ms (几乎无差异)
|
||||
```
|
||||
|
||||
### 内存分配分析
|
||||
|
||||
```csharp
|
||||
// 使用 source generators 的内存分配
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class EfficientController : IController
|
||||
{
|
||||
public void Process()
|
||||
{
|
||||
Logger.Info("Processing"); // 0 分配
|
||||
var model = Context.GetModel<PlayerModel>(); // 0 分配
|
||||
}
|
||||
}
|
||||
|
||||
// 对比反射实现的内存分配
|
||||
public class InefficientController : IController
|
||||
{
|
||||
public void Process()
|
||||
{
|
||||
var logger = GetLoggerViaReflection(); // 每次分配
|
||||
var model = GetModelViaReflection<PlayerModel>(); // 每次分配
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 完整的游戏控制器示例
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
using GFramework.Core.Abstractions;
|
||||
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class GameController : Node, IController
|
||||
{
|
||||
private PlayerModel _playerModel;
|
||||
private CombatSystem _combatSystem;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// 初始化模型和系统引用
|
||||
_playerModel = Context.GetModel<PlayerModel>();
|
||||
_combatSystem = Context.GetSystem<CombatSystem>();
|
||||
|
||||
// 监听事件
|
||||
this.RegisterEvent<PlayerInputEvent>(OnPlayerInput)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
Logger.Info("Game controller initialized");
|
||||
}
|
||||
|
||||
private void OnPlayerInput(PlayerInputEvent e)
|
||||
{
|
||||
Logger.Debug($"Processing player input: {e.Action}");
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case "attack":
|
||||
HandleAttack();
|
||||
break;
|
||||
case "defend":
|
||||
HandleDefend();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAttack()
|
||||
{
|
||||
if (_playerModel.CanAttack())
|
||||
{
|
||||
Logger.Info("Player attacks");
|
||||
_combatSystem.ProcessAttack();
|
||||
Context.SendEvent(new AttackEvent());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning("Player cannot attack - cooldown");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDefend()
|
||||
{
|
||||
if (_playerModel.CanDefend())
|
||||
{
|
||||
Logger.Info("Player defends");
|
||||
_playerModel.IsDefending.Value = true;
|
||||
Context.SendEvent(new DefendEvent());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning("Player cannot defend");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 枚举状态管理示例
|
||||
|
||||
```csharp
|
||||
[GenerateEnumExtensions(
|
||||
generateIsMethods = true,
|
||||
generateHasMethod = true,
|
||||
generateInMethod = true,
|
||||
includeToString = true
|
||||
)]
|
||||
public enum CharacterState
|
||||
{
|
||||
Idle,
|
||||
Walking,
|
||||
Running,
|
||||
Jumping,
|
||||
Falling,
|
||||
Attacking,
|
||||
Hurt,
|
||||
Dead
|
||||
}
|
||||
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class CharacterController : Node, IController
|
||||
{
|
||||
private CharacterModel _characterModel;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_characterModel = Context.GetModel<CharacterModel>();
|
||||
|
||||
// 监听状态变化
|
||||
_characterModel.State.Register(OnStateChanged);
|
||||
}
|
||||
|
||||
private void OnStateChanged(CharacterState newState)
|
||||
{
|
||||
Logger.Info($"Character state changed to: {newState.ToDisplayString()}");
|
||||
|
||||
// 使用生成的扩展方法
|
||||
if (newState.IsDead())
|
||||
{
|
||||
HandleDeath();
|
||||
}
|
||||
else if (newState.IsHurt())
|
||||
{
|
||||
HandleHurt();
|
||||
}
|
||||
else if (newState.In(CharacterState.Walking, CharacterState.Running))
|
||||
{
|
||||
StartMovementEffects();
|
||||
}
|
||||
|
||||
// 检查是否可以接受输入
|
||||
if (newState.In(CharacterState.Idle, CharacterState.Walking, CharacterState.Running))
|
||||
{
|
||||
EnableInput();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisableInput();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanAttack()
|
||||
{
|
||||
var state = _characterModel.State.Value;
|
||||
return state.In(CharacterState.Idle, CharacterState.Walking, CharacterState.Running);
|
||||
}
|
||||
|
||||
private void HandleDeath()
|
||||
{
|
||||
Logger.Info("Character died");
|
||||
DisableInput();
|
||||
PlayDeathAnimation();
|
||||
Context.SendEvent(new CharacterDeathEvent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 🎯 属性使用策略
|
||||
|
||||
#### 1. 合理的属性组合
|
||||
|
||||
```csharp
|
||||
// 好的做法:相关功能组合使用
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class BusinessLogicComponent : IComponent
|
||||
{
|
||||
// 既有日志记录又有上下文访问
|
||||
}
|
||||
|
||||
// 避免:不必要的属性
|
||||
[Log] // ❌ 静态工具类通常不需要日志
|
||||
public static class MathHelper
|
||||
{
|
||||
public static int Add(int a, int b) => a + b;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 命名约定
|
||||
|
||||
```csharp
|
||||
// 好的做法:一致的命名
|
||||
[Log(fieldName = "Logger")]
|
||||
public partial class PlayerController { }
|
||||
|
||||
[Log(fieldName = "Logger")]
|
||||
public partial class EnemyController { }
|
||||
|
||||
// 避免:不一致的命名
|
||||
[Log(fieldName = "Logger")]
|
||||
public partial class PlayerController { }
|
||||
|
||||
[Log(fieldName = "CustomLogger")]
|
||||
public partial class EnemyController { }
|
||||
```
|
||||
|
||||
### 🏗️ 项目组织
|
||||
|
||||
#### 1. 分离生成器和业务逻辑
|
||||
|
||||
```csharp
|
||||
// 好的做法:部分类分离
|
||||
// PlayerController.Logic.cs - 业务逻辑
|
||||
public partial class PlayerController : IController
|
||||
{
|
||||
public void Move(Vector2 direction)
|
||||
{
|
||||
if (CanMove())
|
||||
{
|
||||
UpdatePosition(direction);
|
||||
Logger.Debug($"Player moved to {direction}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PlayerController.Generated.cs - 生成代码所在
|
||||
// 不需要手动维护,由生成器处理
|
||||
```
|
||||
|
||||
#### 2. 枚举设计
|
||||
|
||||
```csharp
|
||||
// 好的做法:有意义的枚举设计
|
||||
[GenerateEnumExtensions]
|
||||
public enum GameState
|
||||
{
|
||||
MainMenu, // 主菜单
|
||||
Playing, // 游戏中
|
||||
Paused, // 暂停
|
||||
GameOver, // 游戏结束
|
||||
Victory // 胜利
|
||||
}
|
||||
|
||||
// 避免:含义不明确的枚举值
|
||||
[GenerateEnumExtensions]
|
||||
public enum State
|
||||
{
|
||||
State1,
|
||||
State2,
|
||||
State3
|
||||
}
|
||||
```
|
||||
|
||||
### 🔧 性能优化
|
||||
|
||||
#### 1. 避免过度日志
|
||||
|
||||
```csharp
|
||||
// 好的做法:合理的日志级别
|
||||
[Log(minLevel = LogLevel.Information)]
|
||||
public partial class PerformanceCriticalComponent
|
||||
{
|
||||
public void Update()
|
||||
{
|
||||
// 只在必要时记录日志
|
||||
if (_performanceCounter % 1000 == 0)
|
||||
{
|
||||
Logger.Debug($"Performance: {_performanceCounter} ticks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 避免:过度日志记录
|
||||
[Log(minLevel = LogLevel.Debug)]
|
||||
public partial class NoisyComponent
|
||||
{
|
||||
public void Update()
|
||||
{
|
||||
Logger.Debug($"Frame: {Engine.GetProcessFrames()}"); // 太频繁
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 延迟上下文初始化
|
||||
|
||||
```csharp
|
||||
// 好的做法:延迟初始化
|
||||
[ContextAware(useLazy = true)]
|
||||
public partial class LazyContextComponent : IComponent
|
||||
{
|
||||
// 只有在第一次访问 Context 时才会初始化
|
||||
public void Initialize()
|
||||
{
|
||||
// 如果这里不需要 Context,就不会初始化
|
||||
SomeOtherInitialization();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 🛡️ 错误处理
|
||||
|
||||
#### 1. 上下文验证
|
||||
|
||||
```csharp
|
||||
[ContextAware(validateContext = true)]
|
||||
public partial class SafeContextComponent : IComponent
|
||||
{
|
||||
public void ProcessData()
|
||||
{
|
||||
if (Context.IsInvalid)
|
||||
{
|
||||
Logger.Error("Context is invalid, cannot process data");
|
||||
return;
|
||||
}
|
||||
|
||||
// 安全地使用 Context
|
||||
var model = Context.GetModel<DataModel>();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 异常处理配合
|
||||
|
||||
```csharp
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class RobustComponent : IComponent
|
||||
{
|
||||
public void RiskyOperation()
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = Context.GetModel<RiskyModel>();
|
||||
model.PerformRiskyOperation();
|
||||
Logger.Info("Operation completed successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Operation failed: {ex.Message}");
|
||||
Context.SendEvent(new OperationFailedEvent { Error = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 为什么需要标记类为 `partial`?
|
||||
|
||||
**A**: 源代码生成器需要向现有类添加代码,`partial` 关键字允许一个类的定义分散在多个文件中。生成器会在编译时创建另一个部分类文件,包含生成的代码。
|
||||
|
||||
```csharp
|
||||
[Log]
|
||||
public partial class MyClass { } // ✅ 需要 partial
|
||||
|
||||
[Log]
|
||||
public class MyClass { } // ❌ 编译错误,无法添加生成代码
|
||||
```
|
||||
|
||||
### Q: 生成的代码在哪里?
|
||||
|
||||
**A**: 生成的代码在编译过程中创建,默认位置在 `obj/Debug/net6.0/generated/` 目录下。可以在项目文件中配置输出位置:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Q: 如何调试生成器问题?
|
||||
|
||||
**A**: 生成器提供了详细的诊断信息:
|
||||
|
||||
1. **查看错误列表**:编译错误会显示在 IDE 中
|
||||
2. **查看生成文件**:检查生成的代码文件
|
||||
3. **启用详细日志**:在项目文件中添加:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Q: 可以自定义生成器吗?
|
||||
|
||||
**A**: 当前版本的生成器支持有限的配置。如需完全自定义,可以创建自己的源代码生成器项目。
|
||||
|
||||
### Q: 性能影响如何?
|
||||
|
||||
**A**: 源代码生成器对运行时性能的影响几乎为零:
|
||||
|
||||
- **编译时**:可能会增加编译时间(通常几秒)
|
||||
- **运行时**:与手写代码性能相同
|
||||
- **内存使用**:与手写代码内存使用相同
|
||||
|
||||
### Q: 与依赖注入框架兼容吗?
|
||||
|
||||
**A**: 完全兼容。生成器创建的是标准代码,可以与任何依赖注入框架配合使用:
|
||||
|
||||
```csharp
|
||||
[Log]
|
||||
[ContextAware]
|
||||
public partial class ServiceComponent : IService
|
||||
{
|
||||
// 可以通过构造函数注入依赖
|
||||
private readonly IDependency _dependency;
|
||||
|
||||
public ServiceComponent(IDependency dependency)
|
||||
{
|
||||
_dependency = dependency;
|
||||
Logger.Info("Service initialized with dependency injection");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 依赖关系
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[GFramework.SourceGenerators] --> B[GFramework.SourceGenerators.Abstractions]
|
||||
A --> C[GFramework.SourceGenerators.Common]
|
||||
A --> D[GFramework.Core.Abstractions]
|
||||
A --> E[Microsoft.CodeAnalysis.CSharp]
|
||||
A --> F[Microsoft.CodeAnalysis.Analyzers]
|
||||
```
|
||||
|
||||
## 版本兼容性
|
||||
|
||||
- **.NET**: 6.0+
|
||||
- **Visual Studio**: 2022 17.0+
|
||||
- **Rider**: 2022.3+
|
||||
- **Roslyn**: 4.0+
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目基于 Apache 2.0 许可证 - 详情请参阅 [LICENSE](../LICENSE) 文件。
|
||||
|
||||
---
|
||||
|
||||
**版本**: 1.0.0
|
||||
**更新日期**: 2026-01-12
|
||||
12
docs/.vitepress/cache/deps/_metadata.json
vendored
12
docs/.vitepress/cache/deps/_metadata.json
vendored
@ -1,25 +1,25 @@
|
||||
{
|
||||
"hash": "38412672",
|
||||
"hash": "74ecdc37",
|
||||
"configHash": "1c302118",
|
||||
"lockfileHash": "0a72f3f9",
|
||||
"browserHash": "8d0bc37a",
|
||||
"lockfileHash": "42b6a898",
|
||||
"browserHash": "b3e735e5",
|
||||
"optimized": {
|
||||
"vue": {
|
||||
"src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "f12583e4",
|
||||
"fileHash": "74f911f6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress > @vue/devtools-api": {
|
||||
"src": "../../../node_modules/@vue/devtools-api/dist/index.js",
|
||||
"file": "vitepress___@vue_devtools-api.js",
|
||||
"fileHash": "d259496e",
|
||||
"fileHash": "673694b5",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress > @vueuse/core": {
|
||||
"src": "../../../node_modules/@vueuse/core/dist/index.js",
|
||||
"file": "vitepress___@vueuse_core.js",
|
||||
"fileHash": "eea92d1c",
|
||||
"fileHash": "6334babc",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,140 +1,507 @@
|
||||
# Core 核心框架概览
|
||||
# GFramework.Core 核心框架
|
||||
|
||||
GFramework.Core 是整个框架的基础,提供了平台无关的核心架构能力。
|
||||
> 一个基于 CQRS、MVC 和事件驱动的轻量级游戏开发架构框架
|
||||
|
||||
## 核心特性
|
||||
## 目录
|
||||
|
||||
### 🏗️ 清洁架构
|
||||
- [框架概述](#框架概述)
|
||||
- [核心概念](#核心概念)
|
||||
- [架构图](#架构图)
|
||||
- [快速开始](#快速开始)
|
||||
- [包说明](#包说明)
|
||||
- [组件联动](#组件联动)
|
||||
- [最佳实践](#最佳实践)
|
||||
- [设计理念](#设计理念)
|
||||
|
||||
基于 Model-View-Controller-System-Utility 五层架构,实现清晰的职责分离和高内聚低耦合。
|
||||
## 框架概述
|
||||
|
||||
### 🔧 CQRS 模式
|
||||
本框架是一个与平台无关的轻量级架构,它结合了多种经典设计模式:
|
||||
|
||||
命令查询职责分离,提供类型安全的命令和查询系统,支持可撤销操作。
|
||||
- **MVC 架构模式** - 清晰的层次划分
|
||||
- **CQRS 模式** - 命令查询职责分离
|
||||
- **IoC/DI** - 依赖注入和控制反转
|
||||
- **事件驱动** - 松耦合的组件通信
|
||||
- **响应式编程** - 可绑定属性和数据流
|
||||
- **阶段式生命周期管理** - 精细化的架构状态控制
|
||||
|
||||
### 📡 事件驱动
|
||||
**重要说明**:GFramework.Core 是与平台无关的核心模块,不包含任何 Godot 特定代码。Godot 集成功能在 GFramework.Godot 包中实现。
|
||||
|
||||
强大的事件总线系统,支持类型安全的事件发布订阅,实现组件间松耦合通信。
|
||||
### 核心特性
|
||||
|
||||
### 🔄 响应式编程
|
||||
- **清晰的分层架构** - Model、View、Controller、System、Utility 各司其职
|
||||
- **类型安全** - 基于泛型的组件获取和事件系统
|
||||
- **松耦合** - 通过事件和接口实现组件解耦
|
||||
- **易于测试** - 依赖注入和纯函数设计
|
||||
- **可扩展** - 基于接口的规则体系
|
||||
- **生命周期管理** - 自动的注册和注销机制
|
||||
- **模块化** - 支持架构模块安装
|
||||
- **平台无关** - Core 模块可以在任何 .NET 环境中使用
|
||||
|
||||
可绑定属性系统,自动化的数据绑定和 UI 更新机制。
|
||||
## 核心概念
|
||||
|
||||
### 🎯 依赖注入
|
||||
### 五层架构
|
||||
|
||||
完善的 IoC 容器,支持自动依赖解析和生命周期管理。
|
||||
|
||||
## 核心组件
|
||||
|
||||
### 架构组件
|
||||
|
||||
- **[Architecture](/core/architecture/architecture)** - 应用架构管理器
|
||||
- **AbstractModel** - 数据模型基类
|
||||
- **AbstractSystem** - 业务系统基类
|
||||
- **AbstractUtility** - 工具类基类
|
||||
|
||||
### 命令查询系统
|
||||
|
||||
- **[AbstractCommand](/core/command-query/commands)** - 命令基类
|
||||
- **AbstractQuery** - 查询基类
|
||||
- **CommandExecutor** - 命令执行器
|
||||
|
||||
### 事件系统
|
||||
|
||||
- **[EventBus](/core/events/event-bus)** - 事件总线
|
||||
- **EasyEvent** - 简单事件
|
||||
- **OrEvent** - 组合事件
|
||||
|
||||
### 属性系统
|
||||
|
||||
- **[BindableProperty](/core/property/bindable-property)** - 可绑定属性
|
||||
- **BindablePropertyGeneric** - 泛型可绑定属性
|
||||
|
||||
### 工具类
|
||||
|
||||
- **[IoC 容器](/core/utilities/ioc-container)** - 依赖注入容器
|
||||
- **Logger** - 日志系统
|
||||
- **ObjectPool** - 对象池
|
||||
|
||||
## 使用场景
|
||||
|
||||
Core 模块适用于任何 .NET 应用场景:
|
||||
|
||||
- **游戏开发** - 作为游戏架构基础
|
||||
- **桌面应用** - WPF、WinForms 应用
|
||||
- **移动应用** - MAUI、Xamarin 应用
|
||||
- **Web 应用** - ASP.NET Core 应用
|
||||
- **控制台应用** - 命令行工具
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
dotnet add package GeWuYou.GFramework.Core
|
||||
dotnet add package GeWuYou.GFramework.Core.Abstractions
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ View / UI │ UI 层:用户界面
|
||||
├─────────────────────────────────────────┤
|
||||
│ Controller │ 控制层:处理用户输入
|
||||
├─────────────────────────────────────────┤
|
||||
│ System │ 逻辑层:业务逻辑
|
||||
├─────────────────────────────────────────┤
|
||||
│ Model │ 数据层:游戏状态
|
||||
├─────────────────────────────────────────┤
|
||||
│ Utility │ 工具层:无状态工具
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 快速示例
|
||||
### 横切关注点
|
||||
|
||||
```
|
||||
Command ──┐
|
||||
Query ──┼──→ 跨层操作(修改/查询数据)
|
||||
Event ──┘
|
||||
```
|
||||
|
||||
### 架构阶段
|
||||
|
||||
```
|
||||
初始化:Init → BeforeUtilityInit → AfterUtilityInit → BeforeModelInit → AfterModelInit → BeforeSystemInit → AfterSystemInit → Ready
|
||||
销毁:Destroy → Destroying → Destroyed
|
||||
```
|
||||
|
||||
## 架构图
|
||||
|
||||
### 整体架构
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ Architecture │ ← 管理所有组件
|
||||
└────────┬─────────┘
|
||||
│
|
||||
┌────────────────────┼────────────────────┐
|
||||
│ │ │
|
||||
┌───▼────┐ ┌───▼────┐ ┌───▼─────┐
|
||||
│ Model │ │ System │ │ Utility │
|
||||
│ 层 │ │ 层 │ │ 层 │
|
||||
└───┬────┘ └───┬────┘ └────────┘
|
||||
│ │
|
||||
│ ┌─────────────┤
|
||||
│ │ │
|
||||
┌───▼────▼───┐ ┌───▼──────┐
|
||||
│ Controller │ │ Command/ │
|
||||
│ 层 │ │ Query │
|
||||
└─────┬──────┘ └──────────┘
|
||||
│
|
||||
┌─────▼─────┐
|
||||
│ View │
|
||||
│ UI │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
### 数据流向
|
||||
|
||||
```
|
||||
用户输入 → Controller → Command → System → Model → Event → Controller → View 更新
|
||||
|
||||
查询流程:Controller → Query → Model → 返回数据
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
本框架采用"约定优于配置"的设计理念,只需 4 步即可搭建完整的架构。
|
||||
|
||||
### 为什么需要这个框架?
|
||||
|
||||
在传统开发中,我们经常遇到这些问题:
|
||||
|
||||
- 代码耦合严重:UI 直接访问游戏逻辑,逻辑直接操作 UI
|
||||
- 难以维护:修改一个功能需要改动多个文件
|
||||
- 难以测试:业务逻辑和 UI 混在一起无法独立测试
|
||||
- 难以复用:代码紧密耦合,无法在其他项目中复用
|
||||
|
||||
本框架通过清晰的分层解决这些问题。
|
||||
|
||||
### 1. 定义架构(Architecture)
|
||||
|
||||
**作用**:Architecture 是整个应用的"中央调度器",负责管理所有组件的生命周期。
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.architecture;
|
||||
|
||||
// 定义架构
|
||||
public class MyAppArchitecture : Architecture
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
RegisterModel(new UserModel());
|
||||
RegisterSystem(new UserService());
|
||||
// 注册 Model - 游戏数据
|
||||
RegisterModel(new PlayerModel());
|
||||
|
||||
// 注册 System - 业务逻辑
|
||||
RegisterSystem(new CombatSystem());
|
||||
|
||||
// 注册 Utility - 工具类
|
||||
RegisterUtility(new StorageUtility());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
// 定义模型
|
||||
public class UserModel : AbstractModel
|
||||
**优势**:
|
||||
|
||||
- **依赖注入**:组件通过上下文获取架构引用
|
||||
- **集中管理**:所有组件注册在一处,一目了然
|
||||
- **生命周期管理**:自动初始化和销毁
|
||||
- **平台无关**:可以在任何 .NET 环境中使用
|
||||
|
||||
### 2. 定义 Model(数据层)
|
||||
|
||||
**作用**:Model 是应用的"数据库",只负责存储和管理状态。
|
||||
|
||||
```csharp
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public BindableProperty<string> Name { get; } = new("User");
|
||||
public BindableProperty<int> Score { get; } = new(0);
|
||||
// 使用 BindableProperty 实现响应式数据
|
||||
public BindableProperty<int> Health { get; } = new(100);
|
||||
public BindableProperty<int> Gold { get; } = new(0);
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
// Model 中可以监听自己的数据变化
|
||||
Health.Register(hp =>
|
||||
{
|
||||
if (hp <= 0) this.SendEvent(new PlayerDiedEvent());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 使用架构
|
||||
var architecture = new MyAppArchitecture();
|
||||
architecture.Initialize();
|
||||
|
||||
var userModel = architecture.GetModel<UserModel>();
|
||||
userModel.Name.Value = "New Name"; // 自动触发更新
|
||||
// 也可以不使用 BindableProperty
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public int Health { get; private set; }
|
||||
public int Gold { get; private set; }
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
Health = 100;
|
||||
Gold = 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 学习路径
|
||||
**优势**:
|
||||
|
||||
建议按以下顺序学习 Core 模块:
|
||||
- **数据响应式**:BindableProperty 让数据变化自动通知监听者
|
||||
- **职责单一**:只存储数据,不包含复杂业务逻辑
|
||||
- **易于测试**:可以独立测试数据逻辑
|
||||
|
||||
1. **[架构基础](/core/architecture/architecture)** - 了解架构组件和生命周期
|
||||
2. **[命令查询](/core/command-query/commands)** - 掌握 CQRS 模式
|
||||
3. **[事件系统](/core/events/event-bus)** - 学习事件驱动编程
|
||||
4. **[属性系统](/core/property/bindable-property)** - 理解响应式编程
|
||||
5. **[工具类](/core/utilities/ioc-container)** - 使用辅助工具
|
||||
### 3. 定义 System(业务逻辑层)
|
||||
|
||||
## 与其他模块的关系
|
||||
**作用**:System 是应用的"大脑",处理所有业务逻辑。
|
||||
|
||||
```
|
||||
GFramework.Core (基础层)
|
||||
↓ 依赖
|
||||
GFramework.Game (游戏层)
|
||||
↓ 依赖
|
||||
GFramework.Godot (引擎层)
|
||||
```csharp
|
||||
public class CombatSystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
// System 通过事件驱动,响应游戏中的各种事件
|
||||
this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
|
||||
}
|
||||
|
||||
private void OnEnemyAttack(EnemyAttackEvent e)
|
||||
{
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
|
||||
// 处理业务逻辑:计算伤害、更新数据
|
||||
playerModel.Health.Value -= e.Damage;
|
||||
|
||||
// 发送事件通知其他组件
|
||||
this.SendEvent(new PlayerTookDamageEvent { Damage = e.Damage });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Core 模块是其他所有模块的基础,提供核心的架构能力和设计模式实现。
|
||||
**优势**:
|
||||
|
||||
## 性能特点
|
||||
- **事件驱动**:通过事件解耦,不同 System 之间松耦合
|
||||
- **可组合**:多个 System 协同工作,每个专注自己的领域
|
||||
- **易于扩展**:新增功能只需添加新的 System 和事件监听
|
||||
|
||||
- **零额外开销** - 编译时优化,运行时无性能损失
|
||||
- **内存友好** - 自动内存管理和对象回收
|
||||
- **类型安全** - 编译时类型检查,避免运行时错误
|
||||
- **可扩展** - 支持自定义扩展和插件
|
||||
### 4. 定义 Controller(控制层)
|
||||
|
||||
## 下一步
|
||||
**作用**:Controller 是"桥梁",连接 UI 和业务逻辑。
|
||||
|
||||
- [深入了解架构组件](/core/architecture/architecture)
|
||||
- [查看完整 API 参考](/api-reference/core-api)
|
||||
- [学习最佳实践](/tutorials/best-practices)
|
||||
```csharp
|
||||
public class PlayerController : IController
|
||||
{
|
||||
// 通过依赖注入获取架构
|
||||
private readonly IArchitecture _architecture;
|
||||
|
||||
public PlayerController(IArchitecture architecture)
|
||||
{
|
||||
_architecture = architecture;
|
||||
}
|
||||
|
||||
// 监听模型变化
|
||||
public void Initialize()
|
||||
{
|
||||
var playerModel = _architecture.GetModel<PlayerModel>();
|
||||
|
||||
// 数据绑定:Model 数据变化自动更新 UI
|
||||
playerModel.Health.RegisterWithInitValue(OnHealthChanged);
|
||||
}
|
||||
|
||||
private void OnHealthChanged(int hp)
|
||||
{
|
||||
// 更新 UI 显示
|
||||
UpdateHealthDisplay(hp);
|
||||
}
|
||||
|
||||
private void UpdateHealthDisplay(int hp) { /* UI 更新逻辑 */ }
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
|
||||
- **自动更新 UI**:通过 BindableProperty,数据变化自动反映到界面
|
||||
- **分离关注点**:UI 逻辑和业务逻辑完全分离
|
||||
- **易于测试**:可以通过依赖注入模拟架构进行测试
|
||||
|
||||
### 完成!现在你有了一个完整的架构
|
||||
|
||||
这 4 步完成后,你就拥有了:
|
||||
|
||||
- **清晰的数据层**(Model)
|
||||
- **独立的业务逻辑**(System)
|
||||
- **灵活的控制层**(Controller)
|
||||
- **统一的生命周期管理**(Architecture)
|
||||
|
||||
### 下一步该做什么?
|
||||
|
||||
1. **添加 Command**:封装用户操作(如购买物品、使用技能)
|
||||
2. **添加 Query**:封装数据查询(如查询背包物品数量)
|
||||
3. **添加更多 System**:如任务系统、背包系统、商店系统
|
||||
4. **使用 Utility**:添加工具类(如存档工具、数学工具)
|
||||
5. **使用模块**:通过 IArchitectureModule 扩展架构功能
|
||||
|
||||
## 包说明
|
||||
|
||||
| 包名 | 职责 | 文档 |
|
||||
|------------------|-----------------|------------------------------------|
|
||||
| **architecture** | 架构核心,管理所有组件生命周期 | [查看](/core/architecture/README.md) |
|
||||
| **constants** | 框架常量定义 | 本文档 |
|
||||
| **model** | 数据模型层,存储状态 | [查看](/core/model/README.md) |
|
||||
| **system** | 业务逻辑层,处理业务规则 | [查看](/core/system/README.md) |
|
||||
| **controller** | 控制器层,连接视图和逻辑 | (在 Abstractions 中) |
|
||||
| **utility** | 工具类层,提供无状态工具 | [查看](/core/utility/README.md) |
|
||||
| **command** | 命令模式,封装写操作 | [查看](/core/command/README.md) |
|
||||
| **query** | 查询模式,封装读操作 | [查看](/core/query/README.md) |
|
||||
| **events** | 事件系统,组件间通信 | [查看](/core/events/README.md) |
|
||||
| **property** | 可绑定属性,响应式编程 | [查看](/core/property/README.md) |
|
||||
| **ioc** | IoC 容器,依赖注入 | [查看](/core/ioc/README.md) |
|
||||
| **rule** | 规则接口,定义组件约束 | [查看](/core/rule/README.md) |
|
||||
| **extensions** | 扩展方法,简化 API 调用 | [查看](/core/extensions/README.md) |
|
||||
| **logging** | 日志系统,记录运行日志 | [查看](/core/logging/README.md) |
|
||||
| **environment** | 环境接口,提供运行环境信息 | [查看](/core/environment/README.md) |
|
||||
|
||||
## 组件联动
|
||||
|
||||
### 1. 初始化流程
|
||||
|
||||
```
|
||||
创建 Architecture 实例
|
||||
└─> Init()
|
||||
├─> RegisterModel → Model.SetContext() → Model.Init()
|
||||
├─> RegisterSystem → System.SetContext() → System.Init()
|
||||
└─> RegisterUtility → Utility 注册到容器
|
||||
```
|
||||
|
||||
### 2. Command 执行流程
|
||||
|
||||
```
|
||||
Controller.SendCommand(command)
|
||||
└─> command.Execute()
|
||||
└─> command.OnDo() // 子类实现
|
||||
├─> GetModel<T>() // 获取数据
|
||||
├─> 修改 Model 数据
|
||||
└─> SendEvent() // 发送事件
|
||||
```
|
||||
|
||||
### 3. Event 传播流程
|
||||
|
||||
```
|
||||
组件.SendEvent(event)
|
||||
└─> TypeEventSystem.Send(event)
|
||||
└─> 通知所有订阅者
|
||||
├─> Controller 响应 → 更新 UI
|
||||
├─> System 响应 → 执行逻辑
|
||||
└─> Model 响应 → 更新状态
|
||||
```
|
||||
|
||||
### 4. BindableProperty 数据绑定
|
||||
|
||||
```
|
||||
Model: BindableProperty<int> Health = new(100);
|
||||
Controller: Health.RegisterWithInitValue(hp => UpdateUI(hp))
|
||||
修改值: Health.Value = 50 → 触发所有回调 → 更新 UI
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 分层职责原则
|
||||
|
||||
每一层都有明确的职责边界,遵循这些原则能让代码更清晰、更易维护。
|
||||
|
||||
**Model 层**:
|
||||
|
||||
```csharp
|
||||
// 好:只存储数据
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public BindableProperty<int> Health { get; } = new(100);
|
||||
protected override void OnInit() { }
|
||||
}
|
||||
|
||||
// 坏:包含业务逻辑
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
public void TakeDamage(int damage) // 业务逻辑应在 System
|
||||
{
|
||||
Health.Value -= damage;
|
||||
if (Health.Value <= 0) Die();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**System 层**:
|
||||
|
||||
```csharp
|
||||
// 好:处理业务逻辑
|
||||
public class CombatSystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
this.RegisterEvent<AttackEvent>(OnAttack);
|
||||
}
|
||||
|
||||
private void OnAttack(AttackEvent e)
|
||||
{
|
||||
var target = this.GetModel<PlayerModel>();
|
||||
int finalDamage = CalculateDamage(e.BaseDamage, target);
|
||||
target.Health.Value -= finalDamage;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 通信方式选择指南
|
||||
|
||||
| 通信方式 | 使用场景 | 优势 |
|
||||
|----------------------|-----------|----------|
|
||||
| **Command** | 用户操作、修改状态 | 可撤销、可记录 |
|
||||
| **Query** | 查询数据、检查条件 | 明确只读意图 |
|
||||
| **Event** | 通知其他组件 | 松耦合、可扩展 |
|
||||
| **BindableProperty** | 数据变化通知 | 自动化、不会遗漏 |
|
||||
|
||||
### 3. 生命周期管理
|
||||
|
||||
**为什么需要注销?**
|
||||
|
||||
忘记注销监听器会导致:
|
||||
|
||||
- **内存泄漏**:对象无法被 GC 回收
|
||||
- **逻辑错误**:已销毁的对象仍在响应事件
|
||||
|
||||
```csharp
|
||||
// 使用 UnRegisterList 统一管理
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
this.RegisterEvent<Event1>(OnEvent1)
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
|
||||
model.Property.Register(OnPropertyChanged)
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_unregisterList.UnRegisterAll();
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 性能优化技巧
|
||||
|
||||
```csharp
|
||||
// 低效:每帧都查询
|
||||
var model = _architecture.GetModel<PlayerModel>(); // 频繁调用
|
||||
|
||||
// 高效:缓存引用
|
||||
private PlayerModel _playerModel;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_playerModel = _architecture.GetModel<PlayerModel>(); // 只查询一次
|
||||
}
|
||||
```
|
||||
|
||||
## 设计理念
|
||||
|
||||
框架的设计遵循 SOLID 原则和经典设计模式。
|
||||
|
||||
### 1. 单一职责原则(SRP)
|
||||
|
||||
- **Model**:只负责存储数据
|
||||
- **System**:只负责处理业务逻辑
|
||||
- **Controller**:只负责协调和输入处理
|
||||
- **Utility**:只负责提供工具方法
|
||||
|
||||
### 2. 开闭原则(OCP)
|
||||
|
||||
- 通过**事件系统**添加新功能,无需修改现有代码
|
||||
- 新的 System 可以监听现有事件,插入自己的逻辑
|
||||
|
||||
### 3. 依赖倒置原则(DIP)
|
||||
|
||||
- 所有组件通过接口交互
|
||||
- 通过 IoC 容器注入依赖
|
||||
- 易于替换实现和编写测试
|
||||
|
||||
### 4. 接口隔离原则(ISP)
|
||||
|
||||
```csharp
|
||||
// 小而专注的接口
|
||||
public interface ICanGetModel : IBelongToArchitecture { }
|
||||
public interface ICanSendCommand : IBelongToArchitecture { }
|
||||
public interface ICanRegisterEvent : IBelongToArchitecture { }
|
||||
|
||||
// 组合需要的能力
|
||||
public interface IController :
|
||||
ICanGetModel,
|
||||
ICanSendCommand,
|
||||
ICanRegisterEvent { }
|
||||
```
|
||||
|
||||
### 5. 组合优于继承
|
||||
|
||||
通过接口组合获得能力,而不是通过继承。
|
||||
|
||||
### 框架核心设计模式
|
||||
|
||||
| 设计模式 | 应用位置 | 解决的问题 | 带来的好处 |
|
||||
|-----------|------------|----------|--------|
|
||||
| **工厂模式** | IoC 容器 | 组件的创建和管理 | 解耦创建逻辑 |
|
||||
| **观察者模式** | Event 系统 | 组件间的通信 | 松耦合通信 |
|
||||
| **命令模式** | Command | 封装操作请求 | 支持撤销重做 |
|
||||
| **策略模式** | System | 不同的业务逻辑 | 易于切换策略 |
|
||||
| **依赖注入** | 整体架构 | 组件间的依赖 | 自动管理依赖 |
|
||||
| **模板方法** | Abstract 类 | 定义算法骨架 | 统一流程规范 |
|
||||
|
||||
### 平台无关性
|
||||
|
||||
- **GFramework.Core**:纯 .NET 库,无任何平台特定代码
|
||||
- **GFramework.Godot**:Godot 特定实现,包含 Node 扩展、GodotLogger 等
|
||||
- 可以轻松将 Core 框架移植到其他平台(Unity、.NET MAUI 等)
|
||||
|
||||
---
|
||||
|
||||
**版本**: 1.0.0
|
||||
**许可证**: Apache 2.0
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user