mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
docs(ecs): 重写 Ecs.Arch 文档入口
- 重写 Ecs.Arch README 与 ecs 栏目页面,使采用路径对齐当前源码和集成测试 - 补充 Ecs.Arch.Abstractions 的 XML inventory 与抽象页阅读链路 - 更新 documentation-full-coverage-governance 的恢复点、验证结果和下一波次计划
This commit is contained in:
parent
af21f16c09
commit
da0ae700a3
@ -33,6 +33,16 @@
|
||||
| `IArchSystemAdapter.cs` | 让 ECS 系统适配到 GFramework `ISystem` 生命周期的接口 |
|
||||
| `ArchOptions.cs` | `WorldCapacity`、`EnableStatistics`、`Priority` 等配置对象 |
|
||||
|
||||
## XML 阅读基线
|
||||
|
||||
下表记录当前契约包的类型声明级 XML 基线,方便把 README、站内抽象页与源码阅读顺序对齐。
|
||||
|
||||
| 类型族 | 代表类型 | XML 状态 | 阅读重点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 模块契约 | `IArchEcsModule` | 已覆盖 | 宿主循环如何统一驱动 ECS 更新 |
|
||||
| 系统桥接契约 | `IArchSystemAdapter<T>` | 已覆盖 | 外部模块怎样只依赖更新接口而不绑定默认实现 |
|
||||
| 配置对象 | `ArchOptions` | 已覆盖 | 跨程序集共享 ECS 配置边界 |
|
||||
|
||||
## 最小接入路径
|
||||
|
||||
### 1. 只想约定宿主循环与 ECS 模块边界
|
||||
|
||||
@ -1,16 +1,30 @@
|
||||
# GFramework.Ecs.Arch
|
||||
|
||||
GFramework 的 Arch ECS 集成包,提供开箱即用的 ECS(Entity Component System)支持。
|
||||
`GFramework.Ecs.Arch` 是 `GFramework` 当前 Arch ECS family 的默认运行时实现包。
|
||||
|
||||
## 特性
|
||||
它负责把 Arch `World`、GFramework 的服务模块生命周期,以及 `ArchSystemAdapter<T>` 系统桥接到同一条采用路径中。
|
||||
如果你需要的只是共享契约,请改为依赖 `GFramework.Ecs.Arch.Abstractions`。
|
||||
|
||||
- 🎯 **显式集成** - 符合 .NET 生态习惯的显式注册方式
|
||||
- 🔌 **零依赖** - 不使用时,Core 包无 Arch 依赖
|
||||
- 🎯 **类型安全** - 完整的类型系统和编译时检查
|
||||
- ⚡ **高性能** - 基于 Arch ECS 的高性能实现
|
||||
- 🔧 **易扩展** - 简单的系统适配器模式
|
||||
## 包定位
|
||||
|
||||
## 快速开始
|
||||
- 这是运行时实现层,不是纯契约层。
|
||||
- 适合需要 `UseArch(...)`、`World` 自动注册、默认模块生命周期和系统桥接基类的项目。
|
||||
- 常见场景:
|
||||
- 在架构实例上显式接入 Arch ECS
|
||||
- 让 `World` 由默认模块创建并放入容器
|
||||
- 让 ECS 系统复用 `ArchSystemAdapter<float>` 生命周期桥接
|
||||
- 通过 `IArchEcsModule.Update(deltaTime)` 统一驱动 ECS 帧更新
|
||||
|
||||
## 与相邻包的关系
|
||||
|
||||
- `GFramework.Core`
|
||||
- 提供架构、容器、生命周期和系统注册基础设施。
|
||||
- `GFramework.Ecs.Arch.Abstractions`
|
||||
- 提供 `IArchEcsModule`、`IArchSystemAdapter<T>` 和契约层 `ArchOptions`。
|
||||
- `GFramework.Ecs.Arch`
|
||||
- 提供 `UseArch(...)`、默认 `ArchEcsModule`、`World` 注册,以及系统适配器基类与示例类型。
|
||||
|
||||
## 最小接入路径
|
||||
|
||||
### 1. 安装包
|
||||
|
||||
@ -18,53 +32,43 @@ GFramework 的 Arch ECS 集成包,提供开箱即用的 ECS(Entity Component
|
||||
dotnet add package GeWuYou.GFramework.Ecs.Arch
|
||||
```
|
||||
|
||||
### 2. 注册 ECS 模块
|
||||
### 2. 在 `Initialize()` 之前显式接入 Arch runtime
|
||||
|
||||
按当前实现,`UseArch(...)` 会把 `ArchEcsModule` 提前登记到 `ArchitectureModuleRegistry`,因此调用时机应早于
|
||||
`Initialize()`。
|
||||
|
||||
```csharp
|
||||
// 在架构初始化时添加 Arch ECS 支持
|
||||
var architecture = new GameArchitecture(config)
|
||||
.UseArch(); // 添加 ECS 支持
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.Ecs.Arch.Extensions;
|
||||
|
||||
architecture.Initialize();
|
||||
```
|
||||
public sealed class GameArchitecture : Architecture
|
||||
{
|
||||
public GameArchitecture() : base(new ArchitectureConfiguration())
|
||||
{
|
||||
}
|
||||
|
||||
### 3. 带配置的注册
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterSystem<MovementSystem>();
|
||||
}
|
||||
}
|
||||
|
||||
```csharp
|
||||
var architecture = new GameArchitecture(config)
|
||||
var architecture = new GameArchitecture()
|
||||
.UseArch(options =>
|
||||
{
|
||||
options.WorldCapacity = 2000;
|
||||
options.EnableStatistics = true;
|
||||
options.WorldCapacity = 2048;
|
||||
options.Priority = 50;
|
||||
});
|
||||
|
||||
architecture.Initialize();
|
||||
```
|
||||
|
||||
```csharp
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Position(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Velocity(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 创建系统
|
||||
### 3. 编写并注册系统
|
||||
|
||||
```csharp
|
||||
using Arch.Core;
|
||||
using GFramework.Ecs.Arch;
|
||||
using GFramework.Ecs.Arch.Components;
|
||||
|
||||
public sealed class MovementSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
@ -78,115 +82,57 @@ public sealed class MovementSystem : ArchSystemAdapter<float>
|
||||
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
World.Query(in _query, (ref Position pos, ref Velocity vel) =>
|
||||
var frameDelta = deltaTime;
|
||||
|
||||
World.Query(in _query, (ref Position position, ref Velocity velocity) =>
|
||||
{
|
||||
pos.X += vel.X * deltaTime;
|
||||
pos.Y += vel.Y * deltaTime;
|
||||
position.X += velocity.X * frameDelta;
|
||||
position.Y += velocity.Y * frameDelta;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 注册系统
|
||||
### 4. 初始化后获取 `World` 与 ECS 模块
|
||||
|
||||
```csharp
|
||||
public class MyArchitecture : Architecture
|
||||
{
|
||||
protected override void OnRegisterSystem(IIocContainer container)
|
||||
{
|
||||
container.Register<MovementSystem>();
|
||||
}
|
||||
}
|
||||
using Arch.Core;
|
||||
using GFramework.Ecs.Arch.Abstractions;
|
||||
|
||||
var world = architecture.Context.GetService<World>();
|
||||
var ecsModule = architecture.Context.GetService<IArchEcsModule>();
|
||||
```
|
||||
|
||||
### 7. 创建实体
|
||||
### 5. 由宿主循环驱动更新
|
||||
|
||||
```csharp
|
||||
var world = this.GetService<World>();
|
||||
var entity = world.Create(
|
||||
new Position(0, 0),
|
||||
new Velocity(1, 1)
|
||||
);
|
||||
```
|
||||
|
||||
### 8. 更新系统
|
||||
|
||||
```csharp
|
||||
var ecsModule = this.GetService<IArchEcsModule>();
|
||||
ecsModule.Update(deltaTime);
|
||||
```
|
||||
|
||||
## 配置选项
|
||||
## 运行时职责地图
|
||||
|
||||
### 代码配置
|
||||
| 文件 | 作用 |
|
||||
| --- | --- |
|
||||
| `Extensions/ArchExtensions.cs` | 通过 `UseArch(...)` 把默认模块注册到 `ArchitectureModuleRegistry` |
|
||||
| `ArchEcsModule.cs` | 创建并注册 `World`,按优先级收集 `ArchSystemAdapter<float>`,负责初始化、销毁和逐帧更新 |
|
||||
| `ArchSystemAdapter.cs` | 把 GFramework 系统生命周期桥接到 Arch `ISystem<T>` 生命周期 |
|
||||
| `ArchOptions.cs` | 承载 `WorldCapacity`、`EnableStatistics`、`Priority` 这组运行时配置 |
|
||||
| `Components/*.cs`、`Systems/*.cs` | 提供最小组件与系统示例,帮助对照查询写法和更新模式 |
|
||||
|
||||
```csharp
|
||||
var architecture = new GameArchitecture(config)
|
||||
.UseArch(options =>
|
||||
{
|
||||
options.WorldCapacity = 2000;
|
||||
options.EnableStatistics = true;
|
||||
options.Priority = 50;
|
||||
});
|
||||
```
|
||||
## XML 阅读基线
|
||||
|
||||
### 配置说明
|
||||
下表记录当前模块 README 与源码可对照的类型声明级 XML 基线。
|
||||
|
||||
- `WorldCapacity` - World 初始容量(默认:1000)
|
||||
- `EnableStatistics` - 是否启用统计信息(默认:false)
|
||||
- `Priority` - 模块优先级(默认:50)
|
||||
| 类型族 | 代表类型 | XML 状态 | 阅读重点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 装配入口 | `ArchExtensions` | 已覆盖 | `UseArch(...)` 的时机与返回值 |
|
||||
| 运行时模块 | `ArchEcsModule` | 已覆盖 | `World` 注册、系统排序、销毁顺序 |
|
||||
| 系统桥接层 | `ArchSystemAdapter<T>` | 已覆盖 | `OnArchInitialize`、`OnUpdate`、`OnArchDispose` |
|
||||
| 示例类型 | `Position`、`Velocity`、`MovementSystem` | 已覆盖 | 组件布局、查询写法、最小示例 |
|
||||
|
||||
## 架构说明
|
||||
## 对应文档入口
|
||||
|
||||
### 显式注册模式
|
||||
|
||||
本包采用 .NET 生态标准的显式注册模式,基于架构实例:
|
||||
|
||||
**优点:**
|
||||
|
||||
- ✅ 符合 .NET 生态习惯
|
||||
- ✅ 显式、可控
|
||||
- ✅ 易于测试和调试
|
||||
- ✅ 支持配置
|
||||
- ✅ 支持链式调用
|
||||
- ✅ 避免"魔法"行为
|
||||
|
||||
**使用方式:**
|
||||
```csharp
|
||||
// 在架构初始化时添加
|
||||
var architecture = new GameArchitecture(config)
|
||||
.UseArch(); // 显式注册
|
||||
|
||||
architecture.Initialize();
|
||||
```
|
||||
|
||||
详见:[INTEGRATION_PATTERN.md](INTEGRATION_PATTERN.md)
|
||||
|
||||
### 系统适配器
|
||||
|
||||
`ArchSystemAdapter<T>` 桥接 Arch.System.ISystem<T> 到 GFramework 架构:
|
||||
|
||||
- 自动获取 World 实例
|
||||
- 集成到框架生命周期
|
||||
- 支持上下文感知(Context-Aware)
|
||||
|
||||
### 生命周期
|
||||
|
||||
1. **注册阶段** - 模块自动注册到架构
|
||||
2. **初始化阶段** - 创建 World,初始化系统
|
||||
3. **运行阶段** - 每帧调用 Update
|
||||
4. **销毁阶段** - 清理资源,销毁 World
|
||||
|
||||
## 示例
|
||||
|
||||
完整示例请参考 `GFramework.Ecs.Arch.Tests` 项目。
|
||||
|
||||
## 依赖
|
||||
|
||||
- GFramework.Core >= 1.0.0
|
||||
- Arch >= 2.1.0
|
||||
- Arch.System >= 1.1.0
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
- ECS 总览:[`../docs/zh-CN/ecs/index.md`](../docs/zh-CN/ecs/index.md)
|
||||
- Arch ECS 集成:[`../docs/zh-CN/ecs/arch.md`](../docs/zh-CN/ecs/arch.md)
|
||||
- 抽象契约页:[`../docs/zh-CN/abstractions/ecs-arch-abstractions.md`](../docs/zh-CN/abstractions/ecs-arch-abstractions.md)
|
||||
- 统一 API / XML 导航:[`../docs/zh-CN/api-reference/index.md`](../docs/zh-CN/api-reference/index.md)
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`DOCUMENTATION-FULL-COVERAGE-GOV-RP-002`
|
||||
- 当前阶段:`Phase 2 - Ecs Runtime Docs Refresh`
|
||||
- 恢复点编号:`DOCUMENTATION-FULL-COVERAGE-GOV-RP-003`
|
||||
- 当前阶段:`Phase 3 - Cqrs Docs Refresh Preparation`
|
||||
- 当前焦点:
|
||||
- 重写 `docs/zh-CN/ecs/index.md` 与 `docs/zh-CN/ecs/arch.md`
|
||||
- 为 `Ecs.Arch` / `Ecs.Arch.Abstractions` 建立 runtime / abstractions / XML 阅读链路
|
||||
- 准备进入 `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` 波次
|
||||
- 延续 `README / landing / API reference / XML inventory` 的同一治理模板
|
||||
- 继续把模块族 inventory 从“入口存在”推进到“可审计的 XML / README / landing 对照表”
|
||||
|
||||
## 当前状态摘要
|
||||
@ -39,16 +39,20 @@
|
||||
- 为 `GFramework.Core/README.md`、`GFramework.Core.Abstractions/README.md` 补齐类型族级 XML 覆盖基线入口
|
||||
- 为 `docs/zh-CN/core/index.md`、`docs/zh-CN/abstractions/core-abstractions.md` 增加“类型族 -> XML 覆盖状态 -> 代表类型”的 inventory
|
||||
- 基于顶层目录轻量盘点确认:`Core` / `Core.Abstractions` 当前公开 / 内部类型声明都已带 XML 注释,成员级审计留待后续波次
|
||||
- 重写 `docs/zh-CN/ecs/index.md`,收敛当前 ECS family 的包边界、采用顺序和 XML inventory
|
||||
- 重写 `docs/zh-CN/ecs/arch.md`,明确 `UseArch(...)` 需早于 `Initialize()` 的真实接入时机
|
||||
- 刷新 `GFramework.Ecs.Arch/README.md`,使运行时 README 与源码 / 测试一致
|
||||
- 为 `GFramework.Ecs.Arch.Abstractions/README.md` 与 `docs/zh-CN/abstractions/ecs-arch-abstractions.md` 补齐类型族级 XML inventory
|
||||
|
||||
## Inventory(第一版)
|
||||
|
||||
| 模块族 | 当前状态 | 当前证据 | 下一动作 |
|
||||
| --- | --- | --- | --- |
|
||||
| `Core` / `Core.Abstractions` | `README / landing / 类型族级 XML inventory 已收口,成员级审计待补齐` | 根 README、模块 README、`docs/zh-CN/core/**`、`docs/zh-CN/abstractions/core-abstractions.md` 已对齐当前目录与类型族基线 | 进入巡检;如有新 API 变更,再追加成员级 XML 审计 |
|
||||
| `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` | `待重写` | README 已存在;站内入口目前分散在 `docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/**` | 在 `Cqrs` 波次里补 dedicated landing / API map 审计 |
|
||||
| `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` | `待重写` | README 已存在;站内入口目前分散在 `docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/**` | 进入下一波次,补 dedicated landing / API map 审计 |
|
||||
| `Game` / `Game.Abstractions` / `Game.SourceGenerators` | `已验证` | 根 README、模块 README、`docs/zh-CN/game/**` 和 abstractions 页已存在 | 后续波次补 XML / 教程链路审计 |
|
||||
| `Godot` / `Godot.SourceGenerators` | `已验证` | 上一轮归档 topic 已完成核心 landing / topic / tutorial 校验 | 进入巡检周期,重点看回漂 |
|
||||
| `Ecs.Arch` / `Ecs.Arch.Abstractions` | `待重写` | runtime README 已存在;`Ecs.Arch.Abstractions` README 与抽象页已在本轮补齐;`docs/zh-CN/ecs/index.md` / `arch.md` 仍偏旧 | 在 `Ecs` 波次重写 runtime docs 并补 XML map |
|
||||
| `Ecs.Arch` / `Ecs.Arch.Abstractions` | `README / landing / abstractions / 类型族级 XML inventory 已收口,成员级审计待补齐` | `GFramework.Ecs.Arch/README.md`、`GFramework.Ecs.Arch.Abstractions/README.md`、`docs/zh-CN/ecs/**`、`docs/zh-CN/abstractions/ecs-arch-abstractions.md` 已对齐当前源码与测试 | 转入巡检;后续仅在运行时公共 API 变动时补成员级 XML 细审 |
|
||||
| `SourceGenerators.Common` 与 `*.SourceGenerators.Abstractions` | `已判定为内部支撑` | `*.csproj` 明确 `IsPackable=false` | 由所属模块 README 与生成器栏目说明 owner,不建独立采用页 |
|
||||
|
||||
## 缺口分级
|
||||
@ -66,8 +70,8 @@
|
||||
|
||||
## 当前风险
|
||||
|
||||
- `docs/zh-CN/ecs/index.md` 与 `docs/zh-CN/ecs/arch.md` 仍保留较多早期表述,本轮只先完成 inventory 与入口补齐
|
||||
- 缓解措施:把 `Ecs.Arch` 排进前五波次,并把 runtime docs 重写列为该波次的必做项
|
||||
- `Cqrs` family 目前仍缺 dedicated landing 与统一 API / XML 阅读链路,站内入口散落在 `core` 与 `source-generators` 栏目
|
||||
- 缓解措施:下一恢复点直接进入 `Cqrs` 波次,按 `Core` / `Ecs` 已验证模板重写入口
|
||||
- 当前 `Core` / `Core.Abstractions` 只完成了类型族级 XML 基线,不等于成员级契约全审计
|
||||
- 缓解措施:后续只在共享抽象或高风险生命周期接口发生改动时补成员级细审,不在本轮扩张范围
|
||||
- 其他模块族尚未全部建立同粒度的 XML inventory
|
||||
@ -100,9 +104,21 @@
|
||||
- `DOTNET_CLI_HOME=/tmp/dotnet-home dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=`
|
||||
- 结果:通过
|
||||
- 备注:`0 Warning(s) / 0 Error(s)`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/ecs/index.md`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在重写 ECS landing 后重新验证
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/ecs/arch.md`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在重写 Arch ECS 专题页后重新验证
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/abstractions/ecs-arch-abstractions.md`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在补充抽象页 XML inventory 后重新验证
|
||||
- `cd docs && bun run build`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在 Ecs 波次重写后重新构建通过;仅保留 VitePress 大 chunk warning,无构建失败
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 进入 `Ecs` 波次,按源码重写 `docs/zh-CN/ecs/index.md` 与 `docs/zh-CN/ecs/arch.md`
|
||||
2. 为 `Ecs.Arch` / `Ecs.Arch.Abstractions` 建立与 `Core` 同粒度的 XML / README / landing inventory
|
||||
1. 进入 `Cqrs` 波次,梳理 `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` 的模块边界与 docs 入口
|
||||
2. 判断是否需要为 `Cqrs` family 新建 dedicated landing,或把现有 `core/cqrs.md` 拆分成模块族入口页
|
||||
3. 继续为每个模块族补“README / landing / tutorials / API reference / XML”对照表,持续清零 `P0` / `P1`
|
||||
|
||||
@ -62,7 +62,40 @@
|
||||
- 构建校验:
|
||||
- `cd docs && bun run build`:通过;仅保留 VitePress 大 chunk warning,无构建失败
|
||||
|
||||
### 当前恢复点:RP-003
|
||||
|
||||
- 完成 `Ecs.Arch` 波次的运行时文档刷新:
|
||||
- `docs/zh-CN/ecs/index.md`
|
||||
- `docs/zh-CN/ecs/arch.md`
|
||||
- `GFramework.Ecs.Arch/README.md`
|
||||
- 为 `Ecs.Arch.Abstractions` 补齐与运行时页同粒度的 XML inventory:
|
||||
- `GFramework.Ecs.Arch.Abstractions/README.md`
|
||||
- `docs/zh-CN/abstractions/ecs-arch-abstractions.md`
|
||||
- 明确记录一个关键采用事实:
|
||||
- `UseArch(...)` 必须早于 `Initialize()` 调用
|
||||
- 该结论以 `ArchExtensions` 的模块注册方式和 `ExplicitRegistrationTests` 为证据
|
||||
- 将 `Ecs.Arch` family 从“入口存在但失真”推进到“README / landing / abstractions / XML inventory 已对齐源码与测试”
|
||||
|
||||
### 当前决策(RP-003)
|
||||
|
||||
- `Ecs` 波次继续采用与 `Core` 相同的治理粒度:
|
||||
- 模块 README 承担仓库入口
|
||||
- `docs/zh-CN/ecs/index.md` 承担模块族 landing
|
||||
- `docs/zh-CN/ecs/arch.md` 承担运行时默认实现专题页
|
||||
- `docs/zh-CN/abstractions/ecs-arch-abstractions.md` 承担契约边界专题页
|
||||
- `EnableStatistics` 当前仅保留在公开配置面上;文档不再把它写成已验证的运行时行为
|
||||
- 下一恢复点切换到 `Cqrs` 波次,优先解决入口分散和 API / XML 阅读链路不统一的问题
|
||||
|
||||
### 当前验证(RP-003)
|
||||
|
||||
- 文档校验:
|
||||
- `validate-all.sh docs/zh-CN/ecs/index.md`:通过
|
||||
- `validate-all.sh docs/zh-CN/ecs/arch.md`:通过
|
||||
- `validate-all.sh docs/zh-CN/abstractions/ecs-arch-abstractions.md`:通过
|
||||
- 构建校验:
|
||||
- `cd docs && bun run build`:通过;仅保留 VitePress 大 chunk warning,无构建失败
|
||||
|
||||
### 下一步
|
||||
|
||||
1. 在 `Ecs` 波次重写运行时 docs,并把 `Ecs.Arch.Abstractions` 纳入完整模块闭环
|
||||
2. 为 `Ecs.Arch` / `Ecs.Arch.Abstractions` 建立与 `Core` 同粒度的 XML inventory 基线
|
||||
1. 在 `Cqrs` 波次核对模块 README、`docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/**` 的真实 owner
|
||||
2. 决定 `Cqrs` family 是补 dedicated landing 还是拆分现有入口页
|
||||
|
||||
@ -32,6 +32,14 @@ description: GFramework.Ecs.Arch.Abstractions 的契约边界、包关系和最
|
||||
| `IArchSystemAdapter<T>` | 让 ECS 系统适配到 `ISystem` 生命周期 |
|
||||
| `ArchOptions` | 承载 `WorldCapacity`、`EnableStatistics`、`Priority` 等配置 |
|
||||
|
||||
## 类型族级 XML Inventory
|
||||
|
||||
| 类型族 | 代表类型 | XML 状态 | 阅读重点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 模块契约 | `IArchEcsModule` | 已覆盖 | 统一更新入口、宿主循环边界 |
|
||||
| 系统契约 | `IArchSystemAdapter<T>` | 已覆盖 | 只依赖更新接口而不绑定默认 runtime |
|
||||
| 配置对象 | `ArchOptions` | 已覆盖 | 共享配置字段与跨程序集采用边界 |
|
||||
|
||||
## 最小接入路径
|
||||
|
||||
### 1. 共享模块只依赖更新契约
|
||||
|
||||
@ -1,46 +1,43 @@
|
||||
---
|
||||
title: Arch ECS 集成
|
||||
description: GFramework 的 Arch ECS 集成包使用指南,提供高性能的实体组件系统支持。
|
||||
description: GFramework.Ecs.Arch 的默认运行时装配路径、系统桥接方式与 XML 阅读入口。
|
||||
---
|
||||
|
||||
# Arch ECS 集成
|
||||
|
||||
## 概述
|
||||
`GFramework.Ecs.Arch` 是当前仓库里负责 Arch ECS 默认接入路径的运行时包。它把 Arch `World`、GFramework 的
|
||||
`IServiceModule` 生命周期,以及 `AbstractSystem` / `ISystem` 体系桥接到同一条初始化与更新链路中。
|
||||
|
||||
`GFramework.Ecs.Arch` 是 GFramework 的 Arch ECS 集成包,提供开箱即用的 ECS(Entity Component
|
||||
System)支持。基于 [Arch.Core](https://github.com/genaray/Arch) 实现,具有极致的性能和简洁的 API。
|
||||
## 什么时候依赖它
|
||||
|
||||
**主要特性**:
|
||||
当你需要下面任一能力时,应直接依赖 `GeWuYou.GFramework.Ecs.Arch`:
|
||||
|
||||
- 🎯 **显式集成** - 符合 .NET 生态习惯的显式注册方式
|
||||
- 🔌 **零依赖** - 不使用时,Core 包无 Arch 依赖
|
||||
- 🎯 **类型安全** - 完整的类型系统和编译时检查
|
||||
- ⚡ **高性能** - 基于 Arch ECS 的高性能实现
|
||||
- 🔧 **易扩展** - 简单的系统适配器模式
|
||||
- 📊 **优先级支持** - 系统按优先级顺序执行
|
||||
- 在架构实例上调用 `UseArch(...)`
|
||||
- 让 `World` 在服务模块注册阶段自动创建并注入容器
|
||||
- 让 ECS 系统继承 `ArchSystemAdapter<T>`
|
||||
- 使用仓库自带的 `Position`、`Velocity`、`MovementSystem` 最小示例
|
||||
|
||||
**性能特点**:
|
||||
如果你只想保留共享边界,而不依赖默认实现,请改看
|
||||
[`../abstractions/ecs-arch-abstractions.md`](../abstractions/ecs-arch-abstractions.md)。
|
||||
|
||||
- 10,000 个实体更新 < 100ms
|
||||
- 1,000 个实体创建 < 50ms
|
||||
- 基于 Archetype 的高效内存布局
|
||||
- 零 GC 分配的组件访问
|
||||
## 最小接入路径
|
||||
|
||||
## 安装
|
||||
### 1. 安装包
|
||||
|
||||
```bash
|
||||
dotnet add package GeWuYou.GFramework.Ecs.Arch
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
### 2. 在 `Initialize()` 之前调用 `UseArch(...)`
|
||||
|
||||
### 1. 注册 ECS 模块
|
||||
当前实现通过 `ArchitectureModuleRegistry.Register(...)` 提前登记 `ArchEcsModule`。这意味着调用时机应位于
|
||||
`Initialize()` 之前,而不是放进 `OnInitialize()` 里。
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Architecture;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.Ecs.Arch.Extensions;
|
||||
|
||||
public class GameArchitecture : Architecture
|
||||
public sealed class GameArchitecture : Architecture
|
||||
{
|
||||
public GameArchitecture() : base(new ArchitectureConfiguration())
|
||||
{
|
||||
@ -48,698 +45,100 @@ public class GameArchitecture : Architecture
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 显式注册 Arch ECS 模块
|
||||
this.UseArch();
|
||||
RegisterSystem<MovementSystem>();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化架构
|
||||
var architecture = new GameArchitecture();
|
||||
var architecture = new GameArchitecture()
|
||||
.UseArch(options =>
|
||||
{
|
||||
options.WorldCapacity = 2048;
|
||||
options.Priority = 50;
|
||||
});
|
||||
|
||||
architecture.Initialize();
|
||||
```
|
||||
|
||||
### 2. 带配置的注册
|
||||
### 3. 用 `ArchSystemAdapter<float>` 编写系统
|
||||
|
||||
```csharp
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 带配置的注册
|
||||
this.UseArch(options =>
|
||||
{
|
||||
options.WorldCapacity = 2000; // World 初始容量
|
||||
options.EnableStatistics = true; // 启用统计信息
|
||||
options.Priority = 50; // 模块优先级
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 定义组件
|
||||
|
||||
组件是纯数据结构,使用 `struct` 定义:
|
||||
|
||||
```csharp
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MyGame.Components;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Position(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Velocity(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Health(float current, float max)
|
||||
{
|
||||
public float Current { get; set; } = current;
|
||||
public float Max { get; set; } = max;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 创建系统
|
||||
|
||||
系统继承自 `ArchSystemAdapter<T>`:
|
||||
`ArchSystemAdapter<T>` 在 `OnInit()` 中从当前上下文解析 `World`,再把 Arch 的 `Initialize / BeforeUpdate /
|
||||
AfterUpdate / Dispose` 钩子桥接到可重写方法。
|
||||
|
||||
```csharp
|
||||
using Arch.Core;
|
||||
using GFramework.Ecs.Arch;
|
||||
using MyGame.Components;
|
||||
using GFramework.Ecs.Arch.Components;
|
||||
|
||||
namespace MyGame.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// 移动系统 - 更新实体位置
|
||||
/// </summary>
|
||||
public sealed class MovementSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
private QueryDescription _query;
|
||||
|
||||
protected override void OnArchInitialize()
|
||||
{
|
||||
// 创建查询:查找所有同时拥有 Position 和 Velocity 组件的实体
|
||||
_query = new QueryDescription()
|
||||
.WithAll<Position, Velocity>();
|
||||
}
|
||||
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 查询并更新所有符合条件的实体
|
||||
World.Query(in _query, (ref Position pos, ref Velocity vel) =>
|
||||
var frameDelta = deltaTime;
|
||||
|
||||
World.Query(in _query, (ref Position position, ref Velocity velocity) =>
|
||||
{
|
||||
pos.X += vel.X * deltaTime;
|
||||
pos.Y += vel.Y * deltaTime;
|
||||
position.X += velocity.X * frameDelta;
|
||||
position.Y += velocity.Y * frameDelta;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 注册系统
|
||||
|
||||
```csharp
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
this.UseArch();
|
||||
|
||||
// 注册 ECS 系统
|
||||
RegisterSystem<MovementSystem>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 创建实体
|
||||
### 4. 初始化后解析 `World` 与模块服务
|
||||
|
||||
```csharp
|
||||
using Arch.Core;
|
||||
using GFramework.Core.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
using MyGame.Components;
|
||||
|
||||
[ContextAware]
|
||||
public partial class GameController
|
||||
{
|
||||
public void Start()
|
||||
{
|
||||
// 获取 World
|
||||
var world = this.GetService<World>();
|
||||
|
||||
// 创建实体
|
||||
var player = world.Create(
|
||||
new Position(0, 0),
|
||||
new Velocity(0, 0),
|
||||
new Health(100, 100)
|
||||
);
|
||||
|
||||
var enemy = world.Create(
|
||||
new Position(10, 10),
|
||||
new Velocity(-1, 0),
|
||||
new Health(50, 50)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 更新系统
|
||||
|
||||
```csharp
|
||||
using GFramework.Ecs.Arch.Abstractions;
|
||||
|
||||
public class GameLoop
|
||||
{
|
||||
private IArchEcsModule _ecsModule;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
// 获取 ECS 模块
|
||||
_ecsModule = architecture.Context.GetService<IArchEcsModule>();
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
// 更新所有 ECS 系统
|
||||
_ecsModule.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
var world = architecture.Context.GetService<World>();
|
||||
var ecsModule = architecture.Context.GetService<IArchEcsModule>();
|
||||
```
|
||||
|
||||
## 配置选项
|
||||
|
||||
### ArchOptions
|
||||
### 5. 由宿主循环显式调用 `Update`
|
||||
|
||||
```csharp
|
||||
public sealed class ArchOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// World 初始容量(默认:1000)
|
||||
/// </summary>
|
||||
public int WorldCapacity { get; set; } = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用统计信息(默认:false)
|
||||
/// </summary>
|
||||
public bool EnableStatistics { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 模块优先级(默认:50)
|
||||
/// </summary>
|
||||
public int Priority { get; set; } = 50;
|
||||
}
|
||||
ecsModule.Update(deltaTime);
|
||||
```
|
||||
|
||||
### 配置示例
|
||||
这一步在 `GFramework.Ecs.Arch.Tests/Ecs/*.cs` 中也采用同样的驱动方式。
|
||||
|
||||
```csharp
|
||||
this.UseArch(options =>
|
||||
{
|
||||
// 设置 World 初始容量
|
||||
// 根据预期实体数量设置,避免频繁扩容
|
||||
options.WorldCapacity = 2000;
|
||||
## 运行时职责
|
||||
|
||||
// 启用统计信息(开发/调试时使用)
|
||||
options.EnableStatistics = true;
|
||||
| 类型 | 责任 | 证据文件 |
|
||||
| --- | --- | --- |
|
||||
| `ArchExtensions` | 把 `ArchEcsModule` 注册到 `ArchitectureModuleRegistry` | `GFramework.Ecs.Arch/Extensions/ArchExtensions.cs` |
|
||||
| `ArchEcsModule` | 创建并注册 `World`,按优先级收集 `ArchSystemAdapter<float>`,负责初始化、销毁和逐帧更新 | `GFramework.Ecs.Arch/ArchEcsModule.cs` |
|
||||
| `ArchSystemAdapter<T>` | 从 GFramework 系统生命周期桥接到 Arch `ISystem<T>` 生命周期 | `GFramework.Ecs.Arch/ArchSystemAdapter.cs` |
|
||||
| `ArchOptions` | 暴露 `WorldCapacity`、`EnableStatistics`、`Priority` 这组运行时配置对象 | `GFramework.Ecs.Arch/ArchOptions.cs` |
|
||||
|
||||
// 设置模块优先级
|
||||
// 数值越小,优先级越高
|
||||
options.Priority = 50;
|
||||
});
|
||||
```
|
||||
## 配置对象阅读提示
|
||||
|
||||
## 核心概念
|
||||
当前公开配置对象是 `GFramework.Ecs.Arch.ArchOptions`。从源码可直接确认:
|
||||
|
||||
### Entity(实体)
|
||||
- `WorldCapacity` 用于 `World.Create(...)` 的容量参数
|
||||
- `Priority` 影响 `ArchEcsModule` 作为服务模块的排序
|
||||
- `EnableStatistics` 目前保留在公开配置面上;采用时应以源码 XML 注释和实现行为为准,而不是依赖旧文档推断
|
||||
|
||||
实体是游戏世界中的基本对象,本质上是一个唯一标识符:
|
||||
## 类型族级 XML Inventory
|
||||
|
||||
```csharp
|
||||
// 创建空实体
|
||||
var entity = world.Create();
|
||||
| 类型族 | 代表类型 | XML 状态 | 阅读重点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 装配入口 | `ArchExtensions` | 已覆盖 | `UseArch(...)` 的时机、链式调用返回值 |
|
||||
| 服务模块 | `ArchEcsModule` | 已覆盖 | `World` 注册、系统收集、模块销毁顺序 |
|
||||
| 系统桥接层 | `ArchSystemAdapter<T>` | 已覆盖 | `OnArchInitialize` / `OnUpdate` / `OnArchDispose` |
|
||||
| 示例类型 | `Position`、`Velocity`、`MovementSystem` | 已覆盖 | 组件布局、查询写法、最小集成样例 |
|
||||
|
||||
// 创建带组件的实体
|
||||
var entity = world.Create(
|
||||
new Position(0, 0),
|
||||
new Velocity(1, 1)
|
||||
);
|
||||
## 相关入口
|
||||
|
||||
// 销毁实体
|
||||
world.Destroy(entity);
|
||||
```
|
||||
|
||||
### Component(组件)
|
||||
|
||||
组件是纯数据结构,用于存储实体的状态:
|
||||
|
||||
```csharp
|
||||
// 添加组件
|
||||
world.Add(entity, new Position(0, 0));
|
||||
|
||||
// 检查组件
|
||||
if (world.Has<Position>(entity))
|
||||
{
|
||||
// 获取组件引用(零 GC 分配)
|
||||
ref var pos = ref world.Get<Position>(entity);
|
||||
pos.X += 10;
|
||||
}
|
||||
|
||||
// 设置组件(替换现有值)
|
||||
world.Set(entity, new Position(100, 100));
|
||||
|
||||
// 移除组件
|
||||
world.Remove<Velocity>(entity);
|
||||
```
|
||||
|
||||
### System(系统)
|
||||
|
||||
系统包含游戏逻辑,处理具有特定组件组合的实体:
|
||||
|
||||
```csharp
|
||||
public sealed class DamageSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
private QueryDescription _query;
|
||||
|
||||
protected override void OnArchInitialize()
|
||||
{
|
||||
// 初始化查询
|
||||
_query = new QueryDescription()
|
||||
.WithAll<Health, Damage>();
|
||||
}
|
||||
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 处理伤害
|
||||
World.Query(in _query, (Entity entity, ref Health health, ref Damage damage) =>
|
||||
{
|
||||
health.Current -= damage.Value * deltaTime;
|
||||
|
||||
if (health.Current <= 0)
|
||||
{
|
||||
health.Current = 0;
|
||||
World.Remove<Damage>(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### World(世界)
|
||||
|
||||
World 是 ECS 的核心容器,管理所有实体和组件:
|
||||
|
||||
```csharp
|
||||
// World 由 ArchEcsModule 自动创建和注册
|
||||
var world = this.GetService<World>();
|
||||
|
||||
// 获取实体数量
|
||||
var entityCount = world.Size;
|
||||
|
||||
// 清空所有实体
|
||||
world.Clear();
|
||||
```
|
||||
|
||||
## 系统适配器
|
||||
|
||||
### ArchSystemAdapter<T>
|
||||
|
||||
`ArchSystemAdapter<T>` 桥接 Arch.System.ISystem<T> 到 GFramework 架构:
|
||||
|
||||
```csharp
|
||||
public sealed class MySystem : ArchSystemAdapter<float>
|
||||
{
|
||||
// Arch 系统初始化
|
||||
protected override void OnArchInitialize()
|
||||
{
|
||||
// 创建查询、初始化资源
|
||||
}
|
||||
|
||||
// 更新前调用
|
||||
protected override void OnBeforeUpdate(in float deltaTime)
|
||||
{
|
||||
// 预处理逻辑
|
||||
}
|
||||
|
||||
// 主更新逻辑
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 处理实体
|
||||
}
|
||||
|
||||
// 更新后调用
|
||||
protected override void OnAfterUpdate(in float deltaTime)
|
||||
{
|
||||
// 后处理逻辑
|
||||
}
|
||||
|
||||
// 资源清理
|
||||
protected override void OnArchDispose()
|
||||
{
|
||||
// 清理资源
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 访问 World
|
||||
|
||||
在系统中可以直接访问 `World` 属性:
|
||||
|
||||
```csharp
|
||||
public sealed class MySystem : ArchSystemAdapter<float>
|
||||
{
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 访问 World
|
||||
var entityCount = World.Size;
|
||||
|
||||
// 创建实体
|
||||
var entity = World.Create(new Position(0, 0));
|
||||
|
||||
// 查询实体
|
||||
var query = new QueryDescription().WithAll<Position>();
|
||||
World.Query(in query, (ref Position pos) =>
|
||||
{
|
||||
// 处理逻辑
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 访问框架服务
|
||||
|
||||
`ArchSystemAdapter<T>` 继承自 `AbstractSystem`,可以使用所有 GFramework 的扩展方法:
|
||||
|
||||
```csharp
|
||||
public sealed class ServiceAccessSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 获取 Model
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
|
||||
// 获取 Utility
|
||||
var timeUtility = this.GetUtility<TimeUtility>();
|
||||
|
||||
// 发送命令
|
||||
this.SendCommand(new SaveGameCommand());
|
||||
|
||||
// 发送查询
|
||||
var score = this.SendQuery(new GetScoreQuery());
|
||||
|
||||
// 发送事件
|
||||
this.SendEvent(new GameOverEvent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 查询实体
|
||||
|
||||
### 基本查询
|
||||
|
||||
```csharp
|
||||
// 查询:必须有 Position 和 Velocity
|
||||
var query = new QueryDescription()
|
||||
.WithAll<Position, Velocity>();
|
||||
|
||||
World.Query(in query, (ref Position pos, ref Velocity vel) =>
|
||||
{
|
||||
pos.X += vel.X * deltaTime;
|
||||
pos.Y += vel.Y * deltaTime;
|
||||
});
|
||||
```
|
||||
|
||||
### 过滤查询
|
||||
|
||||
```csharp
|
||||
// 查询:必须有 Health,但不能有 Damage
|
||||
var query = new QueryDescription()
|
||||
.WithAll<Health>()
|
||||
.WithNone<Damage>();
|
||||
|
||||
World.Query(in query, (ref Health health) =>
|
||||
{
|
||||
// 只处理没有受伤的实体
|
||||
});
|
||||
```
|
||||
|
||||
### 可选组件查询
|
||||
|
||||
```csharp
|
||||
// 查询:必须有 Position,可选 Velocity
|
||||
var query = new QueryDescription()
|
||||
.WithAll<Position>()
|
||||
.WithAny<Velocity>();
|
||||
|
||||
World.Query(in query, (Entity entity, ref Position pos) =>
|
||||
{
|
||||
// 处理逻辑
|
||||
});
|
||||
```
|
||||
|
||||
### 访问实体 ID
|
||||
|
||||
```csharp
|
||||
var query = new QueryDescription().WithAll<Position>();
|
||||
|
||||
World.Query(in query, (Entity entity, ref Position pos) =>
|
||||
{
|
||||
// 可以访问实体 ID
|
||||
Console.WriteLine($"Entity {entity.Id}: ({pos.X}, {pos.Y})");
|
||||
|
||||
// 可以对实体进行操作
|
||||
if (pos.X > 100)
|
||||
{
|
||||
World.Destroy(entity);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 系统优先级
|
||||
|
||||
系统按照优先级顺序执行,数值越小优先级越高:
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Abstractions.bases;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Bases;
|
||||
|
||||
// 使用 Priority 特性设置优先级
|
||||
[Priority(10)] // 高优先级,先执行
|
||||
public sealed class InputSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
[Priority(20)] // 中优先级
|
||||
public sealed class MovementSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
[Priority(30)] // 低优先级,后执行
|
||||
public sealed class RenderSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
执行顺序:InputSystem → MovementSystem → RenderSystem
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 使用 struct 组件
|
||||
|
||||
```csharp
|
||||
// ✅ 推荐:使用 struct
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Position(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
// ❌ 不推荐:使用 class
|
||||
public class Position
|
||||
{
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 缓存查询
|
||||
|
||||
```csharp
|
||||
public class OptimizedSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
// ✅ 推荐:缓存查询
|
||||
private QueryDescription _cachedQuery;
|
||||
|
||||
protected override void OnArchInitialize()
|
||||
{
|
||||
_cachedQuery = new QueryDescription()
|
||||
.WithAll<Position, Velocity>();
|
||||
}
|
||||
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
World.Query(in _cachedQuery, (ref Position pos, ref Velocity vel) =>
|
||||
{
|
||||
pos.X += vel.X * deltaTime;
|
||||
pos.Y += vel.Y * deltaTime;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 使用 ref 访问组件
|
||||
|
||||
```csharp
|
||||
// ✅ 推荐:使用 ref 避免复制
|
||||
World.Query(in query, (ref Position pos, ref Velocity vel) =>
|
||||
{
|
||||
pos.X += vel.X; // 直接修改,零 GC
|
||||
});
|
||||
|
||||
// ❌ 不推荐:不使用 ref
|
||||
World.Query(in query, (Position pos, Velocity vel) =>
|
||||
{
|
||||
pos.X += vel.X; // 复制值,修改不会生效
|
||||
});
|
||||
```
|
||||
|
||||
### 4. 组件大小优化
|
||||
|
||||
```csharp
|
||||
// ✅ 推荐:小而专注的组件
|
||||
public struct Position(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
public struct Velocity(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
// ❌ 不推荐:大而全的组件
|
||||
public struct Transform
|
||||
{
|
||||
public float X, Y, Z;
|
||||
public float RotationX, RotationY, RotationZ;
|
||||
public float ScaleX, ScaleY, ScaleZ;
|
||||
public float VelocityX, VelocityY, VelocityZ;
|
||||
// ... 太多数据
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 组件设计原则
|
||||
|
||||
- 使用 `struct` 而不是 `class`
|
||||
- 只包含数据,不包含逻辑
|
||||
- 使用 `[StructLayout(LayoutKind.Sequential)]` 优化内存布局
|
||||
- 保持组件小而专注
|
||||
|
||||
### 2. 系统设计原则
|
||||
|
||||
- 单一职责:每个系统只负责一件事
|
||||
- 缓存查询:在 `OnArchInitialize` 中创建查询
|
||||
- 使用 ref:访问组件时使用 ref 参数
|
||||
- 批量处理:一次查询处理所有实体
|
||||
|
||||
### 3. 标签组件
|
||||
|
||||
使用空结构体作为标签来分类实体:
|
||||
|
||||
```csharp
|
||||
// 定义标签组件
|
||||
public struct PlayerTag { }
|
||||
public struct EnemyTag { }
|
||||
public struct DeadTag { }
|
||||
|
||||
// 使用标签过滤实体
|
||||
var query = new QueryDescription()
|
||||
.WithAll<Position, Velocity, PlayerTag>()
|
||||
.WithNone<DeadTag>();
|
||||
```
|
||||
|
||||
### 4. 与传统架构结合
|
||||
|
||||
```csharp
|
||||
// ECS 系统可以访问 Model
|
||||
public class EnemySpawnSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
var gameState = this.GetModel<GameStateModel>();
|
||||
|
||||
// 根据关卡生成敌人
|
||||
for (int i = 0; i < gameState.Level; i++)
|
||||
{
|
||||
World.Create(
|
||||
new Position(Random.Shared.Next(0, 100), 0),
|
||||
new Velocity(0, -1),
|
||||
new Health(50, 50)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何在运行时动态添加/移除组件?
|
||||
|
||||
A: Arch 支持运行时修改实体的组件:
|
||||
|
||||
```csharp
|
||||
// 动态添加组件
|
||||
if (pos.X > 100 && !World.Has<FastTag>(entity))
|
||||
{
|
||||
World.Add(entity, new FastTag());
|
||||
}
|
||||
|
||||
// 动态移除组件
|
||||
if (pos.X < 0 && World.Has<FastTag>(entity))
|
||||
{
|
||||
World.Remove<FastTag>(entity);
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何处理实体之间的交互?
|
||||
|
||||
A: 使用嵌套查询或事件:
|
||||
|
||||
```csharp
|
||||
// 方式 1:嵌套查询
|
||||
World.Query(in playerQuery, (Entity player, ref Position playerPos) =>
|
||||
{
|
||||
World.Query(in enemyQuery, (Entity enemy, ref Position enemyPos) =>
|
||||
{
|
||||
// 检测碰撞
|
||||
});
|
||||
});
|
||||
|
||||
// 方式 2:使用事件
|
||||
this.SendEvent(new CollisionEvent
|
||||
{
|
||||
Entity1 = player,
|
||||
Entity2 = enemy
|
||||
});
|
||||
```
|
||||
|
||||
### Q: 如何调试 ECS 系统?
|
||||
|
||||
A: 使用日志和统计信息:
|
||||
|
||||
```csharp
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 打印实体数量
|
||||
Console.WriteLine($"Total entities: {World.Size}");
|
||||
|
||||
// 查询特定实体
|
||||
var query = new QueryDescription().WithAll<Position>();
|
||||
var count = 0;
|
||||
World.Query(in query, (Entity entity, ref Position pos) =>
|
||||
{
|
||||
count++;
|
||||
Console.WriteLine($"Entity {entity.Id}: ({pos.X}, {pos.Y})");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 相关资源
|
||||
|
||||
- [Arch.Core 官方文档](https://github.com/genaray/Arch)
|
||||
- [ECS 概述](./index.md)
|
||||
- ECS 模块总览:[`index.md`](./index.md)
|
||||
- 抽象契约页:[`../abstractions/ecs-arch-abstractions.md`](../abstractions/ecs-arch-abstractions.md)
|
||||
- 仓库模块 README:`GFramework.Ecs.Arch/README.md`
|
||||
- 统一 API / XML 导航:[`../api-reference/index.md`](../api-reference/index.md)
|
||||
|
||||
@ -1,95 +1,39 @@
|
||||
---
|
||||
title: ECS 系统集成
|
||||
description: GFramework 的 ECS(Entity Component System)集成方案,支持多种 ECS 框架。
|
||||
description: GFramework 当前 ECS 模块族的包边界、采用顺序与 XML 阅读入口。
|
||||
---
|
||||
|
||||
# ECS 系统集成
|
||||
|
||||
## 概述
|
||||
GFramework 当前仓库内已经交付并持续维护的 ECS 模块族是 `Ecs.Arch`。它分成运行时实现包
|
||||
`GFramework.Ecs.Arch` 和契约包 `GFramework.Ecs.Arch.Abstractions`,分别覆盖默认装配能力与共享边界约定。
|
||||
|
||||
GFramework 提供了灵活的 ECS(Entity Component System)集成方案,允许你根据项目需求选择合适的 ECS 框架。ECS
|
||||
是一种数据驱动的架构模式,特别适合处理大量相似实体的场景。
|
||||
## 当前模块族
|
||||
|
||||
## 什么是 ECS?
|
||||
| 包 | 适用场景 | 你会得到什么 | 继续阅读 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GFramework.Ecs.Arch` | 需要默认运行时、`UseArch(...)` 装配入口、`World` 注册和系统适配基类 | `ArchEcsModule`、`ArchSystemAdapter<T>`、`ArchExtensions.UseArch(...)`、示例组件与系统 | [`arch.md`](./arch.md) |
|
||||
| `GFramework.Ecs.Arch.Abstractions` | 只想让共享宿主循环、测试替身或扩展模块依赖最小契约,而不引入默认运行时 | `IArchEcsModule`、`IArchSystemAdapter<T>`、`ArchOptions` 契约对象 | [`../abstractions/ecs-arch-abstractions.md`](../abstractions/ecs-arch-abstractions.md) |
|
||||
|
||||
ECS(Entity Component System)是一种架构模式,将游戏对象分解为三个核心概念:
|
||||
## 最小采用路径
|
||||
|
||||
- **Entity(实体)**:游戏世界中的基本对象,本质上是一个唯一标识符
|
||||
- **Component(组件)**:纯数据结构,存储实体的状态
|
||||
- **System(系统)**:包含游戏逻辑,处理具有特定组件组合的实体
|
||||
### 1. 选择包边界
|
||||
|
||||
### ECS 的优势
|
||||
- 需要默认实现时安装 `GeWuYou.GFramework.Ecs.Arch`
|
||||
- 只需要契约时安装 `GeWuYou.GFramework.Ecs.Arch.Abstractions`
|
||||
|
||||
- **高性能**:数据局部性好,缓存友好
|
||||
- **可扩展**:通过组合组件轻松创建新实体类型
|
||||
- **并行处理**:系统之间相互独立,易于并行化
|
||||
- **数据驱动**:逻辑与数据分离,便于序列化和网络同步
|
||||
### 2. 在 `Initialize()` 前显式接入运行时
|
||||
|
||||
### 何时使用 ECS?
|
||||
|
||||
**适合使用 ECS 的场景**:
|
||||
|
||||
- 大量相似实体(敌人、子弹、粒子)
|
||||
- 需要高性能批量处理
|
||||
- 复杂的实体组合和变化
|
||||
- 需要并行处理的系统
|
||||
|
||||
**不适合使用 ECS 的场景**:
|
||||
|
||||
- 全局状态管理
|
||||
- 单例服务
|
||||
- UI 逻辑
|
||||
- 游戏流程控制
|
||||
|
||||
## 支持的 ECS 框架
|
||||
|
||||
GFramework 采用可选集成的设计,你可以根据需求选择合适的 ECS 框架:
|
||||
|
||||
### Arch ECS(推荐)
|
||||
|
||||
[Arch](https://github.com/genaray/Arch) 是一个高性能的 C# ECS 框架,具有以下特点:
|
||||
|
||||
- ✅ **极致性能**:基于 Archetype 的内存布局,零 GC 分配
|
||||
- ✅ **简单易用**:清晰的 API,易于上手
|
||||
- ✅ **功能完整**:支持查询、过滤、并行处理等高级特性
|
||||
- ✅ **活跃维护**:社区活跃,持续更新
|
||||
|
||||
**安装方式**:
|
||||
|
||||
```bash
|
||||
dotnet add package GeWuYou.GFramework.Ecs.Arch
|
||||
```
|
||||
|
||||
**文档链接**:[Arch ECS 集成指南](./arch.md)
|
||||
|
||||
### 其他 ECS 框架
|
||||
|
||||
GFramework 的设计允许集成其他 ECS 框架,未来可能支持:
|
||||
|
||||
- **DefaultEcs**:轻量级 ECS 框架
|
||||
- **Entitas**:成熟的 ECS 框架,Unity 生态常用
|
||||
- **自定义 ECS**:你可以基于 GFramework 的模块系统实现自己的 ECS 集成
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 选择 ECS 框架
|
||||
|
||||
根据项目需求选择合适的 ECS 框架。对于大多数项目,我们推荐使用 Arch ECS。
|
||||
|
||||
### 2. 安装集成包
|
||||
|
||||
```bash
|
||||
# 安装 Arch ECS 集成包
|
||||
dotnet add package GeWuYou.GFramework.Ecs.Arch
|
||||
```
|
||||
|
||||
### 3. 注册 ECS 模块
|
||||
`UseArch(...)` 通过 `ArchitectureModuleRegistry` 注册服务模块。按当前源码与集成测试,它应在架构实例调用
|
||||
`Initialize()` 之前完成。
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Architecture;
|
||||
using GFramework.Ecs.Arc;
|
||||
using Arch.Core;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.Ecs.Arch.Abstractions;
|
||||
using GFramework.Ecs.Arch.Extensions;
|
||||
|
||||
public class GameArchitecture : Architecture
|
||||
public sealed class GameArchitecture : Architecture
|
||||
{
|
||||
public GameArchitecture() : base(new ArchitectureConfiguration())
|
||||
{
|
||||
@ -97,41 +41,29 @@ public class GameArchitecture : Architecture
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 显式注册 Arch ECS 模块
|
||||
this.UseArch(options =>
|
||||
{
|
||||
options.WorldCapacity = 2000;
|
||||
options.EnableStatistics = true;
|
||||
});
|
||||
RegisterSystem<MovementSystem>();
|
||||
}
|
||||
}
|
||||
|
||||
var architecture = new GameArchitecture()
|
||||
.UseArch(options =>
|
||||
{
|
||||
options.WorldCapacity = 2048;
|
||||
options.Priority = 50;
|
||||
});
|
||||
|
||||
architecture.Initialize();
|
||||
|
||||
var world = architecture.Context.GetService<World>();
|
||||
var ecsModule = architecture.Context.GetService<IArchEcsModule>();
|
||||
```
|
||||
|
||||
### 4. 定义组件
|
||||
|
||||
```csharp
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Position(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Velocity(float x, float y)
|
||||
{
|
||||
public float X { get; set; } = x;
|
||||
public float Y { get; set; } = y;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 创建系统
|
||||
### 3. 让 ECS 系统继承 `ArchSystemAdapter<float>`
|
||||
|
||||
```csharp
|
||||
using Arch.Core;
|
||||
using GFramework.Ecs.Arch;
|
||||
using GFramework.Ecs.Arch.Components;
|
||||
|
||||
public sealed class MovementSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
@ -145,147 +77,60 @@ public sealed class MovementSystem : ArchSystemAdapter<float>
|
||||
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
World.Query(in _query, (ref Position pos, ref Velocity vel) =>
|
||||
var frameDelta = deltaTime;
|
||||
|
||||
World.Query(in _query, (ref Position position, ref Velocity velocity) =>
|
||||
{
|
||||
pos.X += vel.X * deltaTime;
|
||||
pos.Y += vel.Y * deltaTime;
|
||||
position.X += velocity.X * frameDelta;
|
||||
position.Y += velocity.Y * frameDelta;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 注册系统
|
||||
### 4. 由宿主循环驱动更新
|
||||
|
||||
`IArchEcsModule` 继承自 `IServiceModule`,负责初始化和销毁;真正的帧更新通过 `Update(float deltaTime)` 显式触发。
|
||||
|
||||
```csharp
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
this.UseArch();
|
||||
using GFramework.Ecs.Arch.Abstractions;
|
||||
|
||||
// 注册 ECS 系统
|
||||
RegisterSystem<MovementSystem>();
|
||||
public sealed class GameLoop
|
||||
{
|
||||
private readonly IArchEcsModule _ecsModule;
|
||||
|
||||
public GameLoop(IArchEcsModule ecsModule)
|
||||
{
|
||||
_ecsModule = ecsModule;
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime)
|
||||
{
|
||||
_ecsModule.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 设计理念
|
||||
## 阅读顺序
|
||||
|
||||
### 显式集成
|
||||
1. 先看本页,确认自己要的是运行时包还是契约包
|
||||
2. 需要默认实现时继续读 [`arch.md`](./arch.md)
|
||||
3. 只想保留共享边界时继续读 [`../abstractions/ecs-arch-abstractions.md`](../abstractions/ecs-arch-abstractions.md)
|
||||
4. 统一查阅 README / docs / XML 入口时回到 [`../api-reference/index.md`](../api-reference/index.md)
|
||||
|
||||
GFramework 采用显式集成的设计,而不是自动注册:
|
||||
## 类型族级 XML Inventory
|
||||
|
||||
```csharp
|
||||
// ✅ 显式注册 - 清晰、可控
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
this.UseArch(); // 明确表示使用 Arch ECS
|
||||
}
|
||||
}
|
||||
下表记录当前 `Ecs.Arch` family 的类型声明级 XML 基线,便于从 README、站内 landing 和源码之间建立一致的审计入口。
|
||||
|
||||
// ❌ 自动注册 - 隐式、难以控制
|
||||
// 只需引入包,自动注册(不推荐)
|
||||
```
|
||||
| 包 | 类型族 | 代表类型 | XML 状态 | 阅读重点 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `GFramework.Ecs.Arch` | 运行时装配与模块生命周期 | `ArchExtensions`、`ArchEcsModule` | 已覆盖 | `UseArch(...)` 的接入时机、`World` 注册、模块优先级 |
|
||||
| `GFramework.Ecs.Arch` | 系统桥接层 | `ArchSystemAdapter<T>` | 已覆盖 | GFramework `ISystem` 生命周期如何桥接到 Arch `ISystem<T>` |
|
||||
| `GFramework.Ecs.Arch` | 示例组件与系统 | `Position`、`Velocity`、`MovementSystem` | 已覆盖 | 查询写法、组件布局、最小可运行示例 |
|
||||
| `GFramework.Ecs.Arch.Abstractions` | 契约与配置对象 | `IArchEcsModule`、`IArchSystemAdapter<T>`、`ArchOptions` | 已覆盖 | 共享宿主循环、测试替身、跨程序集配置边界 |
|
||||
|
||||
**优势**:
|
||||
## 边界说明
|
||||
|
||||
- 清晰的依赖关系
|
||||
- 更好的 IDE 支持
|
||||
- 易于测试和调试
|
||||
- 符合 .NET 生态习惯
|
||||
|
||||
### 零依赖原则
|
||||
|
||||
如果你不使用 ECS,GFramework.Core 包不会引入任何 ECS 相关的依赖:
|
||||
|
||||
```xml
|
||||
<!-- GFramework.Core.csproj -->
|
||||
<ItemGroup>
|
||||
<!-- 无 Arch 依赖 -->
|
||||
</ItemGroup>
|
||||
|
||||
<!-- GFramework.Ecs.Arch.csproj -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Arch" Version="2.1.0" />
|
||||
<PackageReference Include="Arch.System" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### 模块化设计
|
||||
|
||||
ECS 集成基于 GFramework 的模块系统:
|
||||
|
||||
```csharp
|
||||
// ECS 模块实现 IServiceModule 接口
|
||||
public sealed class ArchEcsModule : IArchEcsModule
|
||||
{
|
||||
public string ModuleName => nameof(ArchEcsModule);
|
||||
public int Priority => 50;
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
public void Register(IIocContainer container) { }
|
||||
public void Initialize() { }
|
||||
public ValueTask DestroyAsync() { }
|
||||
public void Update(float deltaTime) { }
|
||||
}
|
||||
```
|
||||
|
||||
## 与传统架构结合
|
||||
|
||||
ECS 可以与 GFramework 的传统架构(Model、System、Utility)无缝结合:
|
||||
|
||||
```csharp
|
||||
// Model 存储全局状态
|
||||
public class GameStateModel : AbstractModel
|
||||
{
|
||||
public int Score { get; set; }
|
||||
public int Level { get; set; }
|
||||
}
|
||||
|
||||
// ECS System 处理实体逻辑
|
||||
public class EnemySpawnSystem : ArchSystemAdapter<float>
|
||||
{
|
||||
protected override void OnUpdate(in float deltaTime)
|
||||
{
|
||||
// 访问 Model
|
||||
var gameState = this.GetModel<GameStateModel>();
|
||||
|
||||
// 根据关卡生成敌人
|
||||
for (int i = 0; i < gameState.Level; i++)
|
||||
{
|
||||
World.Create(
|
||||
new Position(Random.Shared.Next(0, 100), 0),
|
||||
new Velocity(0, -1),
|
||||
new Health(50, 50)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 传统 System 处理游戏逻辑
|
||||
public class ScoreSystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
this.RegisterEvent<EnemyDestroyedEvent>(OnEnemyDestroyed);
|
||||
}
|
||||
|
||||
private void OnEnemyDestroyed(EnemyDestroyedEvent e)
|
||||
{
|
||||
var gameState = this.GetModel<GameStateModel>();
|
||||
gameState.Score += 100;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 下一步
|
||||
|
||||
- [Arch ECS 集成指南](./arch.md) - 详细的 Arch ECS 使用文档
|
||||
|
||||
## 相关资源
|
||||
|
||||
- [Architecture 架构系统](../core/architecture.md)
|
||||
- [System 系统](../core/system.md)
|
||||
- [事件系统](../core/events.md)
|
||||
- 当前仓库没有交付其他可直接消费的 ECS 运行时包;旧文档里把“可能支持的其他 ECS 框架”写成现有能力会误导采用路径。
|
||||
- `GFramework.Ecs.Arch.Abstractions` 负责“边界”,`GFramework.Ecs.Arch` 负责“默认实现”。
|
||||
- 站内页面只维护可构建的 docs 链路;仓库根 README 和模块 README 继续承担包目录入口职责。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user