Unity DOTS Baking System与Baking World

发布于:2024-10-13 ⋅ 阅读:(51) ⋅ 点赞:(0)

最近DOTS终于发布了正式的版本, 我们来分享一下DOTS里面Baking阶段,Baking System,Baking World的关键概念,方便大家上手学习掌握Unity DOTS开发。Unity在Baking也是基于ECS模式开发设计的,所以Baking的时候也会有Baking System与Baking World,把Baking出来的数据放到Baking World里面。

对惹,这里有一个游戏开发交流小组,大家可以点击进来一起交流一下开发经验呀!

Baking 的主要阶段

Baking有很多阶段,主要包含了两个关键的步骤:

  • Bakers: 在这个阶段所有 Baker 会被执行,就把authoring GameObject与Component 转换成ECS 的entities与components。
  • Baking System: 这个阶段,可以编写Baking System代码,统一批量对所有创建与生成好的Entities来做一些处理操作;

在Baker阶段执行之前,Unity会把subscene中所有的Authoring GameObject创建出对应的entity,这个阶段entity不包含任何组件数据,只包含一些metadata的描述数据。创建完Entity以后,就会执行Bakers 每个Baker处理对应它的authoring component的数据转化。每种类型的数据对应一种Baker,例如Entities Graphics就会有Baker将GameObject的渲染数据转换成renderers的数据。Unity Physics有Baker 把刚体RigidBody组件转成ecs数据。相同的类型只要一种Baker就即可。

Unity在运行Baker转换的时候,无法保证执行Baker的顺序, Baker不存在依赖关系。因此Baker不能去读取与改变entity中的组件数据,只能创建新的组件到entity。

当Baker阶段运行结束以后,所有的entity与组件就创建完成了,接下来就执行Baking System阶段。Baking System只会运行在Baking期间。你可以通过[UpdateAfter], [UpdateBefore] [UpdateInGroup]指定Baking System的运行时机。Unity有几个默认Baking System执行的时机分组。

  • PreBakingSystemGroup: 这个System分组执行发生在所有Bakers运行之前;
  • TransformBakingSystemGroup: 是一个用于处理实体的变换信息的系统组。它负责将实体的Transform数据进行烘焙,以提高性能和优化渲染。
  • BakingSystemGroup: 默认的Baking System都被放在了这个分组进行迭代处理;
  • PostBakingSytemGroup: 所有的放在默认分组中的Baking System全部迭代完成后,再来迭代这个分组里面的System;

实时Baking时,Unity会运行所有的baking system分组,它会把entity数据存入到entity scene,并序列化到磁盘。并把烘培出来后变化的数据同步到main ECS World里面。

Baking World

Unity 在Baker 每个entity scene的时候都是独立的。每次只处理一个场景。同时每个独立的场景都有一个独立的main world。同时在Baker每个场景的时候,我们的Unity会额外分配两个世界:

  • Conversion world: 定义了一个Baking发生的Word, 在Baking的时候,所有entity等数据都会被加入到这个World,在这个World里面做Baking。
  • Shadow world: 影子世界,存放的是上一次Baking完后的结果,方便与Conversion对比,来判断哪些在这次Baking时更新了。

Unity 会在 Conversion world 里面来运行Baker与Baking Sytems来做场景物体的Baking。当Baking结束以后,Unity Baking比较Shadow World与Conversion World的变化,看哪些改变了,Unity只把本次改变同步给main World。

Baking System

Baking System 是一种机制,被用于批量处理ecs components与entities的数据。一个Baking System在Unity DOTS内部也是一个System,它基于多线程与Burst 编译器,能够很好做批量的处理。Baking System与Baker机制不同的点在于Baker是将authoring data一个一个的处理转换,然后Baking System用户成批的处理已经转换好的ecs components与entities。所以Baking这个过程在执行Baking System 之前,必须要把要先执行让Baker把所有的entity 都初始化创建出来以后,才能只能执行Baking System, 批量处理。在Baking System中你可以改变当前World里面的所有内容,包括创建一个新的entities。这里要注意在Baking System里面创建一个entity,在bake 场景结束后不会被销毁。你可以在Baking System中创建一个entity,并传递给其它Baking System。如果你想要这个Entity在bake entity场景结束后销毁,你就必须要Baker里面来创建一个entity。当在Baking与实时Baking时,可以在Baker里面来调用CreateAdditionalEntity来创建entity,配合处理。

创建一个Baking System,我们需要给Baking System加上[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]的注解。加了这个注解,我们系统Baker处理就会把它识别出来,并把它加入到Baking World里面来进行迭代。Unity每次Baking,都会迭代World里面的所有Baking System。接下来我们看一个Baking System 添加一个tag component到包含的某A组件的entity中的示例代码:

public struct AnotherTag : IComponentData { }
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
partial struct AddTagToRotationBakingSystem : ISystem
{
  public void OnUpdate(ref SystemState state)
  {
    var queryMissingTag = SystemAPI.QueryBuilder()
      .WithAll<RotationSpeed>()
      .WithNone<AnotherTag>()
      .Build();

    state.EntityManager.AddComponent<AnotherTag>(queryMissingTag);

    // Omitting the second part of this function would lead to inconsistent
    // results during live baking. Added tags would remain on the entity even
    // after removing the RotationSpeed component.

    var queryCleanupTag = SystemAPI.QueryBuilder()
      .WithAll<AnotherTag>()
      .WithNone<RotationSpeed>()
      .Build();

    state.EntityManager.RemoveComponent<AnotherTag>(queryCleanupTag);
  }
}

定义了一个Baking System, 每次迭代update的时候,找出World中所有包含RotationSpeed组件并且不含AnotherTag组件的Entity的集合,给它们加上AnotherTag组件,找出所有不包含RotationSpeed且包含AnotherTag的Entity集合,把它们的AnotherTag删除掉。