Aspect

首先回顾一下我们最开始的 HelloCube 代码。

1
2
3
4
5
foreach (var (transform, speed) in 
SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotationSpeed>>())
{
transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.RadiansPerSecond * deltaTime);
}

此处就是通过 Query 一个一个查询组件,但是此时组件多了这个查询的条件就会特别长,此处就引入了 Aspect

Aspect 可以看成对 entity 一些组件的引用的 Wrapper ,也就是包装。Aspect 必须使用 readonly partial struct 声明接着继承 IAspect。其中只能含有一下字段:

  • 一个 entity 字段,用于存储其 ID。
  • RefRW<T>RefRO<T> 字段,其中 T 实现了 IDataComponent 接口。
  • EnabledRefRWEnabledRefRO 字段,用于访问实现了IEnableableComponent 接口的组件的启用状态。
  • DynamicBuffer<T> 字段,用于访问实现了 IBufferElementData 接口的缓冲区元素。
  • ISharedComponent 字段,用于读取共享组件的值,并且这个访问是只读的。
  • 其他 Aspect 类型。

最终我们的代码就可以修改成这样:

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
namespace HelloCube.Aspects
{
public partial struct RotationSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<Execute.Aspects>();
}

[BurstCompile]
public void OnUpdate(ref SystemState state)
{
float deltaTime = SystemAPI.Time.DeltaTime;

foreach (var rotate in SystemAPI.Query<RotateAspect>())
{
rotate.Rotate(deltaTime);
}
}
}

readonly partial struct RotateAspect : IAspect
{
readonly RefRW<LocalTransform> m_Transform;
readonly RefRO<RotationSpeed> m_Speed;

public void Rotate(float deltaTime)
{
m_Transform.ValueRW = m_Transform.ValueRO.RotateY(m_Speed.ValueRO.RadiansPerSecond * deltaTime);
}
}
}

在 OnUpdate 中就可以使用我们定义的 Aspect 组件来查询。其会自动判断每个 entity 满足其中所有字段组件的要求,并自动为其填充数据。

readonly struct

此处再提一下 Aspect 组件当中定义的 readonly struct

官方文档

其声明后主要有三个作用:

  • 任何字段声明都必须具有 readonly 修饰符。
  • 任何属性(包括自动实现的属性)都必须是只读的或仅 init
  • 除构造函数以外,其他实例成员都是 readonly 的。