Unity ECS+Jobs System笔记 访问数据2(六)
来源:https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/index.html
我会对官方文档内容略作整理,有需要可以查看官方文档
3、IJobChunk
你可以通过在JobComponentSystem中实现IJobChunk来对区块中的数据进行迭代,JobComponentSystem会一个个的对每一个你希望系统去执行的实体所在的区块执行Execute()函数,然后,你可以逐个实体处理每个区块内的数据
使用IJobChunk进行迭代需要更多(相较于IJobForEach而言)的设置,但同时也更加明确,并且是对数据最直接的访问(因为访问的就是内存块)
使用IJobChunk的另一个好处是,您可以检查每个区块中是否存在可选组件(是原型所具有的组件),然后相应处理区块中所有的实体
实现IJobChunk作业需要的步骤包括:
- 通过创建EntityQuery来标识要处理的实体
- 定义Job结构,包括ArchetypeChunkComponentType对象的字段——以标识Job需要直接访问的组件类型并指定Job是读取还是写入这些组件
- 实例化Job结构并在系统OnUpdate()函数中调度Job
- 在Execute()函数中,获取Job读取或写入的组件的NativeArray,最后迭代当前块以执行所需的工作
可以参考ECS Sample中的HelloCube例子
通过EntityQuery查询数据
EntityQuery定义了原型必须包含的一些组件类型,以便系统处理与其相关联的区块和实体,原型也可以有其他组件,但它必须至少包含EntityQuery所定义的组件,你还可以排除包含特定类型组件的原型
对于简单查询,可以使用JobComponentSystem.GetEntityQuery()函数,只需传入组件类型:
public class RotationSpeedSystem : JobComponentSystem { private EntityQuery m_Group; protected override void OnCreate() { m_Group = GetEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>()); } //… }
对于更复杂的情况,可以使用EntityQueryDesc,它提供了灵活的查询机制来查询指定组件类型:
- All——原型中必须包含参数数组中所有的组件
- Any——原型中至少要包含一个参数数组中的组件
- None——原型中不能有任何一个参数数组中的组件
例如,以下代码查询了包含RotationQuaternion和RotationSpeed组件,但不包括任何包含Frozen组件的原型
protected override void OnCreate() { var query = new EntityQueryDesc { None = new ComponentType[]{ typeof(Frozen) }, All = new ComponentType[]{ typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>() } } }; m_Group = GetEntityQuery(query); }
使用ComponentType.ReadOnly< T > 而不是更简单的typeof表达式,可以以只读的方式查询RotationSpeed
你还可以通过传递EntityQueryDesc数组而不是单个来组合查询,使用or运算组合每个查询,以下代码查询了包含RotationQuaternion组件或RotationSpeed组件(或两者)的原型:
protected override void 7ff7 OnCreate() { var query0 = new EntityQueryDesc { All = new ComponentType[] {typeof(RotationQuaternion)} }; var query1 = new EntityQueryDesc { All = new ComponentType[] {typeof(RotationSpeed)} }; m_Group = GetEntityQuery(new EntityQueryDesc[] {query0, query1}); }
注意: 不要在EntityQueryDesc中包含完全可选的组件,要处理可选组件,请在IJobChunk.Execute()中使用chunk.Has< T >()来确定当前ArchetypeChunk是否具有可选组件,由于同一区块中的所有实体具有相同的组件,因此只需要检查每个块是否存在可选组件——而不是检查每个实体
为了提高效率并避免不必要GC,应该在OnCreate()函数中为系统创建EntityQueries,并将结果存储在变量中
定义IJobChunk结构
IJobChunk结构定义了Job运行时所需数据的字段,以及Job的Execute()方法
要访问系统传递给Execute()方法的块内部的组件数组,必须对需要读取或写入的每种类型组件的对象创建ArchetypeChunkComponentType,这些对象允许您获取NativeArrays的实例,从而提供对实体组件的访问,这包含了Execute函数所读写的在Job的实体队列中的所有组件。还可以为EntityQuery中未包含可选组件类型提供ArchetypeChunkComponentType变量。(在尝试访问之前,必须检查以确保当前块具有可选组件)
例如,在HelloCube的IJobChunk示例声明了一个定义ArchetypeChunkComponentType的两个组件变量——RotationQuaternion和RotationSpeed:
[BurstCompile] struct RotationSpeedJob : IJobChunk { public float DeltaTime; public ArchetypeChunkComponentType<RotationQuaternion> RotationType; [ReadOnly] public ArchetypeChunkComponentType<RotationSpeed> RotationSpeedType; public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex){ //... } }
系统在OnUpdate()函数中为这些变量赋值,当ECS框架运行Job时,变量在Execute()方法内部使用
Job还使用Unity delta时间为3D对象的旋转设置动画,该示例还使用struct字段将此值传递给Execute方法
编写Execute方法
IJobChunk Execute()方法的写法是:
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
该chunk参数是包含要在此Job迭代中处理的实体和组件的内存块的句柄,由于chunk只能包含单个原型,因此块中的所有实体都具有相同的组件
使用chunk参数获取组件的NativeArray实例:
var chunkRotations = chunk.GetNativeArray(RotationType); var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType);
这些数组都是对齐的,因此每个实体在不同数组中有着相同的索引,也就可以正常使用for循环遍历组件数组,使用chunk.Count得到存储在当前块的实体的数量:
for (var i = 0; i < chunk.Count; i++) { var rotation = chunkRotations[i]; var rotationSpeed = chunkRotationSpeeds[i]; // Rotate something about its up vector at the speed given by RotationSpeed. chunkRotations[i] = new RotationQuaternion { Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime)) }; }
如果你在EntityQueryDesc使用Any过滤器或者根本没有查询中出现的可选组件,那么就可以在使用它之前用ArchetypeChunk.Has< T >函数测试当前块是否包含其中一个组件
if (chunk.Has<OptionalComp>(OptionalCompType)) { //... }
注意: 如果使用并发实体命令缓冲区,请将chunkIndex参数作为jobIndex参数传递给命令缓冲区函数。
跳过包含未改变的实体的区块
如果只需在组件值更改时更新实体,则可以将该组件类型添加到EntityQuery的更改筛选器(change filter)中——用于选择Job所需的实体和块,例如,如果你有一个系统读取两个组件,并且只需要在前两个组件中的一个发生更改时更新第三个组件,则可以使用EntityQuery,如下所示:
EntityQuery m_Group; protected override void OnCreate() { m_Group = GetEntityQuery(typeof(Output), ComponentType.ReadOnly<InputA>(), ComponentType.ReadOnly<InputB>()); m_Group.SetFilterChanged(new ComponentType{ typeof(InputA), typeof(InputB)}); }
EntityQuery更改筛选器最多支持两个组件,如果要查看更多或布使用EntityQuery,可以选择手动进行检查,要进行此检查,就使用ArchetypeChunk.DidChange()函数将组件的块更改版本与系统的LastSystemVersion进行比较,如果此函数返回false,则可以跳过当前块,因为自上次系统运行以来,该类型的所有组件都没有更改
系统中的LastSystemVersion必须使用struct传递给Job
[BurstCompile] struct UpdateJob : IJobChunk { public ArchetypeChunkComponentType<InputA> InputAType; public ArchetypeChunkComponentType<InputB> InputBType; [ReadOnly] public ArchetypeChunkComponentType<Output> OutputType; public uint LastSystemVersion; public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex){ var inputAChanged = chunk.DidChange(InputAType, LastSystemVersion); var inputBChanged = chunk.DidChange(InputBType, LastSystemVersion); if (!(inputAChanged || inputBChanged)) return; //... }
与所有Job结构字段一样,您必须在安排Job之前分配其变量:
var job = new UpdateJob() { LastSystemVersion = this.LastSystemVersion, //… initialize other fields }
请注意,为了提高效率,版本更改适用于整个块而不是单个实体,如果某个块已被另一个能够写入该类型组件的Job访问,则该组件的版本将更改,并且DidChange()函数返回true
实例化并安排Job
要运行IJobChunk的Job,必须创建Job结构体,设置结构体的字段,然后安排Job,在JobComponentSystem的OnUpdate()函数中执行此操作时,系统会以每帧调度Job
// OnUpdate runs on the main thread. protected override JobHandle OnUpdate(JobHandle inputDependencies) { var job = new RotationSpeedJob() { RotationType = GetArchetypeChunkComponentType<RotationQuaternion>(false), RotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true), DeltaTime = Time.deltaTime }; return job.Schedule(m_Group, inputDependencies); }
当你调用GetArchetypeChunkComponentType函数来设置组件类型变量时,确保作业所读取的组件是只读的,正确设置这些参数会对ECS框架调度作业的效率产生重大影响,这些访问模式设置必须使结构体定义和EntityQuery中的设置相匹配
不要缓存GetArchetypeChunkComponentType在系统类变量中的返回值,因为系统每次运行时都必须调用该函数,并将更新后的值传递给Job
- Unity ECS+Jobs System笔记 访问数据4(八)
- Unity ECS+Jobs System笔记 访问数据3(七)
- Unity ECS+Jobs System笔记 写入组(九)
- SQL 复习笔记3数据控制语句(授权、授权、拒绝访问)
- Android深入探究笔记之十 -- 使用 ContentProvider 共享数据(二),访问与添加通讯录
- opencv视频学习第九课(访问cvMat数据)笔记整理
- ADO.NET笔记之数据访问技术总结
- SL复习笔记之平稳转型——基础篇(二、控件和数据访问)
- C++编程笔记:使用WinHTTP实现HTTP访问(解决接收UTF8数据乱码问题)
- 微软企业库5.0学习笔记(三十三)数据访问模块
- 学习springboot笔记(四)数据访问之JDBC
- Unity学习笔记:EventSystemAPI
- unity开发之三:www访问数据库(数据存储和数据获取)
- SL复习笔记之平稳转型——基础篇(二、控件和数据访问)
- 国科大大数据复习笔记 陈世敏 关系型数据管理系统(2)RBDMS系统架构 数据的存储与访问 B+Trees 查询索引index
- Enterprise Library 4.1学习笔记2----数据访问程序块
- Spring源码学习笔记---数据访问(二)
- Remoting笔记——错误:“由于安全限制,无法访问类型System.RunTime.Remoting.ObjRef”
- arcgis开发笔记【silverlight同步访问WCF和网站数据】
- OpenCV自学笔记2:访问图像数据