UnityECS学习02-IJobEntity
改造
首先我们创建一个 ExecuteAuthoring
类,将其挂载到 SubScene
的一个空的 gameObject
之上。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71namespace HelloCube.Execute
{
public class ExecuteAuthoring : MonoBehaviour
{
public bool MainThread;
public bool IJobEntity;
public bool Aspects;
public bool Prefabs;
public bool IJobChunk;
public bool Reparenting;
public bool EnableableComponents;
public bool GameObjectSync;
class Baker : Baker<ExecuteAuthoring>
{
public override void Bake(ExecuteAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.None);
if (authoring.MainThread) AddComponent<MainThread>(entity);
if (authoring.IJobEntity) AddComponent<IJobEntity>(entity);
if (authoring.Aspects) AddComponent<Aspects>(entity);
if (authoring.Prefabs) AddComponent<Prefabs>(entity);
if (authoring.IJobChunk) AddComponent<IJobChunk>(entity);
if (authoring.Reparenting) AddComponent<Reparenting>(entity);
if (authoring.EnableableComponents) AddComponent<EnableableComponents>(entity);
if (authoring.GameObjectSync) AddComponent<GameObjectSync>(entity);
}
}
}
public struct MainThread : IComponentData
{
}
public struct IJobEntity : IComponentData
{
}
public struct Aspects : IComponentData
{
}
public struct Prefabs : IComponentData
{
}
public struct IJobChunk : IComponentData
{
}
public struct Reparenting : IComponentData
{
}
public struct EnableableComponents : IComponentData
{
}
public struct GameObjectSync : IComponentData
{
}
}
此处我们可以发现:这里 GetEntity
使用的是 TransformUsageFlags.None
,而前面使用的是 TransformUsageFlags.Dynamic
。两者的区别在于:
Dynamic
:表明entity
需要在运行时移动必要的transform
组件,如:LocalTransform
,LocalToWorld
。None
:表明entity
不需要transform
也就是在此处我们的 var entity = GetEntity(TransformUsageFlags.None);
在 Bake
的时候获取了全部的实体,然后根据我们的选择,对其打上不同的组件,各个系统根据这些组件选择运行,也就做到了我们快速切换此 ECS 架构执行的 System
。
改造成 IJobEntity
的 System
如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31namespace HelloCube.JobEntity
{
public partial struct RotationSystem : ISystem
{
[ ]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<Execute.IJobEntity>();
}
[ ]
public void OnUpdate(ref SystemState state)
{
var job = new RotationJob() { deltaTime = SystemAPI.Time.DeltaTime };
job.Schedule();
// state.Dependency = job.Schedule(state.Dependency); 此处与 job.Schedule(); 等价
}
}
[ ]
partial struct RotationJob : IJobEntity
{
public float deltaTime;
// ref 是方法内可变引用,in 是方法类不可变引用
private void Execute(ref LocalTransform transform, in RotationSpeed speed)
{
transform = transform.RotateY(speed.RadiansPerSecond * deltaTime);
}
}
}
相比较于之前的,可以看到,其是把旋转这个任务提取了出来。只需使用 RotationJob
继承 IJobEntity
。然后在 Execute
中编写逻辑即可。观察发现:
- 此处的
ref
对应上一次的RefRW
,也就是读写引用访问 - 此处的
in
对应上一次的RefRO
,也就是只读引用访问
接着在 System
的 OnUpdate
中使用 Schedule
执行即可。此处的 Schedule
有两种写法,此处等价:
job.Schedule();
:将IJobEntity
实例对象添加到任务调度队列(job scheduler queue),以便顺序(非并行)执行。自动使用系统的Dependency
属性作为输入和输出依赖性。state.Dependency = job.Schedule(state.Dependency);
:将IJobEntity
实例对象添加到任务调度队列,顺序(非并行)执行。其参数接受一个JobHandle dependsOn
,为了用来标识已经调度的作业(作业指实现了IJobEntity
接口的任务)。这个标识的用处为:如果一个任务在写入一个组件,那么其不能与其他读取或写入相同组件的作业并行执行。只读取相同组件的作业可以并行执行,也就是避免竞态条件。
更多请查阅 官方文档 Method Schedule
SystemAPI.Query+foreach 和 IJobEntity 的比较
Iterate over component data with SystemAPI.Query
Iterate over component data with IJobEntity
以上是两种迭代组件数据方式的官网文档。其中,SystemAPI.Query
有一句话:
You can’t store in a variable and then use it in multiple statements: there isn’t a way to reuse . This is because the implementation of the API relies on knowing what the query types are at compile time. The source-generation solution doesn’t know at compile-time what to generate and cache, which type handles to call on, nor which dependencies to complete.
相应的 IJobEntity
当中也有相关的一句话:
IJobEntity
is similar to Entities.ForEach
, however you can reuse throughout several systems, so you should use it over where possible.
这两段话都是在表达一个意思:缓存和重用:
SystemAPI.Query
不能将其存储在一个变量然后在多个语句中使用(也就是缓存)。因为其没有办法重用,因为 API 的实现依赖于在编译时了解查询的类型,源代码在编译的时候不知道要生成什么和缓存什么,要调用哪些类型句柄,也不知道要完成哪些依赖项。IJobEntity
则表明可以在多个系统中重用。DOTS 会自动为我们处理。
而且 IJobEntity
是支持使用 ScheduleParallel
来并行处理,而 SystemAPI.Query+foreach
只能顺序(非并行)执行。因此建议是尽可能多的使用 IJobEntity
。