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)