Unity DOTS中的baking(三)过滤baking的输出

2024-02-04 03:36
文章标签 输出 unity 过滤 dots baking

本文主要是介绍Unity DOTS中的baking(三)过滤baking的输出,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Unity DOTS中的baking(三)过滤baking的输出

默认情况下,在conversation world(baker和baking system运行的环境)下产生的所有entities和components,都会作为baking环节的输出。在baking结束时,Unity必须将自上次baking以来发生变化的任何数据,都要复制到main world。不过,有些数据其实只在编辑时有用,我们希望可以在baking输出的时候将其舍弃掉。为此,Unity也提供了一些过滤的手段进行支持。

首先是属性BakingType,它用来标记一个component,使其只会存在于conversation world,不会输出到main world中。我们来看一个例子:

public class MyAuthoring : MonoBehaviour
{public int bakeIntData = 0;public ImageGeneratorInfo info;class MyBaker : Baker<MyAuthoring>{public override void Bake(MyAuthoring authoring){Debug.Log("==========================Bake Invoked!========================== " + authoring.name);DependsOn(authoring.info);if(authoring.info == null) return;//var transform = authoring.transform;var transform = GetComponent<Transform>();var entity = GetEntity(TransformUsageFlags.None);AddComponent(entity, new MyComponent {value = authoring.bakeIntData,spacing = authoring.info.Spacing,position = transform.position});AddComponent(entity, new MyComponentBakingType {value = authoring.bakeIntData});}}
}public struct MyComponent : IComponentData
{public int value;public float spacing;public float3 position;
}[BakingType]
public struct MyComponentBakingType : IComponentData
{public int value;
}

这个例子中,我们在Baker里为entity添加了两个component,其中MyComponentBakingType是一个标记了BakingType属性的component。那么,在conversation world下,可以看到这两个component都存在于entity上:

Unity DOTS中的baking(三)1

而在editor world下,就只剩下MyComponent这一个component了:

Unity DOTS中的baking(三)2

接下来我们来研究研究Unity在其背后做了哪些事情。首先对于BakingType属性,Unity在TypeManager中定义了一个与之匹配的常量:

/// <summary>
/// Bitflag set for component types decorated with the <seealso cref="BakingTypeAttribute"/> attribute.
/// </summary>
public const int BakingOnlyTypeFlag = 1 << 20;

然后在add component时,会对component所定义的属性进行判断:

bool isBakingOnlyType = Attribute.IsDefined(type, typeof(BakingTypeAttribute));
if (isBakingOnlyType)typeIndex |= BakingOnlyTypeFlag;

TypeManager还对外暴露了IsBakingOnlyType这一get属性:

public bool IsBakingOnlyType 
{[MethodImpl(MethodImplOptions.AggressiveInlining)] get { return  (Value & TypeManager.BakingOnlyTypeFlag) != 0; } 
}

这一get属性会在GatherComponentChangesBuildPackedGatherComponentChangesJob两个job中使用,它们用来收集发生变化的components,但包含BakingType属性的component会被过滤掉。这两个job由EntityManagerDiffer类触发:

/// <summary>
/// Generates a detailed change set for the world.
/// All entities to be considered for diffing must have the <see cref="EntityGuid"/> component with a unique value.
/// </summary>
/// <remarks>
/// The resulting <see cref="EntityChanges"/> must be disposed when no longer needed.
/// </remarks>
/// <param name="options">A set of options which can be toggled.</param>
/// <param name="allocator">The allocator to use for the results object.</param>
/// <returns>A set of changes for the world since the last fast-forward.</returns>
public EntityChanges GetChanges(EntityManagerDifferOptions options, AllocatorManager.AllocatorHandle allocator)
{var changes = EntityDiffer.GetChanges(ref m_CachedComponentChanges,srcEntityManager: m_SourceEntityManager,dstEntityManager: m_ShadowEntityManager,options,m_EntityQueryDesc,m_BlobAssetCache,allocator);return changes;
}

从代码中可以猜测出,这里的变化指的是conversation world和shadow world之间的diff,conversation world就是baker和baking system运行的地方,而shadow world则是上一次baking环节输出的拷贝,Unity使用这个shadow world,与当前baking的输出进行对比,只把不同的components和entities拷贝到main world,然后再更新shadow world为当前的conversation world。那么,既然代码中调用的两个job会把包含BakingType属性的component过滤掉,很明显最后输出到main world的components就不包含它们了。

除了BakingType属性之外,Unity还提供了TemporaryBakingType属性标记一个component。这两者有什么区别呢?Unity官方文档中给出了一段说明:

You can also exclude components with the following attributes:

  • [BakingType]: Filters any components marked with this attribute from the baking output.
  • [TemporaryBakingType]: Destroys any components marked with this attribute from the baking output. This means that components marked with this attribute don’t remain from one baking pass to the next, and only exist during the time that a particular baker ran.

可以得知,TemporaryBakingType属于那种阅后即焚的操作,拥有该属性的component,会在触发相应的baking时add到entity上,然后在baking结束时component就会被销毁,这意味着该component也不会一直存在于conversation world。之后,如果不再触发相应的baking,那么该component在conversation world里也不复存在。

这么说有点枯燥,我们还是用代码进行实验,在前面例子的基础上,新增定义一个component,然后在authoring Bake函数中add一下:

[TemporaryBakingType]
public struct MyComponentTempBakingType : IComponentData
{public int value;
}public override void Bake(MyAuthoring authoring)
{AddComponent(entity, new MyComponentTempBakingType{value = authoring.bakeIntData});
}

由于它在conversation world中也是转瞬即逝,我们没法直接在编辑器观察到它。这里需要借助一下BakingSystem,BakingSystem是一类只存在baking过程中的system,它负责把各种baker产生的输出做进一步处理,而我们这里就是要观察baker中TemporaryBakingType属性的component。

[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
[BurstCompile]
public partial struct MyAuthoringBakingSystem : ISystem
{EntityQuery m_query;[BurstCompile]public void OnCreate(ref SystemState state){m_query = SystemAPI.QueryBuilder().WithAll<MyComponentTempBakingType>().Build();state.RequireForUpdate(m_query);}[BurstCompile]public void OnUpdate(ref SystemState state){Debug.Log("=================update my authoring baking system===================");}
}

OnCreate会在system创建的时候执行一次,代码中就是获取当前包含MyComponentTempBakingTypecomponent的entity,如果存在这样的entity,则system才会执行OnUpdate。那么,根据我们之前的假设,这里的OnUpdate只会执行一次。实际上也是如此:

Unity DOTS中的baking(三)3

类似地,暗地里Unity在TypeManager中定义了一个flag常量:

/// <summary>
/// Bitflag set for component types decorated with the <seealso cref="TemporaryBakingTypeAttribute"/> attribute.
/// </summary>
public const int TemporaryBakingTypeFlag = 1 << 21;

然后对外暴露名为TemporaryBakingType的get属性:

/// <summary>
/// <seealso cref="TypeIndex.IsTemporaryBakingType"/>
/// </summary>
public bool TemporaryBakingType => IsTemporaryBakingType(TypeIndex);

最终这一属性会被Unity内部的BakingStripSystem所使用,在OnCreate时会创建所有带有该attribute的entity query:

protected override void OnCreate()
{var allTypes = TypeManager.AllTypes.Where(t => t.TemporaryBakingType).ToArray();m_BakingComponentQueries = new NativeArray<(ComponentType, EntityQuery)>(allTypes.Length, Allocator.Persistent);for(int i = 0; i < allTypes.Length; i++){var componentType = ComponentType.FromTypeIndex(allTypes[i].TypeIndex);EntityQueryDesc desc = new EntityQueryDesc(){All = new ComponentType[] {componentType},Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab};m_BakingComponentQueries[i] = (componentType, GetEntityQuery(desc));}
}

然后每次update时,对符合条件的entity移除掉带有MyComponentTempBakingType属性的component:

protected override void OnUpdate()
{using (s_stripping.Auto()){foreach(var (componentType, query) in m_BakingComponentQueries){EntityManager.RemoveComponent(query, componentType);}}
}

BakingTypeMyComponentTempBakingType都是用来过滤component的,Unity还提供了一种过滤entity的方式,即给需要过滤掉的entity上添加一个名为BakingOnlyEntity的component。

AddComponent<BakingOnlyEntity>(entity);

这样这个entity就只会存在于conversation world中:

Unity DOTS中的baking(三)4

Unity DOTS中的baking(三)5

背后的实现也很简单,就是有一个专门处理这个component的system,筛选出符合条件的entity,把它们一一销毁掉:

protected override void OnCreate()
{_DestroyRemoveEntityInBake = new EntityQueryBuilder(Allocator.Temp).WithAny<RemoveUnusedEntityInBake, BakingOnlyEntity>().WithOptions(EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab).Build(this);
}protected override void OnUpdate()
{EntityManager.DestroyEntity(_DestroyRemoveEntityInBake);
}

Reference

[1] Filter baking output

[2] Baking worlds overview

这篇关于Unity DOTS中的baking(三)过滤baking的输出的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/676255

相关文章

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

java streamfilter list 过滤的实现

《javastreamfilterlist过滤的实现》JavaStreamAPI中的filter方法是过滤List集合中元素的一个强大工具,可以轻松地根据自定义条件筛选出符合要求的元素,本文就来... 目录1. 创建一个示例List2. 使用Stream的filter方法进行过滤3. 自定义过滤条件1. 定

Redis如何实现刷票过滤

《Redis如何实现刷票过滤》:本文主要介绍Redis如何实现刷票过滤问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录引言一、概述二、技术选型三、搭建开发环境四、使用Redis存储数据四、使用SpringBoot开发应用五、 实现同一IP每天刷票不得超过次数六

python多种数据类型输出为Excel文件

《python多种数据类型输出为Excel文件》本文主要介绍了将Python中的列表、元组、字典和集合等数据类型输出到Excel文件中,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一.列表List二.字典dict三.集合set四.元组tuplepython中的列表、元组、字典

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

使用TomCat,service输出台出现乱码的解决

《使用TomCat,service输出台出现乱码的解决》本文介绍了解决Tomcat服务输出台中文乱码问题的两种方法,第一种方法是修改`logging.properties`文件中的`prefix`和`... 目录使用TomCat,service输出台出现乱码问题1解决方案问题2解决方案总结使用TomCat,

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.

Mybatis拦截器如何实现数据权限过滤

《Mybatis拦截器如何实现数据权限过滤》本文介绍了MyBatis拦截器的使用,通过实现Interceptor接口对SQL进行处理,实现数据权限过滤功能,通过在本地线程变量中存储数据权限相关信息,并... 目录背景基础知识MyBATis 拦截器介绍代码实战总结背景现在的项目负责人去年年底离职,导致前期规