Unity ECS 框架摸索第六章:更灵活的筛选与遍历
前文中我们用 IJobForEach
或者 JobComponentSystem.Entities.ForEach
都只能筛选并遍历具有特定 Component 的 Entity,但其实有更灵活且同样高效的方式
EntityQuery
Unity 提供了 EntityQuery
用于精确筛选 Entity,并且文档中提到 IJobForEach
与 JobComponentSystem.Entities.ForEach
的内部实现其实都是 EntityQuery
GetEntityQuery
最简单的,我们可以筛选具有指定组件的实体,并规定这些组件的读写权限
例如下面的代码选出了全部的同时拥有 Translation
与 MoveSpeed
组件的 Entity,并且规定 Translation
可读可写,MoveSpeed
只读
EntityQuery m_Query = GetEntityQuery(typeof(Translation), ComponentType.ReadOnly<MoveSpeed>());
EntityQueryDesc
对于更复杂的筛选,我们使用 EntityQueryDesc
来创建 EntityQuery
,一个 EntityQueryDesc
对象具有三个属性,它们的类型都是 ComponentType
数组
All
:数组中所有的类型必须出现在 Entity 中,且可以在其中规定读写权限Any
:数组中至少一个类型会出现在 Entity 中None
:数组中任意类型都不会出现在 Entity 中
下面举个例子,筛选同时具有 Translation
、MoveSpeed
,不具有 RotationSpeed
,至少具有 Wing
或 Magic
中的一个的所有 Entity
var query = new EntityQueryDesc
{
All = new ComponentType[] { ComponentType.ReadOnly<MoveSpeed>(), typeof(Translation) },
None = new ComponentType[] { typeof(RotationSpeed) },
Any = new ComponentType[] { typeof(Magic), typeof(Wing) }
};
EntityQuery m_Group = GetEntityQuery(query);
联合筛选
我们可以在调用 GetEntityQuery
函数时传入 EntityQueryDesc
数组,数组中的筛选条件是或的关系,下面是例子
var query1 = new EntityQueryDesc
{
All = new ComponentType[] { ComponentType.ReadOnly<MoveSpeed>(), typeof(Translation), typeof(Wing) },
None = new ComponentType[] { typeof(RotationSpeed) }
};
var query2 = new EntityQueryDesc
{
All = new ComponentType[] { ComponentType.ReadOnly<MoveSpeed>(), typeof(Translation), typeof(Magic) }
};
m_Group = GetEntityQuery(new EntityQueryDesc[] { query1, query2 });
Shared Component 过滤
可以对筛选出的 Entity 拥有的指定 Shared Component 进行过滤,保证过滤出的 Entity 拥有的指定 Shared Component 均为特定值
例如下面代码用一个 Shared Component 作为分组标记,过滤出 GroupId
为 5 的所有 Entity
public struct SharedGroup : ISharedComponentData {
public int GroupId;
}
public class FlySystem : JobComponentSystem
{
[BurstCompile]
struct FlyJob : IJobForEach<MoveSpeed, Translation>
{
public void Execute([ReadOnly]ref MoveSpeed moveSpeed, ref Translation translation)
{
var v = translation.Value;
v.y += moveSpeed.MetrePerSecond;
translation = new Translation { Value = v };
}
}
EntityQuery m_Group;
protected override void OnCreate()
{
m_Group = GetEntityQuery(ComponentType.ReadOnly<SharedGroup>(), ComponentType.ReadOnly<MoveSpeed>(), typeof(Translation));
m_Group.AddSharedComponentFilter(new SharedGroup());
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
m_Group.SetSharedComponentFilter(new SharedGroup { GroupId = 4 });
return new FlyJob().Schedule(m_Group, inputDeps);
}
}
解释:
当前版本官方手册中这个地方没有更新,上面代码中把 AddSharedComponentFilter
放在 OnCreate
,SetSharedComponentFilter
放在 OnUpdate
的用法是我瞎猜的。
当前版本的官方手册用的是之前版本的,没有这两个函数。
另外我碰巧发现,就算没有 Add,直接 Set 它也会自动 Add。
版本修改过滤
过滤出指定组件被修改过的
同样也有 Add 和 Set 两个版本,官方手册同样暂时还没有更新。
然后下面的代码用于过滤出 Translation
组件经过修改的实体。
当然是我瞎猜的也没有试(反正 API 还会改)。
m_Group.AddChangedVersionFilter(typeof(Translation));
手动遍历
这种方式可以遍历所有的 Entity,可以灵活访问任意 Entity 的任意 Component,但显然效率低下,官方手册说蛇皮操作推荐用这个
手册说用 IJobParallelFor
,懒得写了(反正 API 也会改)(看着语法提示干就完了,奥利给)