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