Unity ECS学习(6)

现在让我们看一下如何烘焙一个预制体并且使用

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).

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

  • 43
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Unity ECS(Entity Component System)是Unity引擎的一种编程范式,它是一种数据驱动的编程模型,它将游戏对象(Entity)分解为数据和行为两个部分,其中数据由组件(Component)来表示,行为则由系统(System)来实现。相对于传统的面向对象编程模型,ECS提供了更高效、更灵活的编程方式,可以有效地提高游戏的性能和扩展性。 下面是我学习Unity ECS时的笔记: ## Entity Entity是ECS中最基本的概念,它表示游戏对象。每个Entity由一个唯一的ID来标识,可以通过EntityManager来创建、销毁、查询和管理Entity。 ## Component Component是Entity的数据部分,用来描述Entity的属性和状态。每个Component包含一些数据成员和一些方法,用来操作这些数据成员。Component是以结构体(struct)的形式定义的,通常只包含数据成员,不包含方法。 ## System System是Entity的行为部分,用来实现游戏逻辑和操作Component。System可以访问和操作EntityManager和Component,但不能直接访问Entity。每个System包含一个或多个Component,表示它所处理的数据类型。System是以类(class)的形式定义的,通常包含一个Update方法,用来实现游戏逻辑。 ## Job Job是一种轻量级的线程,用于并行执行System中的任务。Job可以访问和操作Component,但不能直接访问Entity和EntityManager。Job通常是以结构体(struct)的形式定义的,不包含方法。 ## Archetype Archetype是Entity的集合,包含一组具有相同Component类型的Entity。Archetype可以用来优化数据的访问和处理,可以在不同的System之间共享。 ## Chunk Chunk是Archetype中的数据块,包含一组连续的Entity和它们的Component数据。Chunk可以用来优化内存的分配和访问,可以在Job中进行并行处理。 ## Buffer Buffer是一种Component类型,用来存储可变长度的数据,例如数组或列表。Buffer可以在System和Job中进行修改和访问。 以上是我学习Unity ECS时的笔记,希望对你有所帮助。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值