通过Component System Groups来指定systems的执行顺序。可以通过在系统的类的定义上使用[UpdateInGroup] Attribute来指定该系统在哪个group中执行。还可以使用[UpdateBefore] [UpdateAfter]来指定在该Group内的执行顺序。
ECS框架创建了一组默认的system groups,可以利用这些groups来控制我们的系统的执行顺序。还可以将一个Group创建到另一个Group内,这样来进一步控制系统的执行顺序(Group是可以嵌套的)。
Component System Groups
ComponentSystemGroup类组织若干相关的Systems到一起,并以一定的顺序执行。ComponentSystemGroup是ComponentSystemBase的派生类,因此它具有一个componenty system应该有的特性,可以跟其它系统关联,有OnUpdate方法,等。最主要的,这意味着Component System Croups可以嵌套到其它Component System Group中,建立层次关系。
默认地,当ComponentSystemGroup的Update调用时,它会调用所有保存的系统列表里的系统的Update()。如果一个子Component System是group时,会递归调用它的子系统列表,按照深度优先遍历整棵层次树。
System Ordering Attributes
目前系统维护了一套顺序Attributes:
- [UpdateInGroup] — 为该系统指定是哪个group的成员。如果没有该修饰,默认会添加到World的SimulationSystemGroup中。
-
[UpdateBefore] [UpdateAfter] — 控制组内系统的相对执行顺序。使用这些修饰的系统类型,必须在同一个group中。对于跨group的顺序,按照包含2个systems的最深的group决定:
例如:SystemA在GroupA,SystemB在GroupB,GroupA和GroupB都在GroupC,那么SystemA和SystemB的隐含顺序为Group A和GroupB的顺序。
- [DisableAutoCreation] — 禁止默认 World初始化时自动创建System。之后需要自己明确的创建和更新这些系统。当然,也可以创建完系统后,添加到一个ComponentSystemGroup中,那么系统的Update会被Group自动调用。
Default System Groups
默认的World包含了一个ComponentSystemGroup实例的层级树。仅三个根级system groups被添加到Unity的逻辑循环中。下面的列表展示了这三个组中预定义的子系统:
- InitializationSystemGroup(在主循环的Initialization阶段结束时执行)
- BeginInitializationEntityCommandBufferSystem
- CopyInitialTransformFromGameObjectSystem
- SubSceneLiveLinkSystem
- SubSceneStreamingSystem
- EndInitializationEntityCommandBufferSystem
- SimulationSystemGroup(主循环的Update结束时执行)
- BeginSimulationEntityCommandBufferSystem
- TransformSystemGroup
- EndFrameParentSystem
- CopyTransformFromGameObjectSystem
- EndFrameTRSToLocalToWorldSystem
- EndFrameTRSToLocalToParentSystem
- EndFrameLocalToParentSystem
- CopyTransformToGameObjectSystem
- LateSimulationSystemGroup
- EndSimulationEntityCommandBufferSystem
- PresentationSystemGroup(主循环的PreLateUpdate结束时调用)
- BeginPresentationEntityCommandBufferSystem
- CreateMissingRenderBoundsFromMeshRenderer RenderingSystemBootstrap
- RenderBoundsUpdateSystem RenderMeshSystem LODGroupSystemV1
- LodRequirementsUpdateSystem
- EndPresentationEntityCommandBufferSystem
这个列表的内容目前还在优化中,会发生变化。
Multiple Worlds
可以创建除了默认World的多个其它Worlds,或者替换默认World。同样的Component System可以创建到多个World中,并且可以以不同的更新频率,在不同的时间点更新。
现在,不支持手动地更新World内的系统;你只能控制某个系统被创建到哪个World中的哪个System Group中。也即是,自定义的WorldB可以实例化SystemX和SystemY,并将SystemX添加到默认World的SimulationSystemGroup,将SystemY添加到默认World的PresentationSystemGroup中。这些Systems的顺序关系在它们所在的Group中被管理,并按照Group的顺序更新。
为了实现这种用法,提供了ICustomBootstrap 接口类:
public interface ICustomBootstrap
{
List<Type> Initialize(List<Type> systems);
}
实现该接口时,一个完整的System类型的列表被传递到Initialize()方法中,而且它在默认World初始化前调用。自定义的启动加载器可以遍历这个列表并且创建需要的System。可以返回一个System列表,该列表内的系统将被默认World初始化。(不返回则不创建)
例如,下面是个典型的MyCustomBootstrap.Initialize()的实现过程:
- 创建一个World和它的最上层的ComponentSystemGroups。
-
对所有的System类型:
遍历ComponentSystemGroup层次,找到该System应该被添加到哪个Group中。
如果找到Group,在该World中创建System并调用group.AddSystemToUpdateList()接口添加到层次结构中。
如果没有找到要添加到的Group,那么,添加到返回的System类型列表中,返回到DefaultWorldInitialization。
-
调用所有顶层的groups的group.SortSystemUpdateList()。
Optionally add them to one of the default world groups
- 返回未处理的系统的列表给DefaultWorldInitializtion。
注:ECS框架通过反射用户自定义的ICustomBootstrap实现。
建议:
- 用[UpdateInGroup]来指定你的系统在哪个Group中执行。如果不指定,默认是SimulationSystemGroup。
- 用手动更新的ComponentSystemGroups来在Unity主循环的其它阶段更新Systems。为ComponentSystem或者ComponentSystemGroup添加[DisableAutoCreation]修饰来避免被自动添加到默认的SystemGroups中,可以手动地通过接口World.GetOrCreateSystem()来创建System并在主线程中调用MySystem.Update()来执行。这是将System添加到主循环其它阶段去执行的简单的办法。(例如,你可以创建一个系统,在一帧的前面或者后面执行。)
- 尽可能的使用已有的EntityCommandBufferSystem,而不要自己创建。一个EntityCommandBufferSystem代表一个SyncPoint,主线程要等待所有工作线程的工作完成,以执行它的缓存的EntityCommandBuffers。重复利用预定义在顶层SystemGroup中的Begin/End Systems来避免SyncPoints,提升效率。
- 避免将自定义逻辑写到ComponentSystemGroup.OnUpdate()中。因为ComponentSystemGroup本身是个ComponentSystem,让人想要将用户逻辑添加到它的OnUpdate()中来执行一些逻辑,创建Jobs,等。我们反对这么做,因为从表面上很难马上确定,它的Execute是在group‘s的其它成员System之前还是之后。最好就是让SystemGroups来执行grouping机制,而在其它的System中实现我们的逻辑,并明确地指定它们的相对执行顺序。