Unity DOTS系列之Filter Baking Output与Prefab In Baking核心分析

  最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Baking核心机制,方便大家上手学习掌握Unity DOTS开发。今天给大家分享的Baking机制中的Filter Baking Output与Prefab In Baking。

Filter Baking Output 机制

  在默认情况下,Baking会为每个GameObject生成的Entity与Component, 这些entity都会被创建到Conversion World里面。然后在创作的时候不是所有的GameObject都需要被转换成Entity。例如: 在一个样条曲线上,一个控制点在创作的时候被用到了,但是bake成ecs数据后可能就再也没有用了,所以不需要把这个控制点Bake成entity。

  为了不把这个GameObject Bake产生的Entity输出到World并保存到entity scene里面,我们可以给这个Entity添加一个BakingOnlyEntity tag Component。当你添加了这个tag component后,Baking系统就不会把你这个entity存储到entity scene里面,也不会把这个entity生成到运行的main World里面。我们可以直接在Baker函数里面添给entity 添加一个BakingOnlyEntity的组件数据,也可以在Authoring GameObject里面添加一个 BakingOnlyEntityAuthoring的组件,这两种方式都可以达到同样的效果没有区别。

  你也可以给组件添加注解[BakingType],[TemporaryBakingType] attributes过滤掉掉组件component,让它不输出到entity中。

  • [BakingType]:被这个attributes注记的Component,将不会在Baking Output的时候输出到entity,它的生命周期与entity一样。

  • [TemporaryBakingType]:被这个attributes注记的Component会在Baking Output的时候不会输出到entity。这个Component只会存活在Baker过程中,Baker结束以后就会销毁。

  我们有时候需要在Baking System里面批量处理一些组件数据,处理完后这些组件就没有用了。例如,baker 把一个bounding box 转换成了ecs component 数据,接下来我们定义了一个Baking System批量处理所有的entity,来计算entity的凸包。计算完成后原来的bounding box组件就可以删除了,这种情况下就可以使用上面的Filter Component Bake output机制。

Prefab In Baking机制

  在Baking的过程中,传统的GameObject Prefab也被Bake成entity Prefab。Entity Prefab其实也是一个Entity,相比普通的Entity它多了几个标记组件prefab tag。当我们查询实体的时候,如果发现这个标记,就会被默认的查询规则排除出来,所以默认查询API查询不到prefab entity。系统会提供一个LinkedEntityGroup buffer会把prefab里面所有的孩子entity的关联起来。当我们创建一个entity prefab的时候,也非常方便的能找到它的孩子相关。

  生成entity prefab到entity scene以后,我们就可以像使用普通的Prefab创建GameObject一样来创建entity到世界。但是使用enity prefab之前一定要确保它已经被Baker到了entity scene。当预制体实例化到Authoring Scene中的时候,Baking把它当作普通的GameObject来进行转换,不会把它当作预制体。

  生成一个entity prefab你需要注册一个Baker,在Bake函数里面添加一个依赖关系,让这个依赖于Authoring GameObject Prefab。然后Prefab将会被bake出来。我们搞一个组件保存了entity prefab的一个引用,那么unity就会把这个entity prefab 序列化到subscene中。当需要使用这个entity prefab的时候就能获取到。代码如下:

public struct EntityPrefabComponent : IComponentData
{
  public Entity Value;
}

public class GetPrefabAuthoring : MonoBehaviour
{
  public GameObject Prefab;
}

public class GetPrefabBaker : Baker<GetPrefabAuthoring>
{
  public override void Bake(GetPrefabAuthoring authoring)
  {
    // Register the Prefab in the Baker
    var entityPrefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic);
    // Add the Entity reference to a component for instantiation later
    var entity = GetEntity(TransformUsageFlags.Dynamic);
    AddComponent(entity, new EntityPrefabComponent() {Value = entityPrefab});
  }
}

  在Baking的时候,当我们需要引用一个entity prefab, 可以使用EntityPrefabReference。这个会把它序列化到entity scene文件里面去。运行的时候直接load进来,就可以使用了。这样可以防止多个subscene不用重复拷贝生成同一个Prefab。

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();
    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();
  }
}

  实例化entity prefab可以使用EntityManager与entity command buffer。

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();
  }
}

  实例化EntityPrefabReference,可以使用如下代码:

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();
    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();
  }
}

  在实例化EntityPrefabReference之前,Unity必须要先加载对应的entity prefab,然后才能使用它,添加RequestEntityPrefabLoaded组件能确保entity prefab被加载。Unity会PrefabLoadResult加载到带有RequestEntityPrefabLoaded同一个entity上。

  预制体也是entity,也可以被查询到,如果需要把一个预制体被查询到,可以在查询条件上添加IncludePrefab。

// This query will return all baked entities, including the prefab entities
var prefabQuery = SystemAPI.QueryBuilder().WithAll<BakedEntity>().WithOptions(EntityQueryOptions.IncludePrefab).Build();

  使用EntityManager与entity command buffer也可以销毁一个预制体节点,代码如下:

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();

今天的Baking系列就分享到这里了,关注我学习更多的最新Unity DOTS开发技巧。


  尊敬的准VIP客户:

  我们Unity DOTS课程也正式发布了,我们课程经过9年多的更新与迭代,已经涵盖了Unity 开发中遇到的绝大部分问题,涵盖了Unity主程序进阶,升职加薪所需要的系统的知识体系,主流游戏类型的重点难点技术解决方案。我们的老师10:00~23:00提供实时解答与回复,包含但不限于客户端+服务端。相信我们提供的游戏开发技术服务能很好的帮助到您。选择我们的VIP课程,您肯定不会后悔!有兴趣请 + 企.鹅.裙 428 540 563


  下面是DOTS的VIP课程前18节视频,免费观看

Unity DOTS进阶与项目实战(B站18集)

第001课DOTS的环境安装与准备事项

第002课 DOTS的核心机制与概述

第003课DOTS的SubScene

第004课Component的概述与普通组件的Baker

第005课System与SystemGroup概述

第006课DOTS中的ECS核心概念总结

第007课Baking系列之Baking与Baker详解

第008课Baking系列之BakingSystem与BakingWorld详解

第009课FilterBakingOutput与PrefabsInBaking

第010课BlobAsset核心机制分析

第011课Aspect核心机制分析

第012课 StructChange核心机制详解

第013课Managed与Unmanaged Component详解与性能分析

第014课ShareComponent核心机制与性能分析

第015课CleanupComponent核心分析

第016课 Dynamic Buffer Component详解与分析

第017课Tag与Chunk Component详解与分析

第018课Enableable与Singleton组件详解与分析

  • 28
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值