Prefabs in baking

制作预制体

首先我们要制作一个预制体。
预制体有两个组件需求

  • 有一个预制体的标记,让方便我们去查询这个entity是不是一个预制体
  • LinkedEntityGroup 缓冲区,将预制件中的所有子级存储在平面列表中,目的是为了快速生成一个完整的组实体,而不是只生成一个父物体

如果不添加预制体的标记,后果是你无法找到自己生成的预制体,如果不添加 LinkedEntityGroup(LinkedEntityGroupAuthoring)后果是你实例化的时候不会生成子物体,只生成父物体

生成预制体

预制体准备好了,我们要把预制体生成的entity保存起来。
保存也有两种方式

  • 直接作为entity保存
public struct PrefabComponent:IComponentData
{
    public Entity _entity;
}
public class PrefabAuthing : MonoBehaviour
{
    public GameObject BakerObject;
}

public partial class PrefabBake :Baker<PrefabAuthing>
{
    public override void Bake(PrefabAuthing authoring)
    {
        Entity entity = GetEntity(TransformUsageFlags.Dynamic);
        Entity prefabEntity = GetEntity(authoring.BakerObject, TransformUsageFlags.Dynamic);
        PrefabComponent prefabComponent = new PrefabComponent();
        prefabComponent._entity = prefabEntity;
        AddComponent(entity, prefabComponent);
    }
}
  • 作为EntityPrefabReference 进行保存
public struct PrefabComponent:IComponentData
{
    public EntityPrefabReference _entity;
}

public class PrefabAuthing : MonoBehaviour
{
    public GameObject BakerObject;
}
public partial class PrefabBake :Baker<PrefabAuthing>
{
    public override void Bake(PrefabAuthing authoring)
    {
        Entity entity = GetEntity(TransformUsageFlags.Dynamic);
        PrefabComponent prefabComponent = new PrefabComponent();
        prefabComponent._entity = new EntityPrefabReference(authoring.BakerObject);
        AddComponent(entity, prefabComponent);
    }
}

EntityPrefabReference的效果将对应的Entity烘焙成独立的文件,该文件可以在使用预制件之前在运行时加载,这样他就可以在多个SubScene中使用了。这里要注意,PrefabAuthing 里面存的一定要是预制体,而不是普通的GameObject.如果存储普通的GameObject 会在生成的时候报错。

预制体使用并生成

这个生成同样是在运行是生效的,所以我们需要使用到System

  • 对于生成的是Entity的预制体
public partial class InstancePrefabSystem : SystemBase
{
    protected override void OnUpdate()
    {
        var ecb = new EntityCommandBuffer(Allocator.Temp);

        // Get all Entities that have the component with the Entity reference
        foreach (var prefab in SystemAPI.Query<RefRO<PrefabComponent>>())
        {
            //Instantiate the prefab Entity
            var instance = ecb.Instantiate(prefab.ValueRO._entity);
            //到这里就已经生成完毕了
            // Note: the returned instance is only relevant when used in the ECB
            // as the entity is not created in the EntityManager until ECB.Playback
            MoveComponent moveComponent = new MoveComponent();
            moveComponent.Speed = 5;
            ecb.AddComponent<MoveComponent>(instance,moveComponent);
        }
        ecb.Playback(EntityManager);
        ecb.Dispose();
    }
}
  • 对于生成EntityPrefabReference的方法
    首先我们要获取到 EntityPrefabReference 对应的Entity
protected override void OnUpdate()
    { 
        var ecb = new EntityCommandBuffer(Allocator.Temp);
	        foreach (var (component, entity) in SystemAPI.Query<RefRO<PrefabComponent>>().WithEntityAccess().WithNone<PrefabLoadResult>())
        {
            if (!EntityManager.HasComponent<RequestEntityPrefabLoaded>(entity))
            {
                RequestEntityPrefabLoaded load = new RequestEntityPrefabLoaded() { Prefab = component.ValueRO._entity };
                ecb.AddComponent<RequestEntityPrefabLoaded>(entity,load);
            }
        }
        ecb.Playback(EntityManager);
        ecb.Dispose();
    }

这里我们借助了 RequestEntityPrefabLoaded 这个组件,这个组件是自动去加载 EntityPrefabReference 所对应的 Entity的,而加载好的entity 则会存储在 自动添加的 PrefabLoadResult 组件里的 PrefabRoot中
接下来,我们对 PrefabLoadResult 直接做和entity相同的操作就行了
(我尝试根据示例中在OnStartRunning进行加载,失败了,退而求其次,选择在OnUpdate中加载)

生成预制体获取和删除

public partial class DestoryprefabSystem : SystemBase
{
    protected override void OnUpdate()
    {
        //var prefabQuery = SystemAPI.QueryBuilder().WithAll<BakedEntity>().WithOptions(EntityQueryOptions.IncludePrefab).Build();
        var ecb = new EntityCommandBuffer(Allocator.Temp);
		//在这里进行获取
        // Get all Entities that have the component with the Entity reference
        foreach (var (component, entity) in
                 SystemAPI.Query<RefRO<LocalTransform>>().WithEntityAccess())
        {
            // Instantiate the prefab Entity
            if (component.ValueRO.Position.x > 20)
            {
            	//这一步是删除
                ecb.DestroyEntity(entity);
            }
        }
        ecb.Playback(EntityManager);
        ecb.Dispose();
    }
}

这样我们就能查找到对应的Entity和删除了

EntityCommandBuffer

我们在做这些的时候用到了 EntityCommandBuffer 这个类,这个类是干啥的呢

EntityCommandBuffer 是用于块和数据的内存分配器,他负责去分配各种数据所对应的内存,
而Allocator.Temp 是指的临时分配内存,我们所实例化出来的Entity需要临时存储在这个类型的内存中。
而 Playback 是为了回放所有的操作,并将这些操作记录在EntityManager中执行(在场景中执行),
执行后,上面的数据才会放置到对应的EntityManager和场景中。
Dispose 则是将过程中产生的内存释放掉(比如生成的Entity).

这样我们就完成了预制体的加载,查询和删除了。