Unity DOTS中的baking(一) Baker简介


baking是DOTS ECS工作流的一环,大概的意思就是将原先Editor下的GameObject数据,全部转换为Entity数据的过程。baking是一个不可逆的过程,原先的GameObject在运行时不复存在,都会变成Entity。


baking只会在Editor环境下运行,而且只会对SubScene中包含的GameObject进行烘焙。SubScene是Unity提供的一种场景格式,SubScene中的GameObjects和MonoBehaviour会转换成ECS中的entity和component。之所以搞出一个新的格式,是因为ECS和之前老的scene system不兼容。

baking主要分为两个关键步骤,其一是Bakers,负责将GameObjects转换为entities和components,其二是Baking systems,对上一步中生成的entities可以再做额外处理。

通常,一个Baker和一个MonoBehaviour绑定,Baker可以使用MonoBehviour中的数据,为Entity添加组件,例如:

using Unity.Entities;
using UnityEngine;

public class MyAuthoring : MonoBehaviour
{
    public int bakeIntData = 0;

    class MyBaker : Baker<MyAuthoring>
    {
        public override void Bake(MyAuthoring authoring)
        {
            var entity = GetEntity(TransformUsageFlags.None);
            AddComponent(entity, new IntComponent { value = authoring.bakeIntData });
        }
    }
}

public struct IntComponent : IComponentData
{
    public int value;
}

代码很简单,就是为当前的Entity添加了一个IntComponent的Component,这个IntComponent的value是用MyAuthoring里的bakeIntData设置的。我们在Editor下也可以预览到GameObject转换为Entity时所拥有的Components:

unity double精度问题 unity的dots_Dynamic

运行时还能看到更详细的信息:

unity double精度问题 unity的dots_unity double精度问题_02

我们在代码中获取当前Enity时,使用了TransformUsageFlags.None这个参数。根据Unity给出的官方文档,这个flag是用来告诉Unity如何将GameObject中的transform转换成ECS的Components的。如果flag为None,说明Entity不需要transform相关的Components,而如果flag为dynamic,则会尽可能把transform的信息复制到Entity里。完整的flags列表如下,摘自官方文档:

Name

Description

Dynamic

Indicates that an entity requires the necessary transform components to be moved at runtime (LocalTransform, LocalToWorld).

ManualOverride

Indicates that you want to take full manual control over the transform conversion of an entity.

NonUniformScale

Indicates that an entity requires transform components to represent non uniform scale.

None

Specifies that the entity doesn’t need transform components.

Renderable

Indicates that an entity requires the necessary transform components to be rendered (LocalToWorld), but it doesn’t require the transform components needed to move the entity at runtime.

WorldSpace

Indicates that an entity needs to be in world space, even if they have a Dynamic entity as a parent.

我们可以对比一下使用TransformUsageFlags.NoneTransformUsageFlags.Dynamic的区别:

unity double精度问题 unity的dots_sed_03

对比一下可以发现,右边Dynamic的多了LocalToWorldLocalTransform两个Component,它们包含了transform的各种信息。

那么,Baker触发的时机是怎样的呢?

首先,对于SubScene,它在Hierarchy中有两种状态,一种是open,一种是closed,在Editor环境下,SubScene的右侧有个勾选,如果没有勾选,就是closed,反之则是open。

根据官方文档,closed状态下,Unity会执行full baking,这是一个异步的过程。

unity double精度问题 unity的dots_sed_04

官方文档给出的触发时机有以下若干种:

  • The entity scene is missing (not present on disk).
  • The authoring scene has been modified and the entity scene is outdated.
  • The baking code is in an assembly that doesn’t contain a single [BakingVersion] attribute. This means that the assembly has been modified and the entity scene is outdated.
  • A [BakingVersion] attribute on baking code has been modified.
  • The Entities Project Settings have been modified.
  • You request a reimport from the subscene Inspector. This feature is intended for troubleshooting.
  • You clear the baking cache in the Editor Preferences.

我们不妨挑几个试验一下。首先在之前的代码里加下触发的log:

public override void Bake(MyAuthoring authoring)
{
    Debug.Log("==========================Bake Invoked!========================== " + authoring.name);
    var entity = GetEntity(TransformUsageFlags.None);
    AddComponent(entity, new IntComponent { value = authoring.bakeIntData });
}

然后在SubScene里放两个空的GameObject,分别叫GameObject1和GameObject2,给它们都挂上MyAuthoring这个脚本:

unity double精度问题 unity的dots_unity_05

这时,进入挂有SubScene的场景时,就会触发Bake了。说明只有SubScene需要被load时,才会触发它的baking。

unity double精度问题 unity的dots_sed_06

下面,我们回到SubScene,只修改某一个GameObject的transform或者MyAuthoring的BakeIntData:

unity double精度问题 unity的dots_Dynamic_07

保存之后再次回到场景,会发现没有修改过的GameObject2也重新进行了baking,也的确是full baking。但是,如果我们把GameObject1修改回之前,却发现此时并不会触发full baking,原来Unity对baking的结果是会进行缓存的,如果缓存中存在就不会再次baking了。

不过,我们也可以手动触发full baking,比如直接reimport SubScene:

unity double精度问题 unity的dots_Dynamic_08

或者在Preference中clear掉缓存:

unity double精度问题 unity的dots_sed_09

现在,我们再勾上SubScene的open,open subscene的好处是可以实时预览SubScene中的GameObjects和Entities:

unity double精度问题 unity的dots_游戏引擎_10

除此之外,open subscene会执行incremental baking,也就是只bake发生修改过的data,不过这要取决于需要处理的数据量。

比如此时如果只修改GameObject1的transform,MyAuthoring的Bake是不会被触发的,因为它本身并没有发生改变。incremental baking的log也提供了相关的信息:

unity double精度问题 unity的dots_游戏引擎_11

如果我们再修改MyAuthoring的BakeIntData,此时就会触发Bake了,不过因为只有GameObject1发生了修改,所以也只有GameObject1需要重新进行Bake:

unity double精度问题 unity的dots_游戏引擎_12

Reference

[1] Baking overview

[2] Enum TransformUsageFlags