System Update Order(系统更新顺序)
使用组件系统组来指定你的系统的更新顺序。你可以在系统的类声明中使用[UpdateInGroup]属性将一个系统放入一个组中。然后你可以使用[UpdateBefore]和[UpdateAfter]属性来指定组内的更新顺序。
ECS框架创建了一套默认的系统组,你可以用它来在一个框架的正确阶段更新你的系统。你可以将一个组嵌套在另一个组内,这样你组内的所有系统都会在正确的阶段进行更新,然后,也会根据组内的顺序进行更新。
Component System Groups(组件系统组)
ComponentSystemGroup类代表了一个相关组件系统的列表,这些系统应按特定顺序一起更新。ComponentSystemGroup派生于ComponentSystemBase,因此它在所有重要方面都像一个组件系统 – 它可以相对于其他系统排序,有一个OnUpdate()方法,等等。最重要的是,这意味着组件系统组可以嵌套在其他组件系统组中,形成一个层次结构。
默认情况下,当调用ComponentSystemGroup的Update()方法时,它会对其成员系统排序列表中的每个系统调用Update()。如果任何成员系统本身是系统组,它们将递归地更新自己的成员。由此产生的系统排序是按照树的深度优先遍历进行的。
System Ordering Attributes(系统排序特性)
现有的系统排序属性被保留,但语义和限制略有不同。
-
[UpdateInGroup] - 指定该系统应成为的ComponentSystemGroup的成员。如果省略此属性,该系统将被自动添加到默认的World的SimulationSystemGroup(见下文)。
-
[UpdateBefore]和[UpdateAfter]–将系统相对于其他系统排序。为这些属性指定的系统类型必须是同一个组的成员。跨组边界的排序是在包含这两个系统的适当的最深组中处理的。
- 例如:如果SystemA在GroupA中,SystemB在GroupB中,而GroupA和GroupB都是GroupC的成员,那么GroupA和GroupB的排序隐含地决定了SystemA和SystemB的相对排序;没有必要对系统进行明确的排序。
-
[DisableAutoCreation] - 防止系统在默认世界初始化过程中被创建。您必须明确地创建和更新该系统。不过,您可以将带有此标签的系统添加到ComponentSystemGroup的更新列表中,然后它就会像该列表中的其他系统一样被自动更新。
Default System Groups(默认的系统组)
默认的世界包含一个由ComponentSystemGroup实例组成的层次结构。只有三个根级系统组被添加到Unity播放器循环中(下面的列表还显示了每个组中预先定义的成员系统)。
-
InitializationSystemGroup(在播放器循环的初始化阶段结束时更新)
- BeginInitializationEntityCommandBufferSystem
- CopyInitialTransformFromGameObjectSystem
- SubSceneLiveLinkSystem
- SubSceneStreamingSystem
- EndInitializationEntityCommandBufferSystem
-
SimulationSystemGroup(在播放器循环的更新阶段结束时更新)
- BeginSimulationEntityCommandBufferSystem
- TransformSystemGroup
- EndFrameParentSystem
- CopyTransformFromGameObjectSystem
- EndFrameTRSToLocalToWorldSystem
- EndFrameTRSToLocalToParentSystem
- EndFrameLocalToParentSystem
- CopyTransformToGameObjectSystem
- LateSimulationSystemGroup
- EndSimulationEntityCommandBufferSystem
-
PresentationSystemGroup(在播放器循环的PreLateUpdate阶段结束时更新)。
- BeginPresentationEntityCommandBufferSystem
- CreateMissingRenderBoundsFromMeshRenderer
- RenderingSystemBootstrap
- RenderBoundsUpdateSystem
- RenderMeshSystem
- LODGroupSystemV1
- LodRequirementsUpdateSystem
- EndPresentationEntityCommandBufferSystem
请注意,这份清单的具体内容可能会有变化。
Multiple Worlds
你可以创建多个世界,除了(或代替)上述的默认世界之外。同一个组件系统类可以在多个世界中实例化,每个实例可以从更新顺序的不同点以不同的速度更新。
目前还没有办法手动更新特定世界中的每个系统;相反,您可以控制在哪个世界中创建哪些系统,以及它们应该被添加到哪个现有的系统组中。因此,自定义的WorldB可以实例化SystemX和SystemY,将SystemX添加到默认世界的SimulationSystemGroup,将SystemY添加到默认世界的PresentationSystemGroup。这些系统可以像往常一样相对于其组的兄弟姐妹排序,并将与相应的组一起被更新。
为了支持这种使用情况,现在有了一个新的ICustomBootstrap接口:
public interface ICustomBootstrap
{
// 返回应该由默认引导程序处理的系统。
// 如果返回null,则根本不会创建默认世界。
// 空列表创建默认的世界和入口
List<Type> Initialize(List<Type> systems);
}
当你实现这个接口时,在默认的世界初始化之前,组件系统类型的完整列表将被传递给类的Initialize()方法。自定义引导器可以遍历这个列表,在它想要的任何世界中创建系统。你可以从Initialize()方法中返回一个系统列表,它们将作为正常的、默认的世界初始化的一部分被创建。
例如,下面是一个自定义MyCustomBootstrap.Initialize()实现的典型过程:
- 创建任何额外的世界和它们的顶级组件系统组。
- 对于系统类型列表中的每个类型:
- 通过ComponentSystemGroup的层次结构向上追溯,找到此系统类型的顶层组。
- 如果是步骤1中创建的组之一,则在该世界中创建系统,并通过group.AddSystemToUpdateList()将其添加到层次结构中。
- 如果不是,就把这个类型追加到列表中,返回到DefaultWorldInitialization。
- 对新的顶层组调用group.SortSystemUpdateList()。
- 可选择将其添加到一个默认的世界组中
- 返回未处理的系统列表给DefaultWorldInitialization
NOTE
ECS框架通过反射找到你的ICustomBootstrap实现。
提示和最佳做法
-
使用[UpdateInGroup]为您编写的每个系统指定系统组。如果不指定,隐含的默认组是SimulationSystemGroup。
-
使用手动选中的ComponentSystemGroups来更新Unity播放器循环中其他地方的系统。为组件系统(或系统组)添加[DisableAutoCreation]属性可以防止其被创建或添加到默认系统组中。你仍然可以用World.GetOrCreateSystem()手动创建系统,并通过从主线程手动调用MySystem.Update()来更新它。这是一种简单的方法,可以在Unity播放器循环中的其他地方插入系统(例如,如果你有一个系统应该在帧的后期或早期运行)。
-
如果可能的话,使用现有的EntityCommandBufferSystems而不是添加新的。EntityCommandBufferSystem代表一个同步点,主线程在处理任何未完成的EntityCommandBuffers之前等待工作线程完成。在每个根级系统组中重复使用一个预定义的开始/结束系统比创建一个新的系统更不可能在框架管道中引入一个新的 “气泡”。
-
避免将自定义逻辑放在ComponentSystemGroup.OnUpdate()中。由于ComponentSystemGroup在功能上本身就是一个组件系统,因此在其OnUpdate()方法中添加自定义处理,以执行一些工作、生成一些作业等,可能会很诱人。我们建议一般不要这样做,因为从外面看并不清楚自定义逻辑是在组的成员被更新之前还是之后执行的。最好是将系统组限制在一个分组机制中,并在一个单独的组件系统中实现所需的逻辑,明确地相对于组的顺序。