Unity Enitity Component System 参考手册 - 2.1 ECS concepts

基于0.11.1-preview 4 版本

https://docs.unity3d.com/Packages/com.unity.entities@0.11/manual/ecs_systems.html

2.1 ECS concepts

ECS架构将身份(entity),数据(component)和行为(system)分开。该架构可以专注于数据。首先索引到某一个entity,然后system读取绑定到该entity上的component的数据流,再将数据从输入状态转换为输出状态。下图说明了这三个基本部分如何协同工作:

在此图中,system读取Translation component和Rotation component,将这两个矩阵相乘,然后对应更新到LocalToWorld component去:( $ L2W = T \times R $ )entity A和entity B具有Renderer component,而entity C则不影响system,因为system并不关心Renderer component。

您可以设置一个system,使其需要一个Renderer component,在这种情况下,system将忽略 entity C的。或者,也可以设置system不处理具有Renderer component的entity,这时候该system将不查询忽entity A和B,所以也会在它之内忽略了这A和B其他component。

2.1.1 Archetypes

若干个component的 唯一组合 称为“ Archetypes”。例如,一个三维物体可能具有一个用于变换到世界坐标下的component;一个用于进行线性运动的component;一个用于旋转的component;以及一个用于其视觉表示的component。这些三维物体的每个实例(instance)都对应一个entity,但因为这些实例都共享相同的component集合,因此ECS将它们归类为单个archetype,如图所示:

在此图中,entity A和B共享ArchetypesM,而entity C具有ArchetypesN。为了顺利更改entity的Archetypes,可以在运行时添加或删除component。例如,如果从entity B中删除Renderer component,那么它将移至Archetypes N。

2.1.2 Memory Chunks

entity的Archetypes决定ECS在何处存储该entity的component。ECS以 chunk 分配内存,每个chunk均由 一个 ArchetypeChunk对象表示。chunk始终包含单个Archetypes的entity。当内存chunk已满时,ECS会为使用相同Archetypes创建的任何新entity,分配新的内存chunk。如果添加或删除component,然后更改了entityArchetypes,则ECS会将该entity的component移动到其他chunk中。

这种组织方案提供了Archetypes和chunk之间的一对多关系。这也意味着,要找到具有给定component集的所有entity,仅需要搜索较少的现有Archetypes即可,不需要通过遍历所有entity来得到。

ECS不会按特定顺序去存储entity。在创建entity或将其更改为新的Archetypes时,ECS会将其放入存储着该Archetypes且还有剩余空间的第一个chunk中。但chunk仍然是按照内存紧凑的方式存储着。当从Archetypes中删除entity时,ECS会将chunk中最后一个entity的component,移动到component阵列(component arrays)新腾出的空位中。

注意:Archetypes中的共享component的值,还决定了哪些entity存储在哪个chunk中。给定chunk中的所有entity,对于任何共享component都具有完全相同的值。如果更改共享component中任何字段的值,则修改后的entity将移至其他chunk,就像更改该entity的Archetypes时一样。如有必要,将分配一个新chunk。

当通过使用 共享component ,将一系列的entity,分组到一个对应的Archetypes里,将会更加高效。例如,Hybrid renderer定义的RenderMeshcomponent就是一种共享component。

2.1.3 Entity queries

使用EntityQuery方法可以确定system应处理哪些entity。EntityQuery方法在现有Archetypes中,搜索具有与指定需求匹配的component的Archetypes。可以通过 以下的查询标识 指定要求:

  • All — Archetypes必须包含指定类别中的 所有 component类型.
  • Any — Archetypes必须包含指定类别中的 至少一种 component类型.
  • None — Archetypes必须 不包含 指定类别中的 任何 component类型

entity查询操作提供了满足查询条件的chunk的列表。然后可以直接使用IJobChunk接口遍历这些chunk中的component。

2.1.4 Jobs

要利用多线程,可以使用[C#Job system]机制。ECS提供SystemBase类的Entities属性的ForEach方法,以及IJobChunk接口的Schedule()ScheduleParallel()方法,在 主线程之外 转换数据。Entities.ForEach是最简单的使用方法,通常只需较少的代码行即可实现。也可以将IJobChunk接口用于单单使用Entities.ForEach无法处理的更复杂的情况。

ECS按照system的所指定的顺序,在主线程上进行作业(job)调度。在job被调度后,ECS会跟踪有哪些job读取和写入了哪些component。针对某component的读取操作job的执行时序,依赖于被预先安排的,且先于本读取job执行的,同样面向该component的写入操作job;反之亦然。 作业调度程序(job scheduler) 使用 作业依赖性(job dependencies) 来确定有哪些job是可以并行运行的,哪些job必须按顺序次第运行。

2.1.5 System organization

ECS通过“World”来管理和分组system。默认情况下,ECS将使用一个预定义的分组来创建默认的world。它找到所有可用的system,实例化它们,并将它们添加到默认世界中的 预定义模拟分组(predefined simulation group) 中。

可以指定同一组中system的更新顺序 。分组是一种system,因此可以像其他system一样,将分组添加到另一个分组中,并指定其顺序。某一分组中的所有system依次得到更新之后,ECS才在对下一个不在本分组的system,或另一个分组,进行更新操作。如果未指定顺序,则ECS会以不依赖于创建顺序的方式,将system插入更新顺序。换句话说,即使未明确指定顺序,同一分组的system,也始终以其分组内的相同顺序进行更新。

system的更新在主线程上进行。但是,system可以使用作业将工作分流到其他线程。SystemBase类提供了创建和调度作业的直接方法。有关system创建,更新顺序以及可用于组织system的属性的更多信息,请参阅《system更新顺序》中的文档。

2.1.6 ECS authoring

在Unity编辑器中创建游戏或应用程序时,可以使用GameObjectsMonoBehaviours创建一个转换system(conversion system),以将这些UnityEngine.GameObjectUnityEngine.Component映射到entity。有关更多信息,请参见创建游戏玩法