来自2020.04.28的说明:
网友提示,0.9.1版本下的Entities已经放弃使用 IJobForEach,木头本人当时学习的是0.3版本,所以,理论上来说,本文已作废。
上一篇我们介绍了JobComponentSystem的基础用法,其实还有更多的用法,这次来介绍一下IJobForEach。
同样是用于筛选实体数据,执行一些逻辑操作,但是IJobForEach比起单纯地使用Entities.ForEach,在大部分情况下会有更优秀的性能。
1.IJobForEach
我们还是用之前的代码,但是,System类又要做一些改动:
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
public class RotationSpeedSystem_ForEach : JobComponentSystem
{
struct RotationSpeedJob : IJobForEach<Rotation, RotationSpeed_ForEach>
{
public float dt;
public void Execute (ref Rotation rotation, [ReadOnly]ref RotationSpeed_ForEach rotationSpeed)
{
rotation.Value = math.mul(
math.normalize(rotation.Value),
quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * dt));
}
}
protected override JobHandle OnUpdate (JobHandle inputDeps)
{
var job = new RotationSpeedJob() { dt = Time.DeltaTime };
return job.Schedule(this, inputDeps);
}
}
现在,OnUpdate里的逻辑都放到一个RotationSpeedJob里处理,然后通过调用这个Job的Schedule函数返回了一个JobHandle对象。和之前有点像,只不过是用一个称之为Job的东西把逻辑封装了。
RotationSpeedJob继承了IJobForEach接口,然后实现了Execute 函数。
Execute函数里做的事情和之前类似,修改实体的旋转值。
但是,这里有一点不一样,很重要的一点——Execute里没有使用Entities.ForEach了。
是的,每一个IJobForEach,只会处理一个实体的组件对象。
并且,这些Job是可以并行处理的,这就是JobComponentSystem配合IJobForEach的优势——并行。我们来理一理:
a. JobComponentSystem的OnUpdate函数的主要逻辑移到了RotationSpeedJob中处理。
b. JobHandle对象需要通过IJobForEach来返回,实际上能返回JobHandle对象的不仅仅是IJobForEach这种接口,还有其他的,即,JobComponentSystem可以和若干种不同类型的Job配合。
c. IJobForEach接口需要实现Execute函数,函数参数指定了需要筛选的组件类型(可多个),以此来获得所需实体的组件对象,然后进行读写操作。指定的组件类型需要和接口声明的泛型对应,比如我们的接口声明的泛型是: IJobForEach<Rotation, RotationSpeed_ForEach>,和Execute函数的参数类型是对应的。
d. Execute函数只能对单个实体的组件(组件可以多个)进行操作。
e. IJobForEach,我们称之为Job,Job是可以并行执行的(多线程),因此能提升性能。
f. 请注意,Execute的参数可以带有ReadOnly特性,代表获取的这个类型的组件是只读的,不能进行写操作。
g. 如果某个Job对某个组件对象进行了写操作,则其他需要读取这个组件对象的Job无法并行执行,相当于锁住了。
关于JobSystem的知识,我现在还没有掌握,我会在后续的教程里和大家分享。
2.运行
好了,看看效果吧,除了System发生了改变,其他内容没变,大家修改了System类后,运行程序,结果是是一样的。
3.要注意的地方,重要!
利用IJobForEach时,系统会自动并行处理,但是,它只会按照块(Chunk)来并行处理。
什么是块(Chunk)?因为这是一个很重要的概念,我会在下一篇单独讲(重要,但理解起来很简单)。
虽然现在大家还没有块的概念,但是可以先注意一下。
如果我们的实体数量很少,筛选出来的实体刚好都在一个块里。
那么,IJobForEach其实是没有帮助的,因为它是按照块为单位进行并行处理的,只有一个块的话,也就不存在并行了。
大家看看下一篇关于块(Chunk)的解释,就能理解这段话的意思了。
注意,本系列教程基于DOTS相关预览版的Package包,是预览版,不代表正式版的时候也适用。
转发本系列文章的朋友请注意,带上原文链接和原文日期,避免误导未来使用正式版的开发者。