Entities - 遍历与查询
DOTS 程序的三大主要工作:
- 设计与组织数据
- 遍历与查找数据
- 修改与更新数据
在【Component 类型】中说过可以将 Component 按照数据访问类型进行划分,分别是 Entity,Chunk,Element,数据与查询中也是如此
1、遍历查询 Entity 数据的 5 种方式
- SystemAPI.Query + ForEach
SystemAPI.Query 用于在合集中过滤出我们需要的子集,然后通过 ForEach 遍历,Query中可以有七种重载查询类型:
但是Query 有两个使用限制:
(1)DynamicBuffer 只读限制:默认为读写访问,如果希望只读,必须自己实现
(2)不能存储 SystemAPI.Query<T>(),然后在一个或多个 foreach 语句中使用它(这是因为SytemAPI.Query 的模板类型是要在编译时确定并生成代码的,也就是说不能用分支语句去控制 SystemAPI.Query 语句的查询结果)
(3)只能在主线程运行
- IJobEntity
这是一种隐式查询后遍历的一种方式
(1)与Entities.ForEach功能类似,为每个Entity调用一次Execute
(2)可在多个System中重用
(3)底层是IJobChunk实现的
(4)查询方式WithAll、WithAny、WithNone、WithChangeFilter、WithOptions
(5)可以通过 【EntityIndexInQuery】 属性来在 Execute 方法中获取 Entity 遍历查询中的索引
[BurstCompile]
public partial struct CopyPostion : IJobEntity
{public NativeArray<float3> copyPositions;public void Execute([EntityIndexInQuery] int entityIndexInQuery, in LocalTransform localTransform){copyPositions[entityIndexInQuery] = localTransform.Position;}
}
- IJobChunk
(1)遍历ArcheType Chunk
(2)为每个Chunk调用一次Execute
(3)一般用在不需要遍历每个Chunk中Entity的情况或者对Chunk内的Entity执行多次遍历或以不寻常顺序遍历的情况
(4)useEnabledMask与ChunkEnableMask来辅助过滤Enableable Component未激活的Entity
注意第(4)点,IJobEntity 和 Entities.ForEach 遍历时会自动跳过与其 EnableableComponent 状态不匹配的 Entity,但是 IJobChunk 不会
- Entities.ForEach
(1)只用于继承SystemBase创建的System
(2)定义是一个Lambda表达式
(3)有太多的使用限制
- Manually
(1)entityManager.GetAllEntities()
(2)entityManager.GetAllChunks()
再通过 Foreach 方法去遍历上面两个接口得到的合集,设置限制条件查询。这种方法使用限制最小,但从性能角度来说是不划算的
举个例子:
以【Entity的创建模式】中的旋转方块的例子为基础,这次设置三种不同颜色的方块,并且每个颜色的方块写一个旋转 System,可以发现方块旋转的非常快,通过 Systems 可以看到,每个 System 处理的 Entity 数量都是 9 个,相当于每个 System 都在对每个颜色的方块进行旋转,每一帧进行了三次旋转处理。
这个时候为了区分每个颜色的方块,就可以用 Tag Component 来分类每个颜色的方块,Tag Component 不包含数据,可以在 Archetype 窗口中看到这些 Component 占了 0 字节,以其中一个为例:
struct RedCubeTag:IComponentData
{
}
public class RedCubeTagAuthoring : MonoBehaviour
{public class Baker:Baker<RedCubeTagAuthoring>{public override void Bake(RedCubeTagAuthoring authoring){var redCube =new RedCubeTag();AddComponent(redCube);}}
}
然后更新 System 中的 Update
[BurstCompile]
public void OnUpdate(ref SystemState state)
{float deltaTime = SystemAPI.Time.DeltaTime;foreach (var (transform, speed, tag)) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotateSpeedComponent>, RefRO<RedCubeTag>>()){transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.rotateSpeed * deltaTime);}
}
还有一种情况,如果要查询所有 Component A 与 C 的 chunk
那么通过 SystemAPI.Query<A,C>(),包含 A,B,C的 chunk 也会被获取,如果要获取只包含 A,C组件的 chunk,那么可以通过创建自定义的 query,并且可以通过多线程处理
new EntityQueryBuilder(Allocator.Temp) .WithAll<A>0 .WithAll<C>() .WithNone<B>() .Build(this).