Unity ECS(四)ECS组成概念

接下来的内容将对ECS的三个组成部分由浅至深的进行阐述,并为接下来的ECS系统流程抛砖引玉,没有弹射起步,请以放松的姿态阅览。

通过之前的HelloWorld程序,相信大家已经对如何编写一个ECS程序有了一个大致的概念,如果仍然不够清晰,也不用太在意,只需要牢记:System根据Component去更新Entity引用的数据这一点。我再进行特别讲解。

除此之外:把编写ECS程序想象成“我是在管理成块成块的数据(Data)”,而不是“管理对象(Object)”,这样的思维方式转变是非常重要的。

 

一、Entity(实体)

在代码层面,你就可以了解到,实体非常非常的简单。它的核心部分:

public struct Entity
{
  public int Index;
  public int Version; 
}

 

Index表示这个实体的ID,和身份证一样用于实体与实体之间区分。Version用于描述这个实体的生命周期,由于实体是可以重用的,那么就需要用Version来区分这个Entity是新生实体,还是即将销毁的实体。前文说过,实体是对数据的索引,实体并不包含任何数据或者是函数逻辑,数据存储在Component中,函数逻辑则由System驱动,实体仅仅扮演一名引路人,引领你前往存储数据的地方(Component)

 

二、Component(组件)

IComponentData

IcomponentData是表示组件最基本的接口,大部分种类的组件都实现此接口。此接口非常非常之简单,它是空的,仅仅是让系统知道,继承了此接口的部分将会被识别成组件,并来用来存储数据。组件必须是结构体,而且不能包含引用类型

public struct MyComponent:IComponentData
{
   public float Value;
}

 

ISharedComponentData

ISharedComponentData表示多个实体共享的数据的接口。此接口多用于存储材质,网格数据,AI数据等通用的数据,比如大量一模一样的丧尸,但是大量模型一样,颜色不一样的丧尸却不行,因为材质数据不一样。ISharedComponentData的强大优势在于用最小的内存访问开销获得大批量的数据,同时能使Burst Complier的效率最大化,用在描述状态机的情况下非常合适。

[System.Serializable]
public struct MyRenderMesh:ISharedComponentData,IEquatable<RenderMesh>
{
   public Mesh mesh;
   public Material mat;
}

被ISharedComponentData实现的组件将会为每一个字段属性都分配一个单独的内存块(存引用信息),并在内存块外存储一个值(不会多也不会少,只有一个值)这样的话,当具有相同原型(MyRenderMesh)的实体大量出现在场景内时,内存并不会成倍增加,而是仍仅仅占用一开始分配好的内存块。此外,如果每个实体的组件的值不同,每一个不同的值都要分配一个内存块,这样的存储效果将十分差。实现的组件的值存储在块之外,块内仅存引用信息,所以它可以包含引用类型,由于要有能够识别要共享的数据部分的能力,就需要继承IEquatable<T>并实现GetHashCode覆盖。

最后ISharedComponent还必须可序列化以允许保存和还原数据。

除了这两种Component以外还有

  1. IBufferElementData

  2. ISharedComponentData

  3. ISystemStateComponentData

  4. ISystemStateBufferElementData

  5. ISystemStateSharedComponentData

几种类型的Component,但这里先不用考虑这些,先学会熟练使用并理解IComponentData与ISharedComponentData这两种最为常用的Component。

 

三、System(系统)

在UnityECS中可以通过继承ComponentSystem来定义系统(继承JobComponentSystem则是定义使用JobSystem的系统)

public class MySystem:ComponentSystem
{
   protected override void OnUpdate()
   {...}
}

 

必须实现OnUpdate()方法,在OnUpdate()里编写自己想要执行的逻辑。和Monobehaviour非常类似,OnUpdate()是每帧进行,如果想在OnUpdate()之前或者之后进行操作,可以实现OnCreate()和OnDestory()方法。

JobSystemComponent则有些区别,你必须声明一个Job,并在三种流程中对Job进行操作,例如每帧将逻辑分发给不同的核心(Job.Schedule())大致的过程是这样的:

public class MyJobSystem:JobComponentSystem
{
   struct MyJob:IJobForEach<T...>
   {
    ...
   }
   protected override JobHandle OnUpdate(JobHandle inputDeps)
   {
    ...
    var job=new MyJob().Schedule(this,inputDeps);
   }
}

除了这三个基本的组成以外,UnityECS还有一个重要的部分:世界

 

四、(一)世界(World)

World包含了所有的Entity,Component,System 是一个ECS的顶层管理者。严格意义上来说一个ECS程序应当只有一个World,但是存在多个World也是可以的,你可以模拟多个不同的World,或者多个运行规律相同的World,但是不同的World是完全独立的,不能互相干涉,也没有办法将实体或是组件发送到另一个世界。

默认情况下,程序会自动生成一个包含所有已定义System的世界--->World.Active并会被设为初始World,你可以通过接口:ICustomBootstrap或者宏定义:UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP、UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD、UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_EDITOR_WORLD来禁用这个流程,但不推荐你这么做。

 

四、(二)实体管理者(EntityManager)

World中可以获得重要的类:EntityManager。World包含了所有的Entity,Component,System。但是World仅仅直接控制System,Entity与Component则由EntityManager进行管理。你可以注意到,在前文中,Entity、Component的产生都是由EntityManager进行的:

你可以手动调用World生成System,但是不能生成Entity或是Component,同样World也不能访问Entity或是Component。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值