From ef7ab80c65d84db0f7782d011496a09ab1dd76fb Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Sun, 1 Mar 2026 22:08:26 +0800
Subject: [PATCH 1/7] =?UTF-8?q?refactor(core):=20=E9=87=8D=E5=91=BD?=
=?UTF-8?q?=E5=90=8DEcsModule=E4=B8=BAEcsServiceModule=E4=BB=A5=E6=8F=90?=
=?UTF-8?q?=E9=AB=98=E5=91=BD=E5=90=8D=E4=B8=80=E8=87=B4=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将EcsModule类重命名为EcsServiceModule
- 更新构造函数名称以匹配新的类名
- 修改ModuleName属性中的类型引用
- 在服务模块管理器中更新模块实例化调用
---
GFramework.Core/services/ServiceModuleManager.cs | 2 +-
.../services/modules/{EcsModule.cs => EcsServiceModule.cs} | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
rename GFramework.Core/services/modules/{EcsModule.cs => EcsServiceModule.cs} (95%)
diff --git a/GFramework.Core/services/ServiceModuleManager.cs b/GFramework.Core/services/ServiceModuleManager.cs
index 0d624f5c..86205338 100644
--- a/GFramework.Core/services/ServiceModuleManager.cs
+++ b/GFramework.Core/services/ServiceModuleManager.cs
@@ -63,7 +63,7 @@ public void RegisterBuiltInModules(IIocContainer container, ArchitectureProperti
if (properties.EnableEcs)
{
- RegisterModule(new EcsModule(enabled: true));
+ RegisterModule(new EcsServiceModule(enabled: true));
_logger.Info("ECS module enabled via configuration");
}
diff --git a/GFramework.Core/services/modules/EcsModule.cs b/GFramework.Core/services/modules/EcsServiceModule.cs
similarity index 95%
rename from GFramework.Core/services/modules/EcsModule.cs
rename to GFramework.Core/services/modules/EcsServiceModule.cs
index bedf0405..41c2fdb8 100644
--- a/GFramework.Core/services/modules/EcsModule.cs
+++ b/GFramework.Core/services/modules/EcsServiceModule.cs
@@ -10,7 +10,7 @@ namespace GFramework.Core.services.modules;
/// ECS(Entity Component System)模块,用于注册、初始化和管理ECS相关服务。
/// 该模块负责创建ECS世界和系统运行器,并将其注册到依赖注入容器中。
///
-public sealed class EcsModule : IServiceModule
+public sealed class EcsServiceModule : IServiceModule
{
private EcsSystemRunner? _ecsRunner;
@@ -20,7 +20,7 @@ public sealed class EcsModule : IServiceModule
/// 构造函数,初始化ECS模块。
///
/// 指定模块是否启用,默认为 true。
- public EcsModule(bool enabled = true)
+ public EcsServiceModule(bool enabled = true)
{
IsEnabled = enabled;
}
@@ -28,7 +28,7 @@ public EcsModule(bool enabled = true)
///
/// 获取模块名称。
///
- public string ModuleName => nameof(EcsModule);
+ public string ModuleName => nameof(EcsServiceModule);
///
/// 获取模块优先级,数值越小优先级越高。
From 3dfad6d45e03a9ae2a9401fdb0e255f7c0267bdf Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Sun, 1 Mar 2026 23:26:29 +0800
Subject: [PATCH 2/7] =?UTF-8?q?refactor(ecs):=20=E5=B0=86ECS=E7=B3=BB?=
=?UTF-8?q?=E7=BB=9F=E9=87=8D=E6=9E=84=E4=B8=BAArch=E9=80=82=E9=85=8D?=
=?UTF-8?q?=E5=99=A8=E6=A8=A1=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除原有的ECS基础实现和接口定义
- 添加ArchEcsModule作为新的ECS模块实现
- 创建ArchSystemAdapter基类用于桥接Arch系统
- 修改MovementSystem继承ArchSystemAdapter适配新架构
- 更新ServiceModuleManager使用新的ArchECS模块
- 移除ArchitectureContext中的ECS相关方法
- 从项目中移除对Arch包的直接依赖引用
---
.../GFramework.Core.Abstractions.csproj | 1 -
.../architecture/IArchitectureContext.cs | 15 --
.../ecs/IEcsSystem.cs | 20 ---
GFramework.Core.Abstractions/ecs/IEcsWorld.cs | 44 ------
.../architecture/ArchitectureContext.cs | 26 ----
GFramework.Core/ecs/ArchEcsModule.cs | 114 +++++++++++++++
GFramework.Core/ecs/ArchSystemAdapter.cs | 136 ++++++++++++++++++
GFramework.Core/ecs/EcsSystemBase.cs | 63 --------
GFramework.Core/ecs/EcsSystemRunner.cs | 67 ---------
GFramework.Core/ecs/EcsWorld.cs | 65 ---------
GFramework.Core/ecs/systems/MovementSystem.cs | 26 ++--
.../services/ServiceModuleManager.cs | 3 +-
.../services/modules/EcsServiceModule.cs | 110 --------------
13 files changed, 263 insertions(+), 427 deletions(-)
delete mode 100644 GFramework.Core.Abstractions/ecs/IEcsSystem.cs
delete mode 100644 GFramework.Core.Abstractions/ecs/IEcsWorld.cs
create mode 100644 GFramework.Core/ecs/ArchEcsModule.cs
create mode 100644 GFramework.Core/ecs/ArchSystemAdapter.cs
delete mode 100644 GFramework.Core/ecs/EcsSystemBase.cs
delete mode 100644 GFramework.Core/ecs/EcsSystemRunner.cs
delete mode 100644 GFramework.Core/ecs/EcsWorld.cs
delete mode 100644 GFramework.Core/services/modules/EcsServiceModule.cs
diff --git a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
index 6a6884d6..cda09d49 100644
--- a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
+++ b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
@@ -25,7 +25,6 @@
all
runtime; build; native; contentfiles; analyzers
-
diff --git a/GFramework.Core.Abstractions/architecture/IArchitectureContext.cs b/GFramework.Core.Abstractions/architecture/IArchitectureContext.cs
index 38f5a435..496d2285 100644
--- a/GFramework.Core.Abstractions/architecture/IArchitectureContext.cs
+++ b/GFramework.Core.Abstractions/architecture/IArchitectureContext.cs
@@ -1,5 +1,4 @@
using GFramework.Core.Abstractions.command;
-using GFramework.Core.Abstractions.ecs;
using GFramework.Core.Abstractions.environment;
using GFramework.Core.Abstractions.events;
using GFramework.Core.Abstractions.model;
@@ -206,18 +205,4 @@ ValueTask SendAsync(
///
/// 环境对象实例
IEnvironment GetEnvironment();
-
- // === ECS 支持 ===
-
- ///
- /// 获取ECS世界实例
- ///
- /// ECS世界实例
- IEcsWorld GetEcsWorld();
-
- ///
- /// 注册ECS系统
- ///
- /// ECS系统类型
- void RegisterEcsSystem() where T : class, IEcsSystem;
}
\ No newline at end of file
diff --git a/GFramework.Core.Abstractions/ecs/IEcsSystem.cs b/GFramework.Core.Abstractions/ecs/IEcsSystem.cs
deleted file mode 100644
index ca94bff6..00000000
--- a/GFramework.Core.Abstractions/ecs/IEcsSystem.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using GFramework.Core.Abstractions.system;
-
-namespace GFramework.Core.Abstractions.ecs;
-
-///
-/// ECS系统接口,继承自ISystem以集成到现有架构
-///
-public interface IEcsSystem : ISystem
-{
- ///
- /// 系统优先级,数值越小越先执行
- ///
- int Priority { get; }
-
- ///
- /// 每帧更新
- ///
- /// 帧间隔时间(秒)
- void Update(float deltaTime);
-}
\ No newline at end of file
diff --git a/GFramework.Core.Abstractions/ecs/IEcsWorld.cs b/GFramework.Core.Abstractions/ecs/IEcsWorld.cs
deleted file mode 100644
index e271ed64..00000000
--- a/GFramework.Core.Abstractions/ecs/IEcsWorld.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Arch.Core;
-
-namespace GFramework.Core.Abstractions.ecs;
-
-///
-/// ECS世界接口,封装Arch的World实例
-///
-public interface IEcsWorld : IDisposable
-{
- ///
- /// 当前实体数量
- ///
- int EntityCount { get; }
-
- ///
- /// 获取内部的Arch World实例(用于高级操作)
- ///
- World InternalWorld { get; }
-
- ///
- /// 创建一个新实体
- ///
- /// 组件类型数组
- /// 创建的实体
- Entity CreateEntity(params ComponentType[] types);
-
- ///
- /// 销毁指定实体
- ///
- /// 要销毁的实体
- void DestroyEntity(Entity entity);
-
- ///
- /// 检查实体是否存活
- ///
- /// 要检查的实体
- /// 实体是否存活
- bool IsAlive(Entity entity);
-
- ///
- /// 清空所有实体
- ///
- void Clear();
-}
\ No newline at end of file
diff --git a/GFramework.Core/architecture/ArchitectureContext.cs b/GFramework.Core/architecture/ArchitectureContext.cs
index 8c813683..00c36b1f 100644
--- a/GFramework.Core/architecture/ArchitectureContext.cs
+++ b/GFramework.Core/architecture/ArchitectureContext.cs
@@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.architecture;
using GFramework.Core.Abstractions.command;
-using GFramework.Core.Abstractions.ecs;
using GFramework.Core.Abstractions.environment;
using GFramework.Core.Abstractions.events;
using GFramework.Core.Abstractions.ioc;
@@ -413,29 +412,4 @@ public IEnvironment GetEnvironment()
}
#endregion
-
- #region ECS Support
-
- ///
- /// 获取ECS世界实例
- ///
- /// ECS世界实例
- public IEcsWorld GetEcsWorld()
- {
- var ecsWorld = GetOrCache();
- return ecsWorld ??
- throw new InvalidOperationException("ECS World not initialized. Enable ECS in configuration.");
- }
-
- ///
- /// 注册ECS系统
- ///
- /// ECS系统类型
- public void RegisterEcsSystem() where T : class, IEcsSystem
- {
- // 使用RegisterPlurality注册到所有接口
- _container.RegisterPlurality();
- }
-
- #endregion
}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/ArchEcsModule.cs b/GFramework.Core/ecs/ArchEcsModule.cs
new file mode 100644
index 00000000..549b2347
--- /dev/null
+++ b/GFramework.Core/ecs/ArchEcsModule.cs
@@ -0,0 +1,114 @@
+using Arch.Core;
+using GFramework.Core.Abstractions.architecture;
+using GFramework.Core.Abstractions.ioc;
+
+namespace GFramework.Core.ecs;
+
+///
+/// Arch ECS 模块 - 核心适配器,桥接 Arch 到框架生命周期
+///
+public sealed class ArchEcsModule : IServiceModule
+{
+ private readonly List> _systems = [];
+ private IIocContainer? _container;
+ private World? _world;
+
+ ///
+ /// 构造函数
+ ///
+ /// 是否启用模块
+ public ArchEcsModule(bool enabled = true)
+ {
+ IsEnabled = enabled;
+ }
+
+ ///
+ /// 模块名称
+ ///
+ public string ModuleName => nameof(ArchEcsModule);
+
+ ///
+ /// 模块优先级
+ ///
+ public int Priority => 50;
+
+ ///
+ /// 是否启用
+ ///
+ public bool IsEnabled { get; }
+
+ ///
+ /// 注册服务 - 创建并注册 World
+ ///
+ public void Register(IIocContainer container)
+ {
+ if (!IsEnabled) return;
+
+ _container = container;
+
+ // 创建并注册 World
+ _world = World.Create();
+ container.Register(_world);
+ }
+
+ ///
+ /// 初始化 - 从容器获取所有适配器并初始化
+ ///
+ public void Initialize()
+ {
+ if (!IsEnabled || _world == null || _container == null) return;
+
+ // 从容器获取所有适配器
+ var adapters = _container.GetAll>();
+ if (adapters.Count > 0)
+ {
+ _systems.AddRange(adapters);
+
+ // 初始化所有系统(会调用 Arch 系统的 Initialize)
+ foreach (var system in _systems)
+ {
+ system.Initialize();
+ }
+ }
+ }
+
+ ///
+ /// 异步销毁
+ ///
+ public async ValueTask DestroyAsync()
+ {
+ if (!IsEnabled) return;
+
+ // 销毁所有系统
+ foreach (var system in _systems)
+ {
+ system.Destroy();
+ }
+
+ _systems.Clear();
+
+ // 销毁 World
+ if (_world != null)
+ {
+ World.Destroy(_world);
+ _world = null;
+ }
+
+ await ValueTask.CompletedTask;
+ }
+
+ ///
+ /// 更新所有 ECS 系统
+ ///
+ /// 帧间隔时间
+ public void Update(float deltaTime)
+ {
+ if (!IsEnabled) return;
+
+ // 调用所有系统的更新
+ foreach (var system in _systems)
+ {
+ system.Update(deltaTime);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/ArchSystemAdapter.cs b/GFramework.Core/ecs/ArchSystemAdapter.cs
new file mode 100644
index 00000000..47b9346e
--- /dev/null
+++ b/GFramework.Core/ecs/ArchSystemAdapter.cs
@@ -0,0 +1,136 @@
+using Arch.Core;
+using GFramework.Core.extensions;
+using GFramework.Core.system;
+using ArchSys = Arch.System;
+
+namespace GFramework.Core.ecs;
+
+///
+/// Arch 系统适配器 - 桥接 Arch.System.ISystem<T> 到框架上下文
+///
+/// 系统数据类型(通常是 float 表示 deltaTime)
+public abstract class ArchSystemAdapter : AbstractSystem, ArchSys.ISystem
+{
+ ///
+ /// 获取或设置 Arch ECS 世界的实例
+ ///
+ public World World { get; private set; } = null!;
+
+ // ===== Arch 显式接口实现 =====
+
+ ///
+ /// 显式实现 Arch.System.ISystem<T> 的初始化方法
+ /// 调用受保护的虚方法 OnArchInitialize 以允许子类自定义初始化逻辑
+ ///
+ void ArchSys.ISystem.Initialize()
+ {
+ OnArchInitialize();
+ }
+
+ ///
+ /// 显式实现 Arch.System.ISystem<T> 的更新前回调方法
+ /// 调用受保护的虚方法 OnBeforeUpdate 以允许子类自定义预处理逻辑
+ ///
+ /// 系统数据参数(通常是 deltaTime)
+ void ArchSys.ISystem.BeforeUpdate(in T t)
+ {
+ OnBeforeUpdate(in t);
+ }
+
+ ///
+ /// 显式实现 Arch.System.ISystem<T> 的主更新方法
+ /// 调用受保护的抽象方法 OnUpdate 以强制子类实现核心更新逻辑
+ ///
+ /// 系统数据参数(通常是 deltaTime)
+ public void Update(in T t)
+ {
+ OnUpdate(in t);
+ }
+
+ ///
+ /// 显式实现 Arch.System.ISystem<T> 的更新后回调方法
+ /// 调用受保护的虚方法 OnAfterUpdate 以允许子类自定义后处理逻辑
+ ///
+ /// 系统数据参数(通常是 deltaTime)
+ void ArchSys.ISystem.AfterUpdate(in T t)
+ {
+ OnAfterUpdate(in t);
+ }
+
+ ///
+ /// 显式实现 IDisposable 的资源释放方法
+ /// 调用受保护的虚方法 OnArchDispose 以允许子类自定义资源清理逻辑
+ ///
+ void IDisposable.Dispose()
+ {
+ OnArchDispose();
+ }
+
+ // ===== GFramework 生命周期 =====
+
+ ///
+ /// 系统初始化方法
+ /// 在此方法中获取 Arch World 实例并调用 Arch 系统的初始化逻辑
+ ///
+ protected override void OnInit()
+ {
+ World = this.GetService()!;
+
+ // 调用 Arch 初始化
+ ((ArchSys.ISystem)this).Initialize();
+ }
+
+ ///
+ /// 系统销毁方法
+ /// 在此方法中调用 Arch 系统的资源释放逻辑
+ ///
+ protected override void OnDestroy()
+ {
+ ((ArchSys.ISystem)this).Dispose();
+ }
+
+ // ===== 子类可重写 Hook =====
+
+ ///
+ /// Arch 系统初始化的受保护虚方法
+ /// 子类可重写此方法以实现自定义的 Arch 系统初始化逻辑
+ ///
+ protected virtual void OnArchInitialize()
+ {
+ }
+
+ ///
+ /// 更新前处理的受保护虚方法
+ /// 子类可重写此方法以实现自定义的预处理逻辑
+ ///
+ /// 系统数据参数(通常是 deltaTime)
+ protected virtual void OnBeforeUpdate(in T t)
+ {
+ }
+
+ ///
+ /// 核心更新逻辑的受保护抽象方法
+ /// 子类必须重写此方法以实现具体的系统更新功能
+ ///
+ /// 系统数据参数(通常是 deltaTime)
+ protected virtual void OnUpdate(in T t)
+ {
+ }
+
+ ///
+ /// 更新后处理的受保护虚方法
+ /// 子类可重写此方法以实现自定义的后处理逻辑
+ ///
+ /// 系统数据参数(通常是 deltaTime)
+ protected virtual void OnAfterUpdate(in T t)
+ {
+ }
+
+ ///
+ /// Arch 系统资源释放的受保护虚方法
+ /// 子类可重写此方法以实现自定义的资源清理逻辑
+ ///
+ protected virtual void OnArchDispose()
+ {
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/EcsSystemBase.cs b/GFramework.Core/ecs/EcsSystemBase.cs
deleted file mode 100644
index 07784bb8..00000000
--- a/GFramework.Core/ecs/EcsSystemBase.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using Arch.Core;
-using GFramework.Core.Abstractions.ecs;
-using GFramework.Core.extensions;
-using GFramework.Core.system;
-
-namespace GFramework.Core.ecs;
-
-///
-/// ECS系统基类,继承自AbstractSystem以集成到现有架构
-///
-public abstract class EcsSystemBase : AbstractSystem, IEcsSystem
-{
- ///
- /// ECS世界实例
- ///
- protected IEcsWorld EcsWorld { get; private set; } = null!;
-
- ///
- /// 快捷访问内部World
- ///
- protected World World => EcsWorld.InternalWorld;
-
- ///
- /// 系统优先级,默认为0
- ///
- public virtual int Priority => 0;
-
- ///
- /// 每帧更新(子类实现)
- ///
- public abstract void Update(float deltaTime);
-
- ///
- /// 系统初始化
- ///
- protected override void OnInit()
- {
- EcsWorld = this.GetService() ?? throw new InvalidOperationException(
- "EcsWorld not found in context. Make sure ECS is properly initialized.");
-
- OnEcsInit();
- }
-
- ///
- /// 系统销毁
- ///
- protected override void OnDestroy()
- {
- OnEcsDestroy();
- }
-
- ///
- /// ECS系统初始化(子类实现)
- ///
- protected abstract void OnEcsInit();
-
- ///
- /// ECS系统销毁(子类可选实现)
- ///
- protected virtual void OnEcsDestroy()
- {
- }
-}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/EcsSystemRunner.cs b/GFramework.Core/ecs/EcsSystemRunner.cs
deleted file mode 100644
index 3820a099..00000000
--- a/GFramework.Core/ecs/EcsSystemRunner.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using GFramework.Core.Abstractions.ecs;
-using GFramework.Core.extensions;
-using GFramework.Core.system;
-
-namespace GFramework.Core.ecs;
-
-///
-/// ECS系统调度器,负责管理和更新所有ECS系统
-///
-public sealed class EcsSystemRunner : AbstractSystem
-{
- private readonly List _systems = new();
- private bool _isRunning;
-
- ///
- /// 初始化调度器,从DI容器获取所有ECS系统
- ///
- protected override void OnInit()
- {
- // 从容器获取所有已注册的ECS系统
- var systemsList = this.GetService>();
- if (systemsList is { Count: > 0 })
- {
- // 按优先级排序
- _systems.AddRange(systemsList.OrderBy(s => s.Priority));
- }
- }
-
- ///
- /// 更新所有ECS系统
- ///
- /// 帧间隔时间
- public void Update(float deltaTime)
- {
- if (!_isRunning) return;
-
- foreach (var system in _systems)
- {
- system.Update(deltaTime);
- }
- }
-
- ///
- /// 启动调度器
- ///
- public void Start()
- {
- _isRunning = true;
- }
-
- ///
- /// 停止调度器
- ///
- public void Stop()
- {
- _isRunning = false;
- }
-
- ///
- /// 销毁调度器
- ///
- protected override void OnDestroy()
- {
- Stop();
- _systems.Clear();
- }
-}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/EcsWorld.cs b/GFramework.Core/ecs/EcsWorld.cs
deleted file mode 100644
index 92651b35..00000000
--- a/GFramework.Core/ecs/EcsWorld.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Arch.Core;
-using GFramework.Core.Abstractions.ecs;
-
-namespace GFramework.Core.ecs;
-
-///
-/// ECS世界实现,封装Arch的World实例
-///
-public sealed class EcsWorld : IEcsWorld
-{
- private bool _disposed;
-
- ///
- /// 获取内部的Arch World实例
- ///
- public World InternalWorld { get; } = World.Create();
-
- ///
- /// 当前实体数量
- ///
- public int EntityCount => InternalWorld.Size;
-
- ///
- /// 创建一个新实体
- ///
- public Entity CreateEntity(params ComponentType[] types)
- {
- return InternalWorld.Create(types);
- }
-
- ///
- /// 销毁指定实体
- ///
- public void DestroyEntity(Entity entity)
- {
- InternalWorld.Destroy(entity);
- }
-
- ///
- /// 检查实体是否存活
- ///
- public bool IsAlive(Entity entity)
- {
- return InternalWorld.IsAlive(entity);
- }
-
- ///
- /// 清空所有实体
- ///
- public void Clear()
- {
- InternalWorld.Clear();
- }
-
- ///
- /// 释放资源
- ///
- public void Dispose()
- {
- if (_disposed) return;
-
- World.Destroy(InternalWorld);
- _disposed = true;
- }
-}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/systems/MovementSystem.cs b/GFramework.Core/ecs/systems/MovementSystem.cs
index c0fe4ccf..3c33b449 100644
--- a/GFramework.Core/ecs/systems/MovementSystem.cs
+++ b/GFramework.Core/ecs/systems/MovementSystem.cs
@@ -4,37 +4,33 @@
namespace GFramework.Core.ecs.systems;
///
-/// 移动系统,负责更新具有位置和速度组件的实体的位置。
-/// 根据速度和时间增量计算实体的新位置。
+/// 移动系统 - Arch 原生实现
+/// 负责更新具有位置和速度组件的实体的位置
///
-public class MovementSystem : EcsSystemBase
+public sealed class MovementSystem : ArchSystemAdapter
{
private QueryDescription _query;
///
- /// 获取系统的优先级,数值越小优先级越高。
+ /// 初始化系统
///
- public override int Priority => 0;
-
- ///
- /// ECS初始化回调方法,在系统初始化时调用。
- /// 创建查询描述符,用于查找同时拥有Position和Velocity组件的实体。
- ///
- protected override void OnEcsInit()
+ public void Initialize(World world)
{
// 创建查询:查找所有同时拥有Position和Velocity组件的实体
_query = new QueryDescription()
.WithAll();
}
+
///
- /// 系统更新方法,每帧调用一次。
+ /// 系统更新方法,每帧调用一次
///
- /// 帧间隔时间,用于计算位置变化量
- public override void Update(float deltaTime)
+ /// ECS 世界
+ /// 帧间隔时间
+ public void Update(World world, float deltaTime)
{
// 查询并更新所有符合条件的实体
- World.Query(in _query, (ref Position pos, ref Velocity vel) =>
+ world.Query(in _query, (ref Position pos, ref Velocity vel) =>
{
pos.X += vel.X * deltaTime;
pos.Y += vel.Y * deltaTime;
diff --git a/GFramework.Core/services/ServiceModuleManager.cs b/GFramework.Core/services/ServiceModuleManager.cs
index 86205338..0f160734 100644
--- a/GFramework.Core/services/ServiceModuleManager.cs
+++ b/GFramework.Core/services/ServiceModuleManager.cs
@@ -3,6 +3,7 @@
using GFramework.Core.Abstractions.lifecycle;
using GFramework.Core.Abstractions.logging;
using GFramework.Core.Abstractions.properties;
+using GFramework.Core.ecs;
using GFramework.Core.logging;
using GFramework.Core.services.modules;
@@ -63,7 +64,7 @@ public void RegisterBuiltInModules(IIocContainer container, ArchitectureProperti
if (properties.EnableEcs)
{
- RegisterModule(new EcsServiceModule(enabled: true));
+ RegisterModule(new ArchEcsModule(enabled: true));
_logger.Info("ECS module enabled via configuration");
}
diff --git a/GFramework.Core/services/modules/EcsServiceModule.cs b/GFramework.Core/services/modules/EcsServiceModule.cs
deleted file mode 100644
index 41c2fdb8..00000000
--- a/GFramework.Core/services/modules/EcsServiceModule.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using GFramework.Core.Abstractions.architecture;
-using GFramework.Core.Abstractions.ecs;
-using GFramework.Core.Abstractions.ioc;
-using GFramework.Core.Abstractions.lifecycle;
-using GFramework.Core.ecs;
-
-namespace GFramework.Core.services.modules;
-
-///
-/// ECS(Entity Component System)模块,用于注册、初始化和管理ECS相关服务。
-/// 该模块负责创建ECS世界和系统运行器,并将其注册到依赖注入容器中。
-///
-public sealed class EcsServiceModule : IServiceModule
-{
- private EcsSystemRunner? _ecsRunner;
-
- private EcsWorld? _ecsWorld;
-
- ///
- /// 构造函数,初始化ECS模块。
- ///
- /// 指定模块是否启用,默认为 true。
- public EcsServiceModule(bool enabled = true)
- {
- IsEnabled = enabled;
- }
-
- ///
- /// 获取模块名称。
- ///
- public string ModuleName => nameof(EcsServiceModule);
-
- ///
- /// 获取模块优先级,数值越小优先级越高。
- ///
- public int Priority => 100;
-
- ///
- /// 获取模块启用状态。
- ///
- public bool IsEnabled { get; }
-
- ///
- /// 注册ECS相关服务到依赖注入容器中。
- /// 包括ECS世界实例和系统运行器实例的注册。
- ///
- /// 依赖注入容器实例。
- public void Register(IIocContainer container)
- {
- if (!IsEnabled) return;
-
- _ecsWorld = new EcsWorld();
- container.Register(_ecsWorld);
- container.Register(_ecsWorld);
-
- container.RegisterPlurality();
- _ecsRunner = container.Get();
- }
-
- ///
- /// 初始化ECS模块。
- /// 如果系统运行器实现了IInitializable接口,则调用其初始化方法。
- ///
- public void Initialize()
- {
- if (!IsEnabled || _ecsRunner == null) return;
-
- if (_ecsRunner is IInitializable initializable)
- {
- initializable.Initialize();
- }
- }
-
- ///
- /// 异步销毁ECS模块并释放相关资源。
- /// 包括销毁系统运行器和释放ECS世界资源。
- ///
- /// 表示异步操作完成的任务。
- public async ValueTask DestroyAsync()
- {
- if (!IsEnabled) return;
-
- if (_ecsRunner is IDestroyable destroyable)
- {
- destroyable.Destroy();
- }
-
- _ecsRunner = null;
-
- if (_ecsWorld != null)
- {
- _ecsWorld.Dispose();
- _ecsWorld = null;
- }
-
- await ValueTask.CompletedTask;
- }
-
- ///
- /// 获取ECS世界实例。
- ///
- /// ECS世界实例,如果未启用则返回 null。
- public IEcsWorld? GetEcsWorld() => _ecsWorld;
-
- ///
- /// 获取ECS系统运行器实例(内部使用)。
- ///
- /// ECS系统运行器实例,如果未启用则返回 null。
- internal EcsSystemRunner? GetEcsRunner() => _ecsRunner;
-}
\ No newline at end of file
From 2775d5555aec0eb9682df6dea4e06e2933c69833 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Sun, 1 Mar 2026 23:26:54 +0800
Subject: [PATCH 3/7] =?UTF-8?q?refactor(ecs):=20=E4=BC=98=E5=8C=96?=
=?UTF-8?q?=E4=BD=8D=E7=BD=AE=E5=92=8C=E9=80=9F=E5=BA=A6=E7=BB=84=E4=BB=B6?=
=?UTF-8?q?=E7=9A=84=E5=86=85=E5=AD=98=E5=B8=83=E5=B1=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 为 Position 结构体添加 StructLayout 特性以确保顺序布局
- 为 Velocity 结构体添加 StructLayout 特性以确保顺序布局
- 更新 Velocity 组件的 XML 文档注释,提供更详细的描述
- 优化 Velocity 组件属性的文档注释,明确单位信息
- 添加 System.Runtime.InteropServices 命名空间引用
---
GFramework.Core/ecs/components/Position.cs | 3 +++
GFramework.Core/ecs/components/Velocity.cs | 12 +++++++-----
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/GFramework.Core/ecs/components/Position.cs b/GFramework.Core/ecs/components/Position.cs
index 472679f7..204f66d6 100644
--- a/GFramework.Core/ecs/components/Position.cs
+++ b/GFramework.Core/ecs/components/Position.cs
@@ -1,3 +1,5 @@
+using System.Runtime.InteropServices;
+
namespace GFramework.Core.ecs.components;
///
@@ -5,6 +7,7 @@ namespace GFramework.Core.ecs.components;
///
/// X轴坐标值
/// Y轴坐标值
+[StructLayout(LayoutKind.Sequential)]
public struct Position(float x, float y)
{
///
diff --git a/GFramework.Core/ecs/components/Velocity.cs b/GFramework.Core/ecs/components/Velocity.cs
index 51e8892a..26465b2b 100644
--- a/GFramework.Core/ecs/components/Velocity.cs
+++ b/GFramework.Core/ecs/components/Velocity.cs
@@ -1,19 +1,21 @@
+using System.Runtime.InteropServices;
+
namespace GFramework.Core.ecs.components;
///
-/// 速度组件,用于表示实体在二维空间中的运动速度。
+/// 速度结构体,用于表示二维空间中实体的瞬时速度向量
+/// 包含X轴和Y轴的速度分量,通常用于物理计算和运动系统
///
-/// X轴方向的速度分量
-/// Y轴方向的速度分量
+[StructLayout(LayoutKind.Sequential)]
public struct Velocity(float x, float y)
{
///
- /// X轴方向的速度分量
+ /// X轴速度分量,单位为距离单位/秒
///
public float X { get; set; } = x;
///
- /// Y轴方向的速度分量
+ /// Y轴速度分量,单位为距离单位/秒
///
public float Y { get; set; } = y;
}
\ No newline at end of file
From ee820ff034129137020c112c631d5e582c942a16 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Sun, 1 Mar 2026 23:36:02 +0800
Subject: [PATCH 4/7] =?UTF-8?q?refactor(ecs):=20=E5=B0=86=20ECS=20?=
=?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=87=8D=E6=9E=84=E4=B8=BA=E5=9F=BA=E4=BA=8E?=
=?UTF-8?q?=20Arch=20=E7=9A=84=E5=8E=9F=E7=94=9F=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 MovementSystem 从继承 EcsSystemBase 改为继承 ArchSystemAdapter
- 更新 MovementSystem 的初始化和更新方法以适配 Arch 架构
- 移除测试代码中的 ECS 相关接口实现和抽象层
- 将测试用例从 GFramework ECS API 迁移到 Arch 原生 API
- 更新 ECS 测试类以使用 Arch World 和实体操作方法
- 重构 ECS 模块初始化流程以支持 Arch 系统注册和管理
---
.../architecture/ArchitectureServicesTests.cs | 11 -
.../architecture/GameContextTests.cs | 11 -
GFramework.Core.Tests/ecs/EcsAdvancedTests.cs | 336 ++++++------------
GFramework.Core.Tests/ecs/EcsBasicTests.cs | 184 +++-------
.../ecs/EcsIntegrationTests.cs | 287 ++++-----------
GFramework.Core/ecs/systems/MovementSystem.cs | 17 +-
6 files changed, 236 insertions(+), 610 deletions(-)
diff --git a/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs b/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
index 1baf59f7..041bfa01 100644
--- a/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
+++ b/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
@@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.architecture;
using GFramework.Core.Abstractions.command;
-using GFramework.Core.Abstractions.ecs;
using GFramework.Core.Abstractions.environment;
using GFramework.Core.Abstractions.events;
using GFramework.Core.Abstractions.ioc;
@@ -428,16 +427,6 @@ public IEnvironment GetEnvironment()
{
return _environment;
}
-
- public IEcsWorld GetEcsWorld()
- {
- throw new NotImplementedException("ECS not implemented in test context");
- }
-
- public void RegisterEcsSystem() where T : class, IEcsSystem
- {
- throw new NotImplementedException("ECS not implemented in test context");
- }
}
#endregion
\ No newline at end of file
diff --git a/GFramework.Core.Tests/architecture/GameContextTests.cs b/GFramework.Core.Tests/architecture/GameContextTests.cs
index 88b3dc76..7967ec21 100644
--- a/GFramework.Core.Tests/architecture/GameContextTests.cs
+++ b/GFramework.Core.Tests/architecture/GameContextTests.cs
@@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.architecture;
using GFramework.Core.Abstractions.command;
-using GFramework.Core.Abstractions.ecs;
using GFramework.Core.Abstractions.environment;
using GFramework.Core.Abstractions.events;
using GFramework.Core.Abstractions.ioc;
@@ -452,14 +451,4 @@ public IEnvironment GetEnvironment()
{
return Environment;
}
-
- public IEcsWorld GetEcsWorld()
- {
- throw new NotImplementedException("ECS not implemented in test context");
- }
-
- public void RegisterEcsSystem() where T : class, IEcsSystem
- {
- throw new NotImplementedException("ECS not implemented in test context");
- }
}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs b/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
index 53582efa..298e0e68 100644
--- a/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
+++ b/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
@@ -1,303 +1,158 @@
-using System.Reflection;
+using System.Diagnostics.CodeAnalysis;
using Arch.Core;
-using GFramework.Core.Abstractions.ecs;
using GFramework.Core.Abstractions.rule;
using GFramework.Core.architecture;
using GFramework.Core.ecs;
using GFramework.Core.ecs.components;
using GFramework.Core.ecs.systems;
using GFramework.Core.ioc;
-using GFramework.Core.logging;
using NUnit.Framework;
namespace GFramework.Core.Tests.ecs;
+///
+/// ECS 高级功能测试类 - 使用 Arch 原生 API
+///
[TestFixture]
+[Experimental("GFrameworkECS")]
public class EcsAdvancedTests
{
[SetUp]
public void Setup()
{
- LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
-
_container = new MicrosoftDiContainer();
- var loggerField = typeof(MicrosoftDiContainer).GetField("_logger",
- BindingFlags.NonPublic | BindingFlags.Instance);
- loggerField?.SetValue(_container,
- LoggerFactoryResolver.Provider.CreateLogger(nameof(EcsAdvancedTests)));
-
_context = new ArchitectureContext(_container);
}
[TearDown]
public void TearDown()
{
- _ecsWorld?.Dispose();
- _ecsWorld = null;
+ if (_world != null)
+ {
+ World.Destroy(_world);
+ _world = null;
+ }
+
_container?.Clear();
_context = null;
+ _ecsModule = null;
}
private MicrosoftDiContainer? _container;
private ArchitectureContext? _context;
- private EcsWorld? _ecsWorld;
+ private World? _world;
+ private ArchEcsModule? _ecsModule;
- private void InitializeEcsWithSystems(params Type[] systemTypes)
+ private void InitializeEcsModule()
{
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld as IEcsWorld);
-
- var systems = new List();
- foreach (var systemType in systemTypes)
- {
- var system = (IEcsSystem)Activator.CreateInstance(systemType)!;
- ((IContextAware)system).SetContext(_context!);
- system.Initialize();
- systems.Add(system);
- _container.RegisterPlurality(system);
- }
+ var movementSystem = new MovementSystem();
+ ((IContextAware)movementSystem).SetContext(_context!);
+ _container!.RegisterPlurality(movementSystem);
- _container.Register(systems as IReadOnlyList);
- }
+ _ecsModule = new ArchEcsModule(enabled: true);
+ _ecsModule.Register(_container);
+ _ecsModule.Initialize();
- private EcsSystemRunner CreateRunner()
- {
- var runner = new EcsSystemRunner();
- ((IContextAware)runner).SetContext(_context!);
- runner.Initialize();
- return runner;
+ _world = _container.Get();
}
[Test]
- public void EcsWorld_Dispose_Should_Be_Idempotent()
+ public void World_Destroy_Should_Be_Safe()
{
- _ecsWorld = new EcsWorld();
- _ecsWorld.CreateEntity(typeof(Position));
+ InitializeEcsModule();
+ _world!.Create(new Position(0, 0));
Assert.DoesNotThrow(() =>
{
- _ecsWorld.Dispose();
- _ecsWorld.Dispose();
+ World.Destroy(_world);
+ _world = null;
});
-
- _ecsWorld = null;
- }
-
- [Test]
- public void EcsWorld_CreateEntity_WithNoComponents_Should_Work()
- {
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity();
-
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1));
- Assert.That(_ecsWorld.IsAlive(entity), Is.True);
}
[Test]
- public void EcsWorld_CreateEntity_WithMultipleComponents_Should_Work()
+ public void World_CreateEntity_WithNoComponents_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
+ InitializeEcsModule();
+ var entity = _world!.Create();
- var world = _ecsWorld.InternalWorld;
- Assert.That(world.Has(entity), Is.True);
- Assert.That(world.Has(entity), Is.True);
+ Assert.That(_world.Size, Is.EqualTo(1));
+ Assert.That(_world.IsAlive(entity), Is.True);
}
[Test]
- public void EcsWorld_IsAlive_AfterDestroy_Should_ReturnFalse()
+ public void World_CreateEntity_WithMultipleComponents_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position));
+ InitializeEcsModule();
+ var entity = _world!.Create(new Position(0, 0), new Velocity(1, 1));
- Assert.That(_ecsWorld.IsAlive(entity), Is.True);
-
- _ecsWorld.DestroyEntity(entity);
-
- Assert.That(_ecsWorld.IsAlive(entity), Is.False);
- }
-
- [Test]
- public void EcsSystemRunner_Update_WithoutStart_Should_NotUpdate()
- {
- InitializeEcsWithSystems(typeof(MovementSystem));
-
- var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity));
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(10, 5));
-
- var runner = CreateRunner();
-
- runner.Update(1.0f);
-
- ref var pos = ref world.Get(entity);
- Assert.That(pos.X, Is.EqualTo(0), "Position should not change without Start()");
- Assert.That(pos.Y, Is.EqualTo(0), "Position should not change without Start()");
+ Assert.That(_world.Has(entity), Is.True);
+ Assert.That(_world.Has(entity), Is.True);
}
[Test]
- public void EcsSystemRunner_StartStop_Should_ControlUpdates()
+ public void World_IsAlive_AfterDestroy_Should_ReturnFalse()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
-
- var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity));
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(10, 5));
+ InitializeEcsModule();
+ var entity = _world!.Create(new Position(0, 0));
- var runner = CreateRunner();
+ Assert.That(_world.IsAlive(entity), Is.True);
- runner.Start();
- runner.Update(1.0f);
- runner.Stop();
- runner.Update(1.0f);
+ _world.Destroy(entity);
- ref var pos = ref world.Get(entity);
- Assert.That(pos.X, Is.EqualTo(10).Within(0.001f), "Only first update should apply");
- Assert.That(pos.Y, Is.EqualTo(5).Within(0.001f), "Only first update should apply");
- }
-
- [Test]
- public void EcsSystemRunner_WithNoSystems_Should_NotThrow()
- {
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(new List() as IReadOnlyList);
-
- var runner = CreateRunner();
-
- Assert.DoesNotThrow(() =>
- {
- runner.Start();
- runner.Update(1.0f);
- runner.Stop();
- });
+ Assert.That(_world.IsAlive(entity), Is.False);
}
[Test]
- public void EcsSystemRunner_OnDestroy_Should_ClearSystems()
+ public void ArchEcsModule_Update_Should_UpdateSystems()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
-
- var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity));
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(10, 5));
-
- var runner = CreateRunner();
- runner.Start();
-
- // 销毁前先更新一次,记录初始位置
- runner.Update(1.0f);
- ref var posBeforeDestroy = ref world.Get(entity);
- var xBefore = posBeforeDestroy.X;
- var yBefore = posBeforeDestroy.Y;
+ InitializeEcsModule();
- runner.Destroy();
+ var entity = _world!.Create(new Position(0, 0), new Velocity(10, 5));
- // 销毁后再更新,位置应该保持不变
- runner.Update(1.0f);
+ _ecsModule!.Update(1.0f);
- ref var posAfterDestroy = ref world.Get(entity);
- Assert.That(posAfterDestroy.X, Is.EqualTo(xBefore), "Position should not change after Destroy()");
- Assert.That(posAfterDestroy.Y, Is.EqualTo(yBefore), "Position should not change after Destroy()");
+ ref var pos = ref _world.Get(entity);
+ Assert.That(pos.X, Is.EqualTo(10).Within(0.001f));
+ Assert.That(pos.Y, Is.EqualTo(5).Within(0.001f));
}
[Test]
- public void MultipleSystems_Should_ExecuteInPriorityOrder()
+ public void ArchEcsModule_WithNoSystems_Should_NotThrow()
{
- var executionOrder = new List();
-
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
-
- var systemA = new OrderTrackingSystem("A", 10, executionOrder);
- var systemB = new OrderTrackingSystem("B", -10, executionOrder);
- var systemC = new OrderTrackingSystem("C", 0, executionOrder);
-
- foreach (var system in new[] { systemA, systemB, systemC })
- {
- ((IContextAware)system).SetContext(_context!);
- system.Initialize();
- _container.RegisterPlurality(system);
- }
-
- _container.Register(new List { systemA, systemB, systemC } as IReadOnlyList);
+ // 不注册任何系统
+ _ecsModule = new ArchEcsModule(enabled: true);
+ _ecsModule.Register(_container!);
+ _ecsModule.Initialize();
- var runner = CreateRunner();
- runner.Start();
- runner.Update(1.0f);
+ _world = _container!.Get();
- Assert.That(executionOrder, Is.EqualTo(["B", "C", "A"]),
- "Systems should execute in priority order (B=-10, C=0, A=10)");
+ Assert.DoesNotThrow(() => { _ecsModule.Update(1.0f); });
}
[Test]
- public void ChainedSystems_Should_PassDataBetweenSystems()
+ public void ChainedUpdates_Should_AccumulateChanges()
{
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
-
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(10, 0));
+ InitializeEcsModule();
- var movementSystem = new MovementSystem();
- ((IContextAware)movementSystem).SetContext(_context!);
- movementSystem.Initialize();
- _container.RegisterPlurality(movementSystem);
+ var entity = _world!.Create(new Position(0, 0), new Velocity(10, 0));
- _container.Register(new List { movementSystem } as IReadOnlyList);
+ _ecsModule!.Update(1.0f);
+ _ecsModule.Update(1.0f);
- var runner = CreateRunner();
- runner.Start();
- runner.Update(1.0f);
- runner.Update(1.0f);
-
- ref var pos = ref world.Get(entity);
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(20).Within(0.001f), "Position should accumulate over multiple updates");
}
- [Test]
- public void InitializeEcs_CalledTwice_Should_BeIdempotent()
- {
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld);
-
- var ecsWorld1 = _context!.GetEcsWorld();
- var ecsWorld2 = _context.GetEcsWorld();
-
- Assert.That(ecsWorld2, Is.SameAs(ecsWorld1), "Should return same world instance");
- }
-
- [Test]
- public void GetEcsWorld_Should_ReturnIEcsWorld()
- {
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld);
-
- var ecsWorld = _context!.GetEcsWorld();
-
- Assert.That(ecsWorld, Is.InstanceOf());
- Assert.That(ecsWorld, Is.InstanceOf());
- }
-
[Test]
public void Component_AddAfterCreation_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(Array.Empty());
- var world = _ecsWorld.InternalWorld;
+ InitializeEcsModule();
+ var entity = _world!.Create();
- world.Add(entity, new Position(5, 10));
+ _world.Add(entity, new Position(5, 10));
- Assert.That(world.Has(entity), Is.True);
- ref var pos = ref world.Get(entity);
+ Assert.That(_world.Has(entity), Is.True);
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(5));
Assert.That(pos.Y, Is.EqualTo(10));
}
@@ -305,42 +160,57 @@ public void Component_AddAfterCreation_Should_Work()
[Test]
public void Component_Remove_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
- var world = _ecsWorld.InternalWorld;
+ InitializeEcsModule();
+ var entity = _world!.Create(new Position(0, 0), new Velocity(1, 1));
- world.Remove(entity);
+ _world.Remove(entity);
- Assert.That(world.Has(entity), Is.True);
- Assert.That(world.Has(entity), Is.False);
+ Assert.That(_world.Has(entity), Is.True);
+ Assert.That(_world.Has(entity), Is.False);
}
[Test]
public void Component_Replace_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position));
- var world = _ecsWorld.InternalWorld;
+ InitializeEcsModule();
+ var entity = _world!.Create(new Position(1, 1));
- world.Set(entity, new Position(1, 1));
- world.Set(entity, new Position(100, 200));
+ _world.Set(entity, new Position(100, 200));
- ref var pos = ref world.Get(entity);
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(100));
Assert.That(pos.Y, Is.EqualTo(200));
}
-}
-internal class OrderTrackingSystem(string name, int priority, List executionOrder) : EcsSystemBase
-{
- public override int Priority { get; } = priority;
-
- protected override void OnEcsInit()
+ [Test]
+ public async Task ArchEcsModule_DestroyAsync_Should_CleanupResources()
{
+ InitializeEcsModule();
+
+ _world!.Create(new Position(0, 0));
+ Assert.That(_world.Size, Is.EqualTo(1));
+
+ await _ecsModule!.DestroyAsync();
+
+ // World 应该已经被销毁
+ _world = null;
}
- public override void Update(float deltaTime)
+ [Test]
+ public void MultipleEntities_WithDifferentComponents_Should_CoExist()
{
- executionOrder.Add(name);
+ InitializeEcsModule();
+
+ var entity1 = _world!.Create(new Position(0, 0), new Velocity(1, 1));
+ var entity2 = _world.Create(new Position(10, 10));
+ var entity3 = _world.Create(new Velocity(5, 5));
+
+ Assert.That(_world.Size, Is.EqualTo(3));
+ Assert.That(_world.Has(entity1), Is.True);
+ Assert.That(_world.Has(entity1), Is.True);
+ Assert.That(_world.Has(entity2), Is.True);
+ Assert.That(_world.Has(entity2), Is.False);
+ Assert.That(_world.Has(entity3), Is.False);
+ Assert.That(_world.Has(entity3), Is.True);
}
}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/ecs/EcsBasicTests.cs b/GFramework.Core.Tests/ecs/EcsBasicTests.cs
index 036345f9..c1d9f52c 100644
--- a/GFramework.Core.Tests/ecs/EcsBasicTests.cs
+++ b/GFramework.Core.Tests/ecs/EcsBasicTests.cs
@@ -1,229 +1,161 @@
using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
using Arch.Core;
-using GFramework.Core.Abstractions.ecs;
+using GFramework.Core.Abstractions.rule;
using GFramework.Core.architecture;
using GFramework.Core.ecs;
using GFramework.Core.ecs.components;
using GFramework.Core.ecs.systems;
using GFramework.Core.ioc;
-using GFramework.Core.logging;
using NUnit.Framework;
namespace GFramework.Core.Tests.ecs;
///
-/// ECS基础功能测试类,用于验证ECS系统的核心功能。
-/// 包括实体创建、组件设置、系统更新、实体销毁等基本操作。
+/// ECS 基础功能测试类 - 使用 Arch 原生 API
///
[TestFixture]
[Experimental("GFrameworkECS")]
public class EcsBasicTests
{
- ///
- /// 测试初始化方法,在每个测试方法执行前运行。
- /// 负责初始化日志工厂、依赖注入容器和架构上下文。
- ///
[SetUp]
public void Setup()
{
- LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
-
_container = new MicrosoftDiContainer();
- var loggerField = typeof(MicrosoftDiContainer).GetField("_logger",
- BindingFlags.NonPublic | BindingFlags.Instance);
- loggerField?.SetValue(_container,
- LoggerFactoryResolver.Provider.CreateLogger(nameof(EcsBasicTests)));
-
_context = new ArchitectureContext(_container);
}
- ///
- /// 测试清理方法,在每个测试方法执行后运行。
- /// 负责释放ECS世界资源并清空容器和上下文。
- ///
[TearDown]
public void TearDown()
{
- _ecsWorld?.Dispose();
- _ecsWorld = null;
+ if (_world != null)
+ {
+ World.Destroy(_world);
+ _world = null;
+ }
+
_container?.Clear();
_context = null;
+ _ecsModule = null;
}
private MicrosoftDiContainer? _container;
private ArchitectureContext? _context;
- private EcsWorld? _ecsWorld;
+ private World? _world;
+ private ArchEcsModule? _ecsModule;
///
- /// 初始化ECS系统并注册指定类型的系统实例。
+ /// 初始化 ECS 模块
///
- /// 需要注册的系统类型数组
- private void InitializeEcsWithSystems(params Type[] systemTypes)
+ private void InitializeEcsModule()
{
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld as IEcsWorld);
-
- var systems = new List();
- foreach (var systemType in systemTypes)
- {
- var system = (IEcsSystem)Activator.CreateInstance(systemType)!;
- system.SetContext(_context!);
- system.Initialize();
- systems.Add(system);
- _container.RegisterPlurality(system);
- }
-
- _container.Register(systems as IReadOnlyList);
+ // 注册系统(直接继承 ArchSystemAdapter,它继承自 AbstractSystem)
+ var movementSystem = new MovementSystem();
+ ((IContextAware)movementSystem).SetContext(_context!);
+ _container!.RegisterPlurality(movementSystem);
+
+ // 创建并注册 ArchEcsModule
+ _ecsModule = new ArchEcsModule(enabled: true);
+ _ecsModule.Register(_container);
+ _ecsModule.Initialize();
+
+ // 获取 World
+ _world = _container.Get();
}
- ///
- /// 测试ECS初始化功能,验证是否能正确创建EcsWorld实例。
- ///
[Test]
- [Experimental("GFrameworkECS")]
- public void Test_01_InitializeEcs_Should_Create_EcsWorld()
+ public void Test_01_InitializeEcs_Should_Create_World()
{
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld);
-
- var ecsWorld = _context!.GetEcsWorld();
+ InitializeEcsModule();
- Assert.That(ecsWorld, Is.Not.Null, "EcsWorld should be created");
- Assert.That(ecsWorld.EntityCount, Is.EqualTo(0), "Initial entity count should be 0");
+ Assert.That(_world, Is.Not.Null, "World should be created");
+ Assert.That(_world!.Size, Is.EqualTo(0), "Initial entity count should be 0");
}
- ///
- /// 测试实体创建功能,验证能否成功创建带有指定组件的实体。
- ///
[Test]
public void Test_02_CreateEntity_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
+ InitializeEcsModule();
+
+ var entity = _world!.Create(new Position(0, 0), new Velocity(1, 1));
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1), "Entity count should be 1");
- Assert.That(_ecsWorld.IsAlive(entity), Is.True, "Entity should be alive");
+ Assert.That(_world.Size, Is.EqualTo(1), "Entity count should be 1");
+ Assert.That(_world.IsAlive(entity), Is.True, "Entity should be alive");
}
- ///
- /// 测试组件设置功能,验证能否正确存储和获取组件数据。
- ///
[Test]
public void Test_03_SetComponent_Should_Store_Data()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position));
- var world = _ecsWorld.InternalWorld;
+ InitializeEcsModule();
- world.Set(entity, new Position(10, 20));
+ var entity = _world!.Create(new Position(10, 20));
- Assert.That(world.Has(entity), Is.True, "Entity should have Position component");
- ref var pos = ref world.Get(entity);
+ Assert.That(_world.Has(entity), Is.True, "Entity should have Position component");
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(10), "Position.X should be 10");
Assert.That(pos.Y, Is.EqualTo(20), "Position.Y should be 20");
}
- ///
- /// 测试移动系统功能,验证系统能否正确更新实体位置。
- ///
[Test]
public void Test_04_MovementSystem_Should_Update_Position()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
-
- var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity));
-
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(10, 5));
-
- var systems = _container!.Get>();
- Assert.That(systems, Is.Not.Null);
- Assert.That(systems!.Count, Is.GreaterThan(0));
+ InitializeEcsModule();
- var movementSystem = systems.First(s => s is MovementSystem) as MovementSystem;
- Assert.That(movementSystem, Is.Not.Null);
+ var entity = _world!.Create(new Position(0, 0), new Velocity(10, 5));
- movementSystem!.Update(1.0f);
+ // 更新系统
+ _ecsModule!.Update(1.0f);
- ref var pos = ref world.Get(entity);
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(10).Within(0.001f), "X position should be 10");
Assert.That(pos.Y, Is.EqualTo(5).Within(0.001f), "Y position should be 5");
}
- ///
- /// 测试实体销毁功能,验证能否正确销毁实体并更新实体计数。
- ///
[Test]
public void Test_05_DestroyEntity_Should_Work()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position));
+ InitializeEcsModule();
- _ecsWorld.DestroyEntity(entity);
+ var entity = _world!.Create(new Position(0, 0));
+ _world.Destroy(entity);
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0), "Entity count should be 0");
- Assert.That(_ecsWorld.IsAlive(entity), Is.False, "Entity should not be alive");
+ Assert.That(_world.Size, Is.EqualTo(0), "Entity count should be 0");
+ Assert.That(_world.IsAlive(entity), Is.False, "Entity should not be alive");
}
- ///
- /// 测试世界清理功能,验证能否清除所有实体。
- ///
[Test]
public void Test_06_ClearWorld_Should_Remove_All_Entities()
{
- _ecsWorld = new EcsWorld();
+ InitializeEcsModule();
+
for (int i = 0; i < 10; i++)
{
- _ecsWorld.CreateEntity(typeof(Position));
+ _world!.Create(new Position(0, 0));
}
- _ecsWorld.Clear();
+ _world!.Clear();
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0), "Entity count should be 0 after clear");
+ Assert.That(_world.Size, Is.EqualTo(0), "Entity count should be 0 after clear");
}
- ///
- /// 测试多个实体的批量更新功能,验证系统能否正确处理多个实体的更新。
- ///
[Test]
public void Test_07_Multiple_Entities_Should_Update_Correctly()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
+ InitializeEcsModule();
- var world = _ecsWorld!.InternalWorld;
var entities = new Entity[10];
-
for (var i = 0; i < 10; i++)
{
- entities[i] = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
- world.Set(entities[i], new Position(0, 0));
- world.Set(entities[i], new Velocity(i, i * 2));
+ entities[i] = _world!.Create(new Position(0, 0), new Velocity(i, i * 2));
}
- var systems = _container!.Get>();
- var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem;
-
- movementSystem!.Update(1.0f);
+ // 更新系统
+ _ecsModule!.Update(1.0f);
for (int i = 0; i < 10; i++)
{
- ref var pos = ref world.Get(entities[i]);
+ ref var pos = ref _world!.Get(entities[i]);
Assert.That(pos.X, Is.EqualTo(i).Within(0.001f), $"Entity {i} X position should be {i}");
Assert.That(pos.Y, Is.EqualTo(i * 2).Within(0.001f), $"Entity {i} Y position should be {i * 2}");
}
}
-
- ///
- /// 测试未初始化情况下获取ECS世界的异常处理。
- ///
- [Test]
- public void Test_08_GetEcsWorld_Without_Initialize_Should_Throw()
- {
- Assert.Throws(() => { _context!.GetEcsWorld(); },
- "ECS World not initialized. Enable ECS in configuration.");
- }
}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs b/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs
index e6703935..55287a9d 100644
--- a/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs
+++ b/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs
@@ -1,358 +1,205 @@
using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
using Arch.Core;
-using GFramework.Core.Abstractions.ecs;
+using GFramework.Core.Abstractions.rule;
using GFramework.Core.architecture;
using GFramework.Core.ecs;
using GFramework.Core.ecs.components;
using GFramework.Core.ecs.systems;
using GFramework.Core.ioc;
-using GFramework.Core.logging;
using NUnit.Framework;
namespace GFramework.Core.Tests.ecs;
///
-/// ECS集成测试类,用于验证ECS系统的整体功能和性能表现。
-/// 包括实体管理、组件操作、系统调度、优先级控制以及性能基准测试。
+/// ECS 集成测试类 - 使用 Arch 原生 API
///
[TestFixture]
[Experimental("GFrameworkECS")]
public class EcsIntegrationTests
{
- ///
- /// 测试初始化方法,在每个测试方法执行前运行。
- /// 负责初始化日志工厂、依赖注入容器和架构上下文。
- ///
[SetUp]
public void Setup()
{
- LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
-
_container = new MicrosoftDiContainer();
- var loggerField = typeof(MicrosoftDiContainer).GetField("_logger",
- BindingFlags.NonPublic | BindingFlags.Instance);
- loggerField?.SetValue(_container,
- LoggerFactoryResolver.Provider.CreateLogger(nameof(EcsIntegrationTests)));
-
_context = new ArchitectureContext(_container);
}
- ///
- /// 测试清理方法,在每个测试方法执行后运行。
- /// 负责释放ECS世界资源并清空容器和上下文。
- ///
[TearDown]
public void TearDown()
{
- _ecsWorld?.Dispose();
- _ecsWorld = null;
+ if (_world != null)
+ {
+ World.Destroy(_world);
+ _world = null;
+ }
+
_container?.Clear();
_context = null;
+ _ecsModule = null;
}
private MicrosoftDiContainer? _container;
private ArchitectureContext? _context;
- private EcsWorld? _ecsWorld;
+ private World? _world;
+ private ArchEcsModule? _ecsModule;
///
- /// 初始化ECS系统并注册指定类型的系统实例。
+ /// 初始化 ECS 模块
///
- /// 需要注册的系统类型数组
- private void InitializeEcsWithSystems(params Type[] systemTypes)
+ private void InitializeEcsModule()
{
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld as IEcsWorld);
+ // 注册系统
+ var movementSystem = new MovementSystem();
+ ((IContextAware)movementSystem).SetContext(_context!);
+ _container!.RegisterPlurality(movementSystem);
- var systems = new List();
- foreach (var systemType in systemTypes)
- {
- var system = (IEcsSystem)Activator.CreateInstance(systemType)!;
- system.SetContext(_context!);
- system.Initialize();
- systems.Add(system);
- _container.RegisterPlurality(system);
- }
+ // 创建并注册 ArchEcsModule
+ _ecsModule = new ArchEcsModule(enabled: true);
+ _ecsModule.Register(_container);
+ _ecsModule.Initialize();
- _container.Register(systems as IReadOnlyList);
+ // 获取 World
+ _world = _container.Get();
}
- ///
- /// 测试ECS初始化功能,验证是否能正确创建EcsWorld实例。
- ///
[Test]
- public void InitializeEcs_Should_Create_EcsWorld()
+ public void InitializeEcs_Should_Create_World()
{
- _ecsWorld = new EcsWorld();
- _container!.Register(_ecsWorld);
- _container.Register(_ecsWorld);
-
- var ecsWorld = _context!.GetEcsWorld();
+ InitializeEcsModule();
- Assert.That(ecsWorld, Is.Not.Null);
- Assert.That(ecsWorld.EntityCount, Is.EqualTo(0));
+ Assert.That(_world, Is.Not.Null);
+ Assert.That(_world!.Size, Is.EqualTo(0));
}
- ///
- /// 测试实体创建功能,验证创建实体后实体计数是否正确增加。
- ///
[Test]
public void CreateEntity_Should_Increase_EntityCount()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
+ InitializeEcsModule();
+
+ var entity = _world!.Create(new Position(0, 0), new Velocity(1, 1));
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1));
- Assert.That(_ecsWorld.IsAlive(entity), Is.True);
+ Assert.That(_world.Size, Is.EqualTo(1));
+ Assert.That(_world.IsAlive(entity), Is.True);
}
- ///
- /// 测试实体销毁功能,验证销毁实体后实体计数是否正确减少。
- ///
[Test]
public void DestroyEntity_Should_Decrease_EntityCount()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position));
+ InitializeEcsModule();
- _ecsWorld.DestroyEntity(entity);
+ var entity = _world!.Create(new Position(0, 0));
+ _world.Destroy(entity);
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0));
- Assert.That(_ecsWorld.IsAlive(entity), Is.False);
+ Assert.That(_world.Size, Is.EqualTo(0));
+ Assert.That(_world.IsAlive(entity), Is.False);
}
- ///
- /// 测试组件设置功能,验证能否正确存储和获取组件数据。
- ///
[Test]
public void SetComponent_Should_Store_ComponentData()
{
- _ecsWorld = new EcsWorld();
- var entity = _ecsWorld.CreateEntity(typeof(Position));
+ InitializeEcsModule();
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(10, 20));
+ var entity = _world!.Create(new Position(10, 20));
- Assert.That(world.Has(entity), Is.True);
- ref var pos = ref world.Get(entity);
+ Assert.That(_world.Has(entity), Is.True);
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(10));
Assert.That(pos.Y, Is.EqualTo(20));
}
- ///
- /// 测试世界清理功能,验证能否清除所有实体。
- ///
[Test]
public void ClearWorld_Should_Remove_All_Entities()
{
- _ecsWorld = new EcsWorld();
+ InitializeEcsModule();
+
for (int i = 0; i < 10; i++)
{
- _ecsWorld.CreateEntity(typeof(Position));
+ _world!.Create(new Position(0, 0));
}
- _ecsWorld.Clear();
+ _world!.Clear();
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0));
+ Assert.That(_world.Size, Is.EqualTo(0));
}
- ///
- /// 测试ECS系统注册功能,验证系统能否正确添加到运行器中。
- ///
[Test]
- public void RegisterEcsSystem_Should_Add_System_To_Runner()
+ public void RegisterEcsSystem_Should_Add_System_To_Module()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
+ InitializeEcsModule();
- var systems = _container!.Get>();
- Assert.That(systems, Is.Not.Null);
- Assert.That(systems!.Count, Is.EqualTo(1));
- Assert.That(systems[0], Is.InstanceOf());
+ var adapters = _container!.GetAll>();
+ Assert.That(adapters, Is.Not.Null);
+ Assert.That(adapters.Count, Is.EqualTo(1));
+ Assert.That(adapters[0], Is.InstanceOf());
}
- ///
- /// 测试移动系统功能,验证系统能否正确更新单个实体的位置。
- ///
[Test]
public void MovementSystem_Should_Update_Position()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
+ InitializeEcsModule();
- var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity));
+ var entity = _world!.Create(new Position(0, 0), new Velocity(10, 5));
- var world = _ecsWorld.InternalWorld;
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(10, 5));
+ _ecsModule!.Update(1.0f);
- var systems = _container!.Get>();
- var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem;
-
- movementSystem!.Update(1.0f);
-
- ref var pos = ref world.Get(entity);
+ ref var pos = ref _world.Get(entity);
Assert.That(pos.X, Is.EqualTo(10).Within(0.001f));
Assert.That(pos.Y, Is.EqualTo(5).Within(0.001f));
}
- ///
- /// 测试移动系统功能,验证系统能否正确批量更新多个实体的位置。
- ///
[Test]
public void MovementSystem_Should_Update_Multiple_Entities()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
+ InitializeEcsModule();
- var world = _ecsWorld!.InternalWorld;
var entities = new Entity[100];
-
for (var i = 0; i < 100; i++)
{
- entities[i] = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
- world.Set(entities[i], new Position(0, 0));
- world.Set(entities[i], new Velocity(i, i * 2));
+ entities[i] = _world!.Create(new Position(0, 0), new Velocity(i, i * 2));
}
- var systems = _container!.Get>();
- var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem;
-
- movementSystem!.Update(0.5f);
+ _ecsModule!.Update(0.5f);
for (var i = 0; i < 100; i++)
{
- ref var pos = ref world.Get(entities[i]);
+ ref var pos = ref _world!.Get(entities[i]);
Assert.That(pos.X, Is.EqualTo(i * 0.5f).Within(0.001f));
Assert.That(pos.Y, Is.EqualTo(i * 2 * 0.5f).Within(0.001f));
}
}
- ///
- /// 测试ECS系统运行器的优先级调度功能,验证系统是否按优先级顺序执行。
- ///
- [Test]
- public void EcsSystemRunner_Should_Respect_Priority()
- {
- InitializeEcsWithSystems(typeof(LowPrioritySystem), typeof(HighPrioritySystem));
-
- var systems = _container!.Get>();
- Assert.That(systems, Is.Not.Null);
- Assert.That(systems!.Count, Is.EqualTo(2));
-
- var sortedSystems = systems.OrderBy(s => s.Priority).ToList();
- Assert.That(sortedSystems[0], Is.InstanceOf());
- Assert.That(sortedSystems[1], Is.InstanceOf());
- }
-
- ///
- /// 测试未初始化情况下获取ECS世界的异常处理。
- ///
- [Test]
- public void GetEcsWorld_Without_Initialize_Should_Throw()
- {
- Assert.Throws(() => { _context!.GetEcsWorld(); },
- "ECS World not initialized. Enable ECS in configuration.");
- }
-
- ///
- /// 性能基准测试:验证更新10000个实体的性能表现。
- ///
[Test]
public void Performance_Test_10000_Entities()
{
- InitializeEcsWithSystems(typeof(MovementSystem));
-
- var world = _ecsWorld!.InternalWorld;
+ InitializeEcsModule();
for (int i = 0; i < 10000; i++)
{
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(1, 1));
+ _world!.Create(new Position(0, 0), new Velocity(1, 1));
}
- var systems = _container!.Get>();
- var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem;
-
var startTime = DateTime.UtcNow;
- movementSystem!.Update(0.016f);
+ _ecsModule!.Update(0.016f);
var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds;
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(10000));
+ Assert.That(_world!.Size, Is.EqualTo(10000));
Assert.That(elapsed, Is.LessThan(100), $"Updating 10000 entities took: {elapsed}ms");
}
- ///
- /// 性能基准测试:验证创建1000个实体的性能表现。
- ///
[Test]
public void Performance_Test_1000_Entities_Creation()
{
- _ecsWorld = new EcsWorld();
- var world = _ecsWorld.InternalWorld;
+ InitializeEcsModule();
var startTime = DateTime.UtcNow;
for (int i = 0; i < 1000; i++)
{
- var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity));
- world.Set(entity, new Position(0, 0));
- world.Set(entity, new Velocity(1, 1));
+ _world!.Create(new Position(0, 0), new Velocity(1, 1));
}
var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds;
- Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1000));
+ Assert.That(_world!.Size, Is.EqualTo(1000));
Assert.That(elapsed, Is.LessThan(50), $"Creating 1000 entities took: {elapsed}ms");
}
-}
-
-///
-/// 高优先级系统示例,用于测试系统调度优先级功能。
-///
-public class HighPrioritySystem : EcsSystemBase
-{
- ///
- /// 获取系统优先级,数值越小优先级越高。
- ///
- public override int Priority => -100;
-
- ///
- /// ECS初始化回调方法。
- ///
- protected override void OnEcsInit()
- {
- }
-
- ///
- /// 系统更新方法。
- ///
- /// 帧间隔时间
- public override void Update(float deltaTime)
- {
- }
-}
-
-///
-/// 低优先级系统示例,用于测试系统调度优先级功能。
-///
-public class LowPrioritySystem : EcsSystemBase
-{
- ///
- /// 获取系统优先级,数值越大优先级越低。
- ///
- public override int Priority => 100;
-
- ///
- /// ECS初始化回调方法。
- ///
- protected override void OnEcsInit()
- {
- }
-
- ///
- /// 系统更新方法。
- ///
- /// 帧间隔时间
- public override void Update(float deltaTime)
- {
- }
}
\ No newline at end of file
diff --git a/GFramework.Core/ecs/systems/MovementSystem.cs b/GFramework.Core/ecs/systems/MovementSystem.cs
index 3c33b449..897ca8b8 100644
--- a/GFramework.Core/ecs/systems/MovementSystem.cs
+++ b/GFramework.Core/ecs/systems/MovementSystem.cs
@@ -4,7 +4,7 @@
namespace GFramework.Core.ecs.systems;
///
-/// 移动系统 - Arch 原生实现
+/// 移动系统 - 继承 ArchSystemAdapter
/// 负责更新具有位置和速度组件的实体的位置
///
public sealed class MovementSystem : ArchSystemAdapter
@@ -12,28 +12,27 @@ public sealed class MovementSystem : ArchSystemAdapter
private QueryDescription _query;
///
- /// 初始化系统
+ /// Arch 系统初始化
///
- public void Initialize(World world)
+ protected override void OnArchInitialize()
{
// 创建查询:查找所有同时拥有Position和Velocity组件的实体
_query = new QueryDescription()
.WithAll();
}
-
///
/// 系统更新方法,每帧调用一次
///
- /// ECS 世界
/// 帧间隔时间
- public void Update(World world, float deltaTime)
+ protected override void OnUpdate(in float deltaTime)
{
// 查询并更新所有符合条件的实体
- world.Query(in _query, (ref Position pos, ref Velocity vel) =>
+ var f = deltaTime;
+ World.Query(in _query, (ref Position pos, ref Velocity vel) =>
{
- pos.X += vel.X * deltaTime;
- pos.Y += vel.Y * deltaTime;
+ pos.X += vel.X * f;
+ pos.Y += vel.Y * f;
});
}
}
\ No newline at end of file
From 76d7f8040889043bc51b6cc977fe28e1ad87e1de Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Mon, 2 Mar 2026 21:13:16 +0800
Subject: [PATCH 5/7] =?UTF-8?q?fix(ecs):=20=E4=BF=AE=E5=A4=8D=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E9=87=8D=E5=A4=8D=E5=88=9D=E5=A7=8B=E5=8C=96=E5=92=8C?=
=?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=9B=B4=E6=96=B0=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 添加 _isInitialized 标志防止 ArchEcsModule 重复初始化
- 在 Initialize 方法中检查是否已初始化避免重复执行
- 设置初始化标志在所有系统初始化完成后
- 修改 DestroyAsync 方法确保仅在已初始化时执行销毁
- 重置 _isInitialized 标志为 false 在销毁时
- 修正 ArchSystemAdapter 注释描述 OnUpdate 为虚方法而非抽象方法
- 简化 EcsAdvancedTests 中的断言语法移除不必要的大括号
---
GFramework.Core.Tests/ecs/EcsAdvancedTests.cs | 2 +-
GFramework.Core/ecs/ArchEcsModule.cs | 13 ++++++++++++-
GFramework.Core/ecs/ArchSystemAdapter.cs | 2 +-
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs b/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
index 298e0e68..3831138f 100644
--- a/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
+++ b/GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
@@ -126,7 +126,7 @@ public void ArchEcsModule_WithNoSystems_Should_NotThrow()
_world = _container!.Get();
- Assert.DoesNotThrow(() => { _ecsModule.Update(1.0f); });
+ Assert.DoesNotThrow(() => _ecsModule.Update(1.0f));
}
[Test]
diff --git a/GFramework.Core/ecs/ArchEcsModule.cs b/GFramework.Core/ecs/ArchEcsModule.cs
index 549b2347..df25f37f 100644
--- a/GFramework.Core/ecs/ArchEcsModule.cs
+++ b/GFramework.Core/ecs/ArchEcsModule.cs
@@ -11,6 +11,7 @@ public sealed class ArchEcsModule : IServiceModule
{
private readonly List> _systems = [];
private IIocContainer? _container;
+ private bool _isInitialized;
private World? _world;
///
@@ -58,6 +59,12 @@ public void Initialize()
{
if (!IsEnabled || _world == null || _container == null) return;
+ // 防止重复初始化
+ if (_isInitialized)
+ {
+ return;
+ }
+
// 从容器获取所有适配器
var adapters = _container.GetAll>();
if (adapters.Count > 0)
@@ -70,6 +77,8 @@ public void Initialize()
system.Initialize();
}
}
+
+ _isInitialized = true;
}
///
@@ -77,7 +86,7 @@ public void Initialize()
///
public async ValueTask DestroyAsync()
{
- if (!IsEnabled) return;
+ if (!IsEnabled || !_isInitialized) return;
// 销毁所有系统
foreach (var system in _systems)
@@ -94,6 +103,8 @@ public async ValueTask DestroyAsync()
_world = null;
}
+ _isInitialized = false;
+
await ValueTask.CompletedTask;
}
diff --git a/GFramework.Core/ecs/ArchSystemAdapter.cs b/GFramework.Core/ecs/ArchSystemAdapter.cs
index 47b9346e..8d567fd2 100644
--- a/GFramework.Core/ecs/ArchSystemAdapter.cs
+++ b/GFramework.Core/ecs/ArchSystemAdapter.cs
@@ -39,7 +39,7 @@ void ArchSys.ISystem.BeforeUpdate(in T t)
///
/// 显式实现 Arch.System.ISystem<T> 的主更新方法
- /// 调用受保护的抽象方法 OnUpdate 以强制子类实现核心更新逻辑
+ /// 调用受保护的虚方法 OnUpdate 以强制子类实现核心更新逻辑
///
/// 系统数据参数(通常是 deltaTime)
public void Update(in T t)
From 6bcd1928e57647498e8b8978a3012379d4c79175 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Mon, 2 Mar 2026 21:15:35 +0800
Subject: [PATCH 6/7] =?UTF-8?q?fix(ecs):=20=E4=BF=AE=E5=A4=8D=E5=BC=82?=
=?UTF-8?q?=E6=AD=A5=E9=94=80=E6=AF=81=E6=96=B9=E6=B3=95=E8=BF=94=E5=9B=9E?=
=?UTF-8?q?=E5=80=BC=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除 DestroyAsync 方法中的 async 关键字
- 修改条件判断逻辑,确保返回正确的 ValueTask.CompletedTask
- 删除不必要的 await 关键字,直接返回 ValueTask.CompletedTask
- 保持方法的异步操作正确性
---
GFramework.Core/ecs/ArchEcsModule.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/GFramework.Core/ecs/ArchEcsModule.cs b/GFramework.Core/ecs/ArchEcsModule.cs
index df25f37f..784b8229 100644
--- a/GFramework.Core/ecs/ArchEcsModule.cs
+++ b/GFramework.Core/ecs/ArchEcsModule.cs
@@ -84,9 +84,12 @@ public void Initialize()
///
/// 异步销毁
///
- public async ValueTask DestroyAsync()
+ public ValueTask DestroyAsync()
{
- if (!IsEnabled || !_isInitialized) return;
+ if (!IsEnabled || !_isInitialized)
+ {
+ return ValueTask.CompletedTask;
+ }
// 销毁所有系统
foreach (var system in _systems)
@@ -105,7 +108,7 @@ public async ValueTask DestroyAsync()
_isInitialized = false;
- await ValueTask.CompletedTask;
+ return ValueTask.CompletedTask;
}
///
From 6d36081da679cc30f38715adcad73ff882373684 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Mon, 2 Mar 2026 21:19:58 +0800
Subject: [PATCH 7/7] =?UTF-8?q?refactor(ecs):=20=E6=9B=B4=E6=96=B0=20ArchS?=
=?UTF-8?q?ystemAdapter=20=E4=B8=AD=20OnUpdate=20=E6=96=B9=E6=B3=95?=
=?UTF-8?q?=E7=9A=84=E6=96=87=E6=A1=A3=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修改了核心更新逻辑方法的文档描述
- 明确说明该方法为受保护虚方法,默认不执行任何操作
- 详细说明子类可以按需选择性重写此方法来实现具体系统逻辑
- 更新了参数说明文档
---
GFramework.Core/ecs/ArchSystemAdapter.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/GFramework.Core/ecs/ArchSystemAdapter.cs b/GFramework.Core/ecs/ArchSystemAdapter.cs
index 8d567fd2..8a86c98e 100644
--- a/GFramework.Core/ecs/ArchSystemAdapter.cs
+++ b/GFramework.Core/ecs/ArchSystemAdapter.cs
@@ -109,8 +109,8 @@ protected virtual void OnBeforeUpdate(in T t)
}
///
- /// 核心更新逻辑的受保护抽象方法
- /// 子类必须重写此方法以实现具体的系统更新功能
+ /// 受保护虚方法,在系统更新时被调用。
+ /// 默认实现不执行任何操作,子类可以根据需要选择性地重写此方法以实现具体的系统逻辑。
///
/// 系统数据参数(通常是 deltaTime)
protected virtual void OnUpdate(in T t)