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:
运行时还能看到更详细的信息:
我们在代码中获取当前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.None
和TransformUsageFlags.Dynamic
的区别:
对比一下可以发现,右边Dynamic的多了LocalToWorld
和LocalTransform
两个Component,它们包含了transform的各种信息。
那么,Baker触发的时机是怎样的呢?
首先,对于SubScene,它在Hierarchy中有两种状态,一种是open,一种是closed,在Editor环境下,SubScene的右侧有个勾选,如果没有勾选,就是closed,反之则是open。
根据官方文档,closed状态下,Unity会执行full baking,这是一个异步的过程。
官方文档给出的触发时机有以下若干种:
- 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这个脚本:
这时,进入挂有SubScene的场景时,就会触发Bake了。说明只有SubScene需要被load时,才会触发它的baking。
下面,我们回到SubScene,只修改某一个GameObject的transform或者MyAuthoring的BakeIntData:
保存之后再次回到场景,会发现没有修改过的GameObject2也重新进行了baking,也的确是full baking。但是,如果我们把GameObject1修改回之前,却发现此时并不会触发full baking,原来Unity对baking的结果是会进行缓存的,如果缓存中存在就不会再次baking了。
不过,我们也可以手动触发full baking,比如直接reimport SubScene:
或者在Preference中clear掉缓存:
现在,我们再勾上SubScene的open,open subscene的好处是可以实时预览SubScene中的GameObjects和Entities:
除此之外,open subscene会执行incremental baking,也就是只bake发生修改过的data,不过这要取决于需要处理的数据量。
比如此时如果只修改GameObject1的transform,MyAuthoring的Bake是不会被触发的,因为它本身并没有发生改变。incremental baking的log也提供了相关的信息:
如果我们再修改MyAuthoring的BakeIntData,此时就会触发Bake了,不过因为只有GameObject1发生了修改,所以也只有GameObject1需要重新进行Bake:
Reference
[1] Baking overview
[2] Enum TransformUsageFlags