在ECS系统中使用IJobChunk作业
洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智学习DOTS。
你可以在系统内部实现IJobChunk,用于逐块遍历数据。当你在系统的OnUpdate()
中安排IJobChunk作业时,该作业为每个符合entity查询条件的chunk调用一次Execute()
。然后,你可以遍历每个chunk中的entity上的数据。
使用IJobChunk
与Entities.ForEach相比,使用IJobChunk进行迭代需要更多的代码,但是也更直接。
按块进行迭代的另一个好处是,你可以使用Archetype.Has<T>()
来检查每个块中是否存在可选组件,然后相应地处理块中的所有entity。
实现IJobChunk作业的步骤如下:
- 创建一个
EntityQuery
来标识要处理的entity。 - 定义job结构体,并包含
ArchetypeChunkComponentType
对象的字段,这些对象标识job必须直接访问的组件的类型。另外,指定作业是只读还是写入这些组件。 - 实例化作业并在系统
OnUpdate()
方法中安排作业。 - 在
Execute()
函数中,获取NativeArray
作业读取或写入的组件的实例,然后在当前块上进行遍历以执行所需的工作。
后面会有实例演示如何使用IJobChunk
。
使用EntityQuery查询数据
EntityQuery定义原型必须包含的一组组件类型,系统才能处理其关联的块和实体。原型中可以有其他组件,但是它必须至少有EntityQuery定义的组件。你还可以排除包含特定类型组件的原型。
对于简单查询,可以使用SystemBase.GetEntityQuery()
函数并按如下所示传入组件类型:
public class RotationSpeedSystem : SystemBase
{private EntityQuery m_Query;protected override void OnCreate(){m_Query = GetEntityQuery(ComponentType.ReadOnly<Rotation>(),ComponentType.ReadOnly<RotationSpeed>());//...}
对于更复杂的情况,你可以使用EntityQueryDesc
。EntityQueryDesc
提供了灵活的查询机制,以指定的组件类型:
All
:此数组中的所有组件类型必须存在于原型中Any
:原型中必须至少存在此数组中的一种组件类型None
:原型中不能存在此数组中的任何组件类型
例如,以下查询包括包含RotationQuaternion
和RotationSpeed
组件的原型,但不包括包含Frozen
组件的任何原型:
protected override void OnCreate()
{var queryDescription = new EntityQueryDesc(){None = new ComponentType[]{typeof(Static)},All = new ComponentType[]{ComponentType.ReadWrite<Rotation>(),ComponentType.ReadOnly<RotationSpeed>()}};m_Query = GetEntityQuery(queryDescription);
}
查询时可以使用ComponentType.ReadOnly<T>
而不是更简单的typeof
表达式来指定系统不会写入RotationSpeed
。
你还可以组合多个查询,传入EntityQueryDesc
对象数组而不是单个实例。ECS使用逻辑或运算来组合每个查询。下面的示例选择包含RotationQuaternion
或RotationSpeed
组件(或两者同时都有)的所有原型:
protected override void OnCreate()
{var queryDescription0 = new EntityQueryDesc{All = new ComponentType[] {typeof(Rotation)}};var queryDescription1 = new EntityQueryDesc{All = new ComponentType[] {typeof(RotationSpeed)}};m_Query = GetEntityQuery(new EntityQueryDesc[] {queryDescription0, queryDescription1});
}
**注意:**请勿在EntityQueryDesc
中包含可选组件。要处理可选组件,在IJobChunk.Execute()
使用chunk.Has<T>()
内部方法检查当前ArchetypeChunk是否有可选组件。因为同一块中的所有实体都具有相同的组件,所以你只需要一个块检查一次即可,不用每个实体检查一次。
为了提高效率并避免创建不必要地垃圾收集的引用类型,应在系统OnCreate()
方法中创建EntityQueries
,然后将结果存储在实例变量中。(在上面示例中,m_Query
变量就是这个用途)
定义IJobChunk结构体
IJobChunk结构体中定义了作业运行时需要的数据以及作业的Execute()
方法。
要访问系统传给Execute()
方法的块内的组件数组,必须为作业读取或写入的每种类型的组件创建一个ArchetypeChunkComponentType<T>
对象。你可以使用这些对象获取NativeArray
实例,通过这些NativeArray
可以访问实体的组件。包括作业的Execute()
方法读取或写入的EntityQuery中引用的所有组件。你还可以用ArchetypeChunkComponentType
获取未包含在EntityQuery中的可选组件类型。
在访问当前块之前,必须检查确保当前块有可选组件。例如,HelloCube IJobChunk示例定义了一个作业结构体,该结构定义了两个组件的ArchetypeChunkComponentType<T>
变量,RotationQuaternion
和RotationSpeed
:
[BurstCompile]
struct RotationSpeedJob : IJobChunk
{public float DeltaTime;public ComponentTypeHandle<Rotation> RotationTypeHandle;[ReadOnly] public ComponentTypeHandle<RotationSpeed> RotationSpeedTypeHandle;public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex){// ...}
}
系统为OnUpdate()
函数中的这些变量赋值,ECS在运行作业时会在Execute()
方法内使用这些变量。
这个作业还使用Unity的delta时间为3D对象的旋转设置动画。该示例使用struct字段将delta值传递给Execute()
方法。
编写Execute方法
IJobChunk Execute()
方法的签名为:
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
chunk
参数是内存块的句柄,包含此作业的迭代时必须处理的实体和组件。因为块只能包含一个原型,所以块中的所有实体都具有相同的组件。
使用chunk
参数获取组件的NativeArray实例:
var chunkRotations = chunk.GetNativeArray(RotationTypeHandle);
var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedTypeHandle);
这些数组是对齐的,同个实体在所有数组中具有相同的索引。可以使用正常的for循环来遍历组件数组。使用chunk.Count
得到存储在当前块的实体的数量:
var chunkRotations = chunk.GetNativeArray(RotationTypeHandle);
var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedTypeHandle);
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 Rotation{Value = math.mul(math.normalize(rotation.Value),quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime))};
}
如果在EntityQueryDesc中有Any
过滤器,或者可选的组件没有写在查询中,则可以在使用之前用ArchetypeChunk.Has<T>()
函数测试当前块是否包含这些组件:
if (chunk.Has<OptionalComp>(OptionalCompType))
{//...}
**注意:**如果使用并发的entity command buffer,将chunkIndex
参数作为sortKey
参数传递给命令缓冲区函数。
跳过没有变化的实体的块
如果仅在组件值发生更改时才需要更新实体,可以将该组件类型添加到EntityQuery的更改筛选器中。例如,如果你的系统读取两个组件,并且仅在前两个组件中的一个已更改时才需要更新第三个组件,则可以按以下方式使用EntityQuery:
private EntityQuery m_Query;protected override void OnCreate()
{m_Query = GetEntityQuery(ComponentType.ReadWrite<Output>(),ComponentType.ReadOnly<InputA>(),ComponentType.ReadOnly<InputB>());m_Query.SetChangedVersionFilter(new ComponentType[]{ComponentType.ReadWrite<InputA>(),ComponentType.ReadWrite<InputB>()});
}
EntityQuery更改过滤器最多支持两个组件。如果你想进行更多检查或不使用EntityQuery,则可以手动进行检查。要进行这个检查,可以使用ArchetypeChunk.DidChange()
函数将组件的块的更改版本与系统的LastSystemVersion
进行比较。如果此函数返回false,则可以完全跳过当前块,因为自从上次系统运行以来,该类型的组件均未更改。
你必须使用一个struct字段将LastSystemVersion
从系统传递到作业中,如下所示:
[BurstCompile]
struct UpdateJob : IJobChunk
{public ComponentTypeHandle<InputA> InputATypeHandle;public ComponentTypeHandle<InputB> InputBTypeHandle;[ReadOnly] public ComponentTypeHandle<Output> OutputTypeHandle;public uint LastSystemVersion;public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex){var inputAChanged = chunk.DidChange(InputATypeHandle, LastSystemVersion);var inputBChanged = chunk.DidChange(InputBTypeHandle, LastSystemVersion);// If neither component changed, skip the current chunkif (!(inputAChanged || inputBChanged))return;var inputAs = chunk.GetNativeArray(InputATypeHandle);var inputBs = chunk.GetNativeArray(InputBTypeHandle);var outputs = chunk.GetNativeArray(OutputTypeHandle);for (var i = 0; i < outputs.Length; i++){outputs[i] = new Output { Value = inputAs[i].Value + inputBs[i].Value };}}
}
与所有作业结构体字段一样,在计划作业之前,必须先赋值:
protected override void OnUpdate()
{var job = new UpdateJob();job.LastSystemVersion = this.LastSystemVersion;job.InputATypeHandle = GetComponentTypeHandle<InputA>(true);job.InputBTypeHandle = GetComponentTypeHandle<InputB>(true);job.OutputTypeHandle = GetComponentTypeHandle<Output>(false);this.Dependency = job.ScheduleParallel(m_Query, this.Dependency);
}
**注意:**为了提高效率,更改版本适用于整个块,而不是单个实体。如果另一个具有写入该类型组件功能的作业访问了块,ECS就会增加该组件的更改版本,并且DidChange()
函数会返回true。即使声明对组件进行写如访问的作业实际上并未更改组件的值,ECS也会增加更改版本。
实例化并安排作业
要运行IJobChunk作业,必须创建作业结构体的实例,给结构体字段赋值,然后安排作业。当你在SystemBase的OnUpdate()
方法中执行时,系统会将安排作业每帧运行一次。
protected override void OnUpdate()
{var job = new RotationSpeedJob(){RotationTypeHandle = GetComponentTypeHandle<Rotation>(false),RotationSpeedTypeHandle = GetComponentTypeHandle<RotationSpeed>(true),DeltaTime = Time.DeltaTime};this.Dependency = job.ScheduleParallel(m_Query, this.Dependency);
}
调用GetArchetypeChunkComponentType<T>()
函数设置组件类型变量时,确保将只需要读取不需要写入的组件的isReadOnly
参数设置为true,这些参数可能会对ECS框架安排作业的效率产生重大影响。这些访问模式的设置,需要在结构体定义和EntityQuery中匹配。
不要在系统类变量中缓存GetArchetypeChunkComponentType<T>()
返回值。你必须在每次系统运行时调用这个函数,并将更新后的值传给作业。
扩展阅读
- ECS中的Entity实体
- ECS之Component组件
- ECS之System系统
- 在ECS系统中使用Entities.ForEach
- 在ECS系统中使用Job.WithCode
【扩展学习】在洪流学堂公众号回复
DOTS
可以阅读本系列所有文章,更有视频教程等着你!
呼~ 今天小新絮絮叨叨的真是够够的了。没讲清楚的地方欢迎评论,咱们一起探索。
我是大智(vx:zhz11235),你的技术探路者,下次见!
别走!点赞,收藏哦!
好,你可以走了。
在ECS系统中使用IJobChunk作业相关推荐
- 在ECS系统中使用Job.WithCode
洪流学堂,让你快人几步.你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智学习DOTS. Entities.ForEach是最常见也是最简单的遍历entity的方式,除此之外还有一些更灵活 ...
- 在ECS系统中使用Entities.ForEach
洪流学堂,让你快人几步.你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智学习DOTS. SystemBase中提供的Entities.ForEach方法是遍历entity和compone ...
- python测试udp端口_Linux系统的ECS实例中TCP/UDP端口测试及验证方法说明
免责声明:本文档可能包含第三方产品信息,该信息仅供参考.阿里云对第三方产品的性能.可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺. 概述 本文主要介绍在Linux系统的ECS实例中,如 ...
- 在ECS实例的centos系统中安装Hadoop
返回<在阿里云中搭建大数据实验环境>首页 提示:本教程是一个系列文章,请务必按照本教程首页中给出的各个步骤列表,按照先后顺序一步步进行操作,本博客假设你已经顺利完成了之前的操作步骤. 在阿 ...
- 在一个请求分页系统中,分别采用 FIFO、LRU和 OPT页面置换算法时,假如一个作业的页面走向为 4、3、2、1、4、3、5、4、3、2、1、5,当分配给该作业的物理块数M分别为 3、4时,
页面置换算法 题目: 在一个请求分页系统中,分别采用 FIFO.LRU和 OPT页面置换算法时,假如一个作业的页面走向为 4.3.2.1.4.3.5.4.3.2.1.5,当分配给该作业的物理块数M分别 ...
- 在一个请求分页系统中,假定系统分配给一个作业的物理块数为 3,并且此作业的页面走向为 2、3、2、1、5、2、4、5、3、2、5、2。试用 FIFO和 LRU 两种算法分别计算出程序访问过程中所发生
页面置换算法 题目: 在一个请求分页系统中,假定系统分配给一个作业的物理块数为 3,并且此作业的页面走向为 2.3.2.1.5.2.4.5.3.2.5.2.试用 FIFO和 LRU 两种算法分别计算出 ...
- 【解决方案】SkeyeVSS无线监测视频监控系统在有限空间作业中的保障应用
在有限空间作业,往往因为通风不良,导致有毒.易燃气体的积聚和缺氧,造成有限空间人员伤亡事故:同时由于内部固有风险产生危害时,人员不便于逃离或救援. 有限空间作业无线监测视频监控系统解决方案,正是为了预 ...
- (八)在ECS实例的Ubuntu系统中安装Hadoop
在阿里云ECS的Ubuntu系统中安装Hadoop,和在本地电脑安装Hadoop,基本相似,但是,也有略微差别,必须正确配置,否则,会导致无法顺利启动.安装Hadoop之前,请确保已经根据前面的博客& ...
- 信号与系统中的机器学习相关的算法的进展和理解(期末作业)
机器学习是一门多学科交叉专业,涵盖概率论知识,统计学知识,近似理论知识和复杂算法知识,使用计算机作为工具并致力于真实实时的模拟人类学习方式, 并将现有内容进行知识结构划分来有效提高学习效率. 机器学习 ...
最新文章
- Open vSwitch 安装
- [转]PHP: 深入pack/unpack
- linux系统负载检查方法
- 【WPF】拖拽ListBox中的Item
- PowerShell 2.0 实践(四)管理Windows进程
- Android-一张图理解MVP的用法
- 我的学习生涯(Delphi篇) - 21
- 如何查看 Linux 中所有正在运行的服务
- 这个隐瞒了100多天的彩蛋,在圣诞节搞丢了一群开发者的饭碗
- 前端项目开发流程(附思维导图PC)
- python+django+mysql图书馆座位预约系统毕业设计毕设开题报告
- Iphone各个型号机型的详细参数,尺寸和dpr以及像素
- 新元宇宙每周连载《地球人奇游天球记》第十八回冥王遇鬼
- CE-FPN: Enhancing Channel Information for Object Detection
- “赋能”企业,数加服装ERP智助企业乘风破浪
- 百度网盘漏洞,2019年不限速方法,一直享受高速加速下载!
- 玩手机惹怒丈夫 男子用菜刀砍死怀孕8个月妻子
- 世界上不存在完美的人性
- Arduino实验十三 YFS201霍尔效应水流传感器
- Linux-nginx配置文件详解与配置与请求行/头/体过长414、413配置