Fliter baking output
概述:
- 这是一种Baking机制,过滤掉那些不需要保存到entity scene的数据(磁盘上)。
- 在实际的Baking的需求中,不是所有的都GameObject 需要转成entity,比如只在编辑器下使用的一些控制点。
- Filter Baking Output, 就是把那些不需要转成的数据给他过滤掉,不输出出来。
操作方法:
-
排除Entity:
- 如果你要把一个entity从Baking output里面排除:你可以在entity里面添加一个"BakingOnlyEntity" ecs Component 。这样在输出的时候那么这个entity就会被排除掉;
- 当加了这样的一个组件到entity里面,unity就不会把这个enity保存到entity scene里面;也不会把它合并到ECS main world里面。
- 两种方式:
(1)在Baker里直接对entity添加BakingOnlyEntity组件,
(2)在创作的时候直接对Authoring GameObjec上添加BakingOnlyEntityAuthoring组件
-
排除Component Data
- 给这个组件加对应的注解Attributes就行
- [BakingType]:那些被标记了BakingType的ComponentData都不会被输出到baking output , Bakeing结束时和Entiy同享的生命周期;
- 给这个组件加对应的注解Attributes就行
应用场景举例:
- 我们每个entiy有一个bounding Box,随后又定义了一个Baking System,搜集所有的entity的bounding box,计算出来它们的convex hull(凸包).当这个Baking System结束以后,这个boundingBox这个数据就没有用了。所以这个bounding Box组件数据就不需要输出到output上,这个时候,你就可以使用[BakingType]或者[TemporaryBakingType]进行标注
示例代码:
[TemporaryBakingType]
public struct TemporaryBakingData : IComponentData
{
public float Mass;
}
public struct SomeComputedData : IComponentData
{
// Computing this is too expensive to do in a Baker, we want to make use of Burst (or maybe Jobs)
public float ComputedValue;
}
public class RigidBodyBaker : Baker<Rigidbody>
{
public override void Bake(Rigidbody authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new TemporaryBakingData{Mass = authoring.mass});
// Even though we don't compute the data, we add the type from the Baker
AddComponent(entity, new SomeComputedData());
}
}
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
[BurstCompile]
partial struct SomeComputingBakingSystem : ISystem
{
// We are doing performance critical work here, so we use Burst
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Because TemporaryBakingData is a [TemporaryBakingType] it only exists the same Bake pass that RigidBodyBaker ran
// This means this Baking System will only run if the inputs to RigidBodyBaker change and cause it to re-bake
// Additionally, because we are using no managed types in this system, we can use Burst
foreach (var (computedComponent, bakingData) in
SystemAPI.Query<RefRW<SomeComputedData>, RefRO<TemporaryBakingData>>())
{
var mass = bakingData.ValueRO.Mass;
float result = 0;
// Do heavy computation here, which is taking advantage of Burst
// result = ...
computedComponent.ValueRW.ComputedValue = result;
}
}
}
Prefabs in baking
概述:
- ECS 里面我们也可以把entity做成prefab,这样就可以非常方便的clone创建出来entity;
- Entity prefab和GameObject prefab的使用方式都是类似的。
- Step1:首先把GameObject Prefab Baking成Entity Prefab。
- Step2:使用我们的entity Prefab来进行复制我们的entity。
- Entity Prefab是一种特殊entity, 拥有以下两个Component
- prefab tag: 区别了prefab entity与普通的entity,我们要查询世界所有entity的时候, 默认的查询规则就根据这个tag,排除掉prefab entity;
- LinkedEntityGroup buffer: 一种保存所有孩子的entity的数据结构的引用.利用LinkedEntityGroup,可以非常方便的找到子entity
- 使用注意:
- entity prefab可以像GamePrefab一样在游戏运行的时候实例化entity出来
- 在使用enity prefab之前必须要确保GameObject Prefab已经Bake转成了entity prefab,使它们在 entity scene可用。
- 如果你得Authoring Scene有一个预制体关联得这个节点,那么Baking处理得时候,会把这个场景中预制体关联得节点当作普通节点,不会把它当作预制体去Bake。
将GameObject Prefab Baking成Entity Prefab方法
- 为了确保预制体被烘焙并在实体场景中可用,必须将它们注册到一个baker中。这确保了对预制体对象的依赖性。当你在组件中引用实体预制体时,Unity会将内容序列化到使用它的subscene场景中。
Prefab in subscene 示例代码:
public struct EntityPrefabComponent : IComponentData
{
//保存Entity Prefab用
public Entity Value;
}
public class GetPrefabAuthoring : MonoBehaviour
{
//关联用Prafab。通过Inspector面板挂载
public GameObject Prefab;
}
public class GetPrefabBaker : Baker<GetPrefabAuthoring>
{
public override void Bake(GetPrefabAuthoring authoring)
{
// Register the Prefab in the Baker
// 将Prefab直接转为Entity
var entityPrefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic);
// Add the Entity reference to a component for instantiation later
// 获取当前entity 并将entityPrefab 赋予给Value值
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new EntityPrefabComponent() {Value = entityPrefab});
}
}
PrefabReferenceInSubScene 示例代码:
- 可以把当前的entity prefab序列化生成到一个单独的entity scene里面,这样多个subscene都可以重用这个(防止每个subscene单独加载)。
public struct EntityPrefabReferenceComponent : IComponentData
{
//保存prefab引用
public EntityPrefabReference Value;
}
public class GetPrefabReferenceAuthoring : MonoBehaviour
{
public GameObject Prefab;
}
public class GetPrefabReferenceBaker : Baker<GetPrefabReferenceAuthoring>
{
public override void Bake(GetPrefabReferenceAuthoring authoring)
{
// Create an EntityPrefabReference from a GameObject. This will allow the
// serialization process to serialize the prefab in its own entity scene
// file instead of duplicating the prefab ECS content everywhere it is used
//通过EntityPrefabReference 获得entity prefab 引用
var entityPrefab = new EntityPrefabReference(authoring.Prefab);
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new EntityPrefabReferenceComponent() {Value = entityPrefab});
}
}
Instantiate Prefab 实例化预制
InstantiateEmbeddedPrefabs 代码实例(对应上面第一种Prefab使用方式):
public partial struct InstantiatePrefabSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Get all Entities that have the component with the Entity reference
foreach (var prefab in
SystemAPI.Query<RefRO<EntityPrefabComponent>>())
{
// Instantiate the prefab Entity
var instance = ecb.Instantiate(prefab.ValueRO.Value);
// Note: the returned instance is only relevant when used in the ECB
// as the entity is not created in the EntityManager until ECB.Playback
ecb.AddComponent<ComponentA>(instance);
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
}
InstantiateLoadedPrefabs 代码实例(对应上面第二种Prefab使用方式):
- 使用EntityPrefabReference时,必须先添加RequestEntityPrefabLoaded组件
- unity会加载有RequestEntityPrefabLoaded的Entity Prefab,同时在加载完成时添加PrefabLoadResult组件
- update的时候把加载好的entity prefab 找出来,然后调用Instantiate,实例化出来;
public partial struct InstantiatePrefabReferenceSystem : ISystem
{
public void OnStartRunning(ref SystemState state)
{
// Add the RequestEntityPrefabLoaded component to the Entities that have an
// EntityPrefabReference but not yet have the PrefabLoadResult
// (the PrefabLoadResult is added when the prefab is loaded)
// Note: it might take a few frames for the prefab to be loaded
var query = SystemAPI.QueryBuilder()
.WithAll<EntityPrefabComponent>()
.WithNone<PrefabLoadResult>().Build();
//使用EntityPrefabReference时,必须先添加RequestEntityPrefabLoaded组件
state.EntityManager.AddComponent<RequestEntityPrefabLoaded>(query);
}
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.Temp);
// For the Entities that have a PrefabLoadResult component (Unity has loaded
// the prefabs) get the loaded prefab from PrefabLoadResult and instantiate it
foreach (var (prefab, entity) in
SystemAPI.Query<RefRO<PrefabLoadResult>>().WithEntityAccess())
{
var instance = ecb.Instantiate(prefab.ValueRO.PrefabRoot);
// Remove both RequestEntityPrefabLoaded and PrefabLoadResult to prevent
// the prefab being loaded and instantiated multiple times, respectively
ecb.RemoveComponent<RequestEntityPrefabLoaded>(entity);
ecb.RemoveComponent<PrefabLoadResult>(entity);
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
}
如何查询Prefab Entity
- 默认情况下,Prefab是被排除在查询外,如果要包括进来,需要使用IncludePrefab 选项进行查询
public partial struct PrefabsInQueriesSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
#region PrefabsInQueries
// This query will return all baked entities, including the prefab entities
var prefabQuery = SystemAPI.QueryBuilder()
.WithAll<BakedEntity>().WithOptions(EntityQueryOptions.IncludePrefab).Build();
#endregion
#region DestroyPrefabs
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (component, entity) in
SystemAPI.Query<RefRO<RotationSpeed>>().WithEntityAccess())
{
if (component.ValueRO.RadiansPerSecond <= 0)
{
ecb.DestroyEntity(entity);
}
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
#endregion
}
}