VContainer-integrations/ecs | 集成——ECS (beta)
VContainer 支持 Unity ECS(实体组件系统)与常规 C# 世界的集成。
(实验性功能,欢迎反馈!:0)
:::caution
当前功能需要 Unity 2019.3 或更高版本
:::
设置
当项目中安装 com.unity.entities
包时,VContainer 的 ECS 功能将自动启用。
- 目前,ECS 目前处于预览阶段。可能需要在
[Windows] -> [Package Manager]
和[Advanced] -> [Show preview packages]
中进行设置。 - 选择
Entities
包并点击[Install]
。
安装 com.unity.entities
完成后,VCONTAINER_ECS_INTEGRATION
编译符号会自动定义并启用以下功能。
使用 Unity 的默认 World
默认情况下,ECS 会自动实例化继承了 ComponentSystemBase
的类,并将其添加到默认的 World 中运行。
在这种模式下,可通过方法注入到 ECS 的 System
中。
(由于 Unity 自动调用构造函数,故不能使用构造注入。)
class SystemA : SystemBase
{
[Inject]
public void Construct(Settings settings)
{
// ...
}
protected override void OnUpdate()
{
// ...
}
}
// 将 `System` 注入到 Unity 的默认 World 中
builder.RegisterSystemFromDefaultWorld<SystemA>();
// builder.RegisterSystemFromDefaultWorld<SystemB>();
// builder.RegisterSystemFromDefaultWorld<SystemC>();
// 其他依赖可以注入到 System 中。
builder.RegisterInstance(settings);
// ...
(可选)上述内容也可以通过分组声明如下:
builder.UseDefaultWorld(systems =>
{
systems.Add<SystemA>();
// systems.Add<SystemB>();
// systems.Add<SystemC>();
// ...
});
底层实现等价于:
var system = World.DefaultGameObjectInjectionWorld.GetExistingSystem<SystemA>();
system.Construct(settings);
:::note
默认情况下(未使用 UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP
),可以通过 Attribute(例如:[UpdateInGroup(typeof(SystemGroupType))]
)控制 System 所属的 SystemGroup 。
:::
实体配置示例(使用默认 World)
public class GameLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
builder.UseDefaultWorld(systems =>
{
systems.Add<SystemA>();
})
}
}
public class SystemA : SystemBase
{
[Inject]
public void Construct(Foo foo)
{
// 创建实体...
var archtype = World.EntityManager.CreateArchetype(typeof(ComponentDataA));
World.EntityManager.CreateEntity(archtype);
}
protected override void OnUpdate()
{
Entities.ForEach((ref ComponentDataA data) =>
{
// 逻辑处理...
})
.Schedule();
}
}
使用自定义 World
ECS 允许创建并注册自己的系统。
可通过两种方式禁用 Unity 的自动系统引导:
- 定义符号
UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP
可以禁用所有 World 和 System 的自动引导。 - 通过在类定义中添加
[DisableAutoCreation]
属性来禁用每个系统的自动引导。
禁用自动引导后,可使用构造注入:
public class SystemA : SystemBase
{
readonly ServiceA serviceA;
// 构造函数注入
public SampleSystem(ServiceA serviceA)
{
this.serviceA = serviceA;
}
protected override void OnUpdate()
{
// ...
}
}
需手动配置 World 才能使用这个系统。
VContainer 支持 World 的创建与注册。
// 注册由 VContainer 管理的新世界。
builder.RegisterNewWorld("My World 1", Lifetime.Scoped);
// 通过指定要添加的 World 名称来注册 System。
builder.RegisterSystemIntoWorld<SystemA>("My World 1");
// builder.RegisterSystemIntoWorld<SystemB>("My World 1");
// builder.RegisterSystemIntoWorld<SystemC>("My World 1");
// 其他依赖可以注入到 System 中。
builder.Register<ServiceA>(Lifetime.Singleton);
(可选)上述内容也可以通过分组声明如下:
builder.UseNewWorld("My World 1", Lifetime.Scoped, systems =>
{
systems.Add<SystemA>();
// systems.Add<SystemB>();
// systems.Add<SystemC>();
// ...
});
底层实现等价于:
// 当解析 World 时...
var world = new World("My World 1");
world.CreateSystem<InitializationSystemGroup>();
world.CreateSystem<SimulationSystemGroup>();
world.CreateSystem<PresentationSystemGroup>();
ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world, PlayerLoop.GetCurrentPlayerLoop());
// 解析依赖...
var systemA = new SystemA(new ServiceA());
world.AddSystem(systemA);
var systemGroup = (ComponentSystemGroup)world.GetOrCreateSystem<SimulationSystemGroup>();
systemGroup.AddSystemToUpdateList(systemA);
// 在容器构建后...
foreach (var system in world.Systems)
{
if (system is ComponentSystemGroup group)
group.SortSystems();
}
:::note
- 目前,VContainer 使用
ScriptBehaviourUpdateOrder.UpdatePlayerLoop
注册 World。 - 这是一个注册了 3 个 SystemGroups 到 PlayerLoop 的别名,因此 VContainer 也会在内部创建这些 SystemGroups。
:::
默认情况下,VContainer 会将 System 注册到 SimulationSystemGroup
。可使用 .IntoGroup<T>()
进行变更:
// 示例
builder.RegisterSystemIntoWorld<SystemA>("My World 1")
.IntoGroup<PresentationSystemGroup>();
World 和 System 的生命周期
RegisterNewWorld(...)
或 UseNewWorld(...)
可以接受 Lifetime 作为参数。
- 新的 World 由 VContainer 管理。
- World 持有 System。因此,System 的生命周期与 World 相同。
- 如果指定了
Lifetime.Scoped
,在作用域销毁时,将调用属于该 World 的所有 System 的 Dispose 方法。
builder.RegisterNewWorld("My World 1", Lifetime.Scoped);
builder.RegisterSystemIntoWorld("My World 1");
public class SystemA : SystemBase, IDisposable
{
protected override void OnUpdate()
{
// ...
}
// 当作用域销毁时调用。
public void Dispose()
{
// ...
}
}
实体配置示例(使用自定义 World)
public class GameLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
builder.UseNewWorld("My World 1", Lifetime.Scoped, systems =>
{
systems.Add<SystemA>();
})
}
}
public class SystemA : SystemBase
{
public void SystemA(Foo foo)
{
// 注入的依赖...
}
protected override void OnCreate()
{
// 设置实体...
var archtype = World.EntityManager.CreateArchetype(typeof(ComponentDataA));
World.EntityManager.CreateEntity(archtype);
}
protected override void OnUpdate()
{
Entities.ForEach((ref ComponentDataA data) =>
{
// 逻辑处理...
})
.Schedule();
}
}
解析 World
// 当只注册了一个 World 时:
class ClassA
{
public ClassA(World world) { /* ... */ }
}
// 当注册了多个 World 时
class ClassA
{
public ClassA(IEnumerable<World> worlds)
{
var world = worlds.First(x => x.Name == "My new world");
// ...
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)