ECS概念
一个实体组件系统(ECS)架构将身份(entities)、数据(components)和行为(systems)分开。该架构的重点是数据。systems读取components的数据流,然后将数据从输入状态转化为输出状态,然后由实体进行索引。
下图说明了这三个基本部分如何一起工作:
在这个图中,System 读取 Translation 和 Rotation 组件,将其相乘,然后更新相应的 LocalToWorld 组件(L2W = T*R)。
实体A和B有一个Renderer组件,而实体C没有,这并不影响System,因为System并不关心Renderer组件。
你可以设置一个System,使其需要一个 Renderer 组件,在这种情况下,系统会忽略实体C的组件;或者,你可以设置一个系统,排除具有Renderer组件的实体,然后忽略实体A和B的组件。
Archetypes(原型)
组件类型的唯一组合称为EntityArchetype。例如,3D对象可能有一个用于其世界变换的 component,一个用于其线性运动的 component,一个用于旋转的 component,以及一个用于其视觉表示的component。这些3D对象之一的每个实例对应于一个实体,但由于它们共享同一组组件,因此ECS将它们归类为单个Archetypes(原型):
在此图中,实体A和B共享原型M,而实体C共享原型N。
要平滑更改实体的原型,可以在运行时添加或删除组件。例如,如果从Entity B中删除渲染器组件,则它将移动到原型N。
Memory Chunks(内存块)
实体的原型确定ECS存储该实体的组件的位置。ECS以“块”的形式分配内存,每个块由一个ArchetypeChunk对象表示。块始终包含单个原型的实体。当内存块装满时,ECS会为使用相同原型创建的任何新实体分配新的内存块。如果添加或删除组件(这会更改 entity 原型),ECS会将该 entity 的 components 移动到不同的块中。
此组织方案在原型和块之间提供一对多关系。这还意味着,查找具有给定组件集的所有实体只需要搜索现有的原型,这些原型的数量通常很少,而不需要搜索所有的实体,这些实体的数量通常要大得多。
ECS不按特定顺序存储块中的实体。当实体被创建或更改为新原型时,ECS会将其放入具有空间的第一个存储原型的块中。然而,块仍然被紧密地打包;当一个实体从原型中移除时,ECS将该块中最后一个实体的组件移动到组件数组中新腾出的插槽中。
NOTE
原型中共享组件的值还决定了哪些实体存储在哪个块中。对于任何共享组件,给定块中的所有实体都具有完全相同的值。如果更改共享组件中任何字段的值,则修改后的实体将移动到不同的块中,就像更改该实体的原型时一样。如有必要,将分配新的块。
当一起处理实体更有效率时,使用共享组件来分组原型内的实体。例如,Hybrid Renderer定义了它的RenderMesh组件来实现这一点。
Entity queries(实体查询)
为了确定一个系统应该处理哪些实体,请使用EntityQuery。实体查询在现有的原型中搜索那些有符合你要求的组件的原型。你可以用查询指定以下组件要求:
- All — 原型必须包含所有类别中的所有组件类型。
- Any — 原型必须至少包含任何类别中的一个组件类型。
- None — 原型必须不包含无类别中的任何组件类型。
一个实体查询提供了一个包含查询所需组件类型的块的列表。然后你可以用IJobEntityBatch直接迭代这些块中的组件。
Jobs
为了利用多线程的优势,你可以使用C# Job系统。ECS提供了SystemBase类,以及Entities.ForEach和IJobEntityBatch Schedule()和ScheduleParallel()方法,用于在主线程之外转换数据。Entities.ForEach的使用最为简单,通常需要较少的代码行来实现。你可以使用IJobChunk来处理Entities.ForEach所不能处理的更复杂的情况。
ECS按照您的systems安排的顺序在主线程上安排工作。当Jobs被安排时,ECS会跟踪哪些Jobs读取和写入哪些组件。读取一个组件的Job依赖于任何事先安排好的写到同一组件的Job,反之亦然。Jobs调度器使用Job依赖关系来决定哪些Jobs可以并行运行,哪些必须依次运行。
System organization(系统组织)
ECS按World,然后按Group来组织System。默认情况下,ECS创建了一个具有预定义组的默认世界。它找到所有可用的System,将其实例化,并将其添加到默认世界中预定义的模拟group。
你可以指定同一组内各系统的更新顺序。一个组是一种系统,所以你可以把一个组添加到另一个组,并像其他系统一样指定其顺序。一个组内的所有系统都会在下一个系统或组之前更新。如果你不指定顺序,ECS会以一种不依赖于创建顺序的确定性方式将系统插入到更新顺序中。换句话说,即使你没有明确指定一个顺序,同一组系统在其组内总是以相同的顺序更新。
System update发生在主线程上。然而,系统可以使用Jobs来卸载Job到其他线程。SystemBase提供了一个直接的方法来创建和安排Job。
关于System创建、更新顺序以及你可以用来组织System的属性的更多信息,请参见关于System 更新顺序的文档。
ECS authoring(编写)
当你在Unity编辑器中创建你的游戏或应用程序时,你可以使用GameObjects和MonoBehaviours来创建一个转换系统,将那些UnityEngine对象和组件映射到实体。了解更多信息,请参见创建Gameplay。