Baking
概念:
- 把Editor中的GameObject数据转换成entities数据写入到Entity Scens里面,这个过程我们叫做Baking
- Baking是一种不可逆的操作,把低效代价昂贵的但是灵活的GameObjects转换成性能高效的Entities与Components。
作用:
- 在传统的模式下,开发者使用的GameObject机制进行创作,然后运行也是基于它的. 通过baking,能让用户在ecs模式下,创作的时候使用还是使用GameObject机制来创作,增加易用性。
- Unity通过Baking的方式,把GameObject数据Authoring Data(创作数据)在编译时就转换为Runtime Data(运行数据),这样在运行的时候就可以直接使用运行数据
处理过程
-
在ECS模式下,传统的GameObject,称为Authoring GameObject, Authoring GameObject上的Component 称为: Authoring Component
-
Authoring Scenes:传统的GameObject所在场景叫做authoring scene,用来将传统GameObject数据转换成ECS模式下的数据所在的场景(场所);
-
Baking 转换发生的过程只会在编辑器的模式下,在运行前先转好ecs数据,然后运行的时候直接使用ecs数据; 无论什么时候,只要我们Authoring Scene中的Authoring GameObject被改变了,就会触发我们Baking; Authoring Scene必须要必须要作为subscene加载进来时,创作Scene才会执行baking;
- 会baking:
- 不会baking
- 会baking:
baking的两种模式:
-
Full baking(全baking): Unity整个Authoring Scene 全部bake成entities scene;
- 触发条件:
1:在我们的disk上,找不到转换好的entities scene的数据;
2:我们的Authoring Scene被修改了,同时entity scene,已经过期了;
3:当修改了Baker代码的时候,BakingVersion,注解的时候,会引发full baking;
4: A[BakingVersion] attribute注解的Baking代码被修改了,会引发full baking.
5: Project Setting设置里面与Entities相关的设置被修改了,会导致引发fullbaking;
6:如果你在subscene节点的属性检查器里面点击了reimport按钮,那么这个时候会导致full baking;
7:如果你清理了Baking Cache,也会导致我们的full baking; - Full Baking会把我们的输出数据存放到我们的磁盘disk上(一些文件文件),你编辑器或运行的时候需要使用,就去加载
- 触发条件:
-
lncremental baking(增量baking):只bake改变的数据;
- 当作为subscene来加载创作场景的同时我们也创建与初始化了Incremental baking;
- 在创作编辑的时候,可以在ECS的数据里面直接体现出实时编辑与修改的结果;
- 增量Baking只会发生在我们的内存里面,不会同步到我们的disk;
- 当编辑场景数据的时候,会把改变的baking到内存里面,所以ECS就可以直接使用编辑好的数据。同时只baking改变的数据,所以速度非常快;
-
注意: lncremental Baking 和Full baking输出是不同的,就会导致entity的顺序不同,内存大小不同,chunk的排布可能也不同。所以为了避免一些可能的未知错误,需要使用Reimport来强制进行full baking
Baker运作机制
- 一个Baker就是Baking 的其中的一个执行者,负责把某个Authoring Data转成ecs data;
- 要定义一个Baker,首先要定一个托管类:继承Baker,T指的是要转换的Authoring Component的类型;
- 当Baking的时候只要遇到这种类型的Authoring Component,就会找到这个Baker,然后调用这个Baker方法,并把要转的组件数据对象作为参数,输入进来;
- 代码示例:
// IComponentData是一个空接口,只是用来标记这个类是一个ComponentData
// 这是unmanaged 类型,不会被GC管理, 内部只能使用unmanaged的数据类型
struct ComponentData : IComponentData
{public float rotSpeed;
}public class ComponentAuthoring : MonoBehaviour
{public float rotateSpeed = 90f;
}// 自定义一个Baker,方便我们场景在Bake的时候,来转这个组件数据
public class MyComponentBaker : Baker<ComponentAuthoring>
{// subscene bake时调用的方法 编译时就调用public override void Bake(ComponentAuthoring authoring){// 获取到对应的Entityvar entity = GetEntity(TransformUsageFlags.Dynamic);// 这里只是数据传递,并不是Entity中通过Archetype分配出来的内存var data = new ComponentData(){// authoring是我们在场景中设置的值// ECS 中使用的数学库是Unity.Mathematics!rotSpeed = math.radians(authoring.rotateSpeed)};// 将数据添加到Entity中AddComponent(entity, data);}
}
- 对于其他Unity自带的Component(例如 Collider这些),Unity已经提供了写好了的Baker。
- Full Baking,Authoring Scene 里面所有组件的Baker都会被调用一次;lncremental Baking:那么只有修改的组件才会被Baker一次;
在baker中访问其他来源数据
为了保证lncremental Baking的正常工作,Unity会自动跟踪我们的Authoring Component中的数据成员,当数据成员改变了,就会调用Baker的bake方法,让他重新执行;但是Unity不会跟踪这个转换依赖的其它的数据资源, 所以需要在Baker方法里面添加依赖关系,系统才可以追踪依赖的变化。
- 代码示例:
public class DependentDataAuthoring : MonoBehaviour{public GameObject Other;public Mesh Mesh;}public class GetComponentBaker : Baker<DependentDataAuthoring>{public override void Bake(DependentDataAuthoring authoring){// Before any early out, declare a dependency towards the external references.// Because even if those evaluate to null, they might still be a proper Unity// reference to a missing object. The dependency ensures that the baker will// be triggered when those objects are restored.DependsOn(authoring.Other);DependsOn(authoring.Mesh);if (authoring.Other == null) return;if (authoring.Mesh == null) return;var transform = GetComponent<Transform>();var transformOther = GetComponent<Transform>(authoring.Other);
使用DependOn方法声明依赖项,这样当Other或者Mesh变化时,这个GameObject也会被Baking
**注意:**这里的GetComponent并不是原来意义上的GetComponent. 它在调用时内部也会注册依赖关系, 即使一开始对象上没有GetComponent获取的组件(baking时返回为空) 当重新获得这个组件的时候也会再一次触发这个Baker