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");
        // ...
    }
}
posted @   凌雪寒  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示