Querying for data using a EntityQuery:使用EntityQuery查询实体
读取或写入数据的第一步是找到该数据. ECS框架中的数据存储在组件中,组件根据其类型和实体一起分配在内存块中. 如果想要你程序中需要的数据,就需要EntityQuery来查询.
创建EntityQuery结构体之后, 你可以:
- 运行一个job 来访问你查询到的实体和组件
- 获得一个NativeArray包含所有选择的实体
- 获得一个 NativeArrays 包含所有选择的组件(通过组件类型来获得by component type)
EntityQuery 在检索entity 和 component arrays数组时是并行进行的,所以同一个实体在这两个数组中的索引是一样的,就相当于块中每一行都有两个组件,并行排列
Note: ComponentSystem.Entites.ForEach
委托和 IJobForEach 是基于组件类型和组件的属性创建的EntityQueries
Defining a query:定义一个query
EntityQuery 定义了一个实体原型当中必须有的组件类型或者是没有的组件类型.
对于简单查询,可以基于组件类型数组创建EntityQuery. 面的示例定义了一个EntityQuery,它查找所有具有RotationQuaternion和RotationSpeed组件的实体。
EntityQuery m_Query = GetEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>());
使用 ComponentType.ReadOnly<T> 而不是简单的
typeof
指定系统不写入RotationSpeed. 这样对数据的访问约束较少,也就是job只需要读,不需要写,能够更有效地执行job
EntityQueryDesc
EntityQueryDesc 能够创建给复杂的EntityQuery. EntityQueryDesc提供了一种灵活的查询机制,可以根据以下组件集指定要选择的原型:
All
= All component types in this array must exist in the archetypeAny
= At least one of the component types in this array must exist in the archetypeNone
= None of the component types in this array can exist in the archetype
例如,下面的查询包含包含 RotationQuaternion and RotationSpeed components, but excludes any archetypes containing the Frozen component:
var query = new EntityQueryDesc
{None = new ComponentType[]{ typeof(Frozen) },All = new ComponentType[]{ typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>() }
}
EntityQuery m_Query = GetEntityQuery(query);
Note: ArchetypeChunk.Has<T>()
方法确定块是否包含可选组件.
Query options
当你创建一个 EntityQueryDesc, 您可以设置它的选项变量. 这些选项允许进行专门的查询 (通常不需要设置它们):
- Default — no options set; the query behaves normally.
- IncludePrefab — includes archetypes containing the special Prefab tag component.
- IncludeDisabled — includes archetypes containing the special Disabled tag component.
- FilterWriteGroup — considers the WriteGroup of any components in the query.
当你 设置FilterWriteGroup 选项, 只有含有在Write Group的组件的实体才会被选上,具有来自相同WriteGroup的任何附加组件的实体将被排除。
假设C2和C3是基于C1的同一WriteGroup中的组件,您使用FilterWriteGroup选项创建了一个查询,该选项需要C1和C3:
public struct C1: IComponentData{}[WriteGroup(C1)]
public struct C2: IComponentData{}[WriteGroup(C1)]
public struct C3: IComponentData{}// ... In a system:
var query = new EntityQueryDesc{All = new ComponentType{typeof(C1), ComponentType.ReadOnly<C3>()},Options = EntityQueryDescOptions.FilterWriteGroup
};
var m_Query = GetEntityQuery(query);
这个查询排除了C2和C3的任何实体,因为C2没有显式地包含在查询中. While you could design this into the query using None
, doing it through a Write Group provides an important benefit:您不需要更改其他系统使用的查询(只要这些系统也使用Write Groups).
Write Groups are a mechanism that allow you to extend existing systems. For example, if C1 and C2 are defined in another system (perhaps part of a library that you don't control), you could put C3 into the same Write Group as C2 in order to change how C1 is updated. For any entities to which you add your C3 component, your system will update C1 and the original system will not. For other entities without C3, the original system will update C1 as before.
See Write Groups for more information.
Combining queries
您可以通过传递一个EntityQueryDesc对象数组而不是一个实例来组合多个查询。每个查询都使用一个逻辑或操作进行组合。 下面的示例选择一个包含RotationQuaternion组件或RotationSpeed组件(或两者)的原型:
var query0 = new EntityQueryDesc
{All = new ComponentType[] {typeof(RotationQuaternion)}
};var query1 = new EntityQueryDesc
{All = new ComponentType[] {typeof(RotationSpeed)}
};EntityQuery m_Query = GetEntityQuery(new EntityQueryDesc[] {query0, query1});
Creating a EntityQuery
在 system class系统类外部, EntityManager.CreateEntityQuery()
方法创建EntityQuery
EntityQuery m_Query = CreateEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>());
但是, 在 system class系统类内部, 必须使用 GetEntityQuery()
方法:
public class RotationSpeedSystem : JobComponentSystem
{private EntityQuery m_Query;protected override void OnCreate(){m_Query = GetEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>());}//…
}
当您计划重用同一个查询到的东西时,您应该缓存EntityQuery实例, 如果可能,而不是为每个使用创建一个新的. 例如,在一个系统中,您可以在系统的OnCreate()函数中创建EntityQuery,并将结果存储在一个实例变量中。以上示例中的m_Query变量用于此目的。
Defining filters
除了定义哪些组件必须包含或排除在查询之外,您还可以筛选那种类型的组件。你可以指定以下类型的过滤器:
- Shared component values —选择拥有shared component的特定值的实体
- Change filter —选择组件类型的值是否可能发生改变的实体
Shared component filters
要使用shared component filter, 首先要在EntityQuery包含shared component (以及其他需要的组件), 然后调用 SetFilter()
方法, 传入与ISharedComponent类型相同的结构,该结构包含要选择的值. 所有值必须匹配 ,你可以向filter至多添加两个不同的 shared components.
您可以随时更改过滤器, 但是更改filter并不能更改数组中已存在的实体或者组件,这些实体或者组件是你通过 ToComponentDataArray()
or ToEntityArray()
方法获得的. 你必须重新创建这些数组.
下面定义了一个 SharedGrouping 的shared component,系统仅仅访问拥有Group字段,且值为1的实体
struct SharedGrouping : ISharedComponentData
{public int Group;
}class ImpulseSystem : ComponentSystem
{EntityQuery m_Query;protected override void OnCreate(int capacity){m_Query = GetEntityQuery(typeof(Position), typeof(Displacement), typeof(SharedGrouping));}protected override void OnUpdate(){// Only iterate over entities that have the SharedGrouping data set to 1m_Query.SetFilter(new SharedGrouping { Group = 1 });var positions = m_Query.ToComponentDataArray<Position>(Allocator.Temp);var displacememnts = m_Query.ToComponentDataArray<Displacement>(Allocator.Temp);for (int i = 0; i != positions.Length; i++)positions[i].Value = positions[i].Value + displacememnts[i].Value;}
}
Change filters
如果一个组件的值更改了,这时你只需要更新你的实体,你可以 调用 SetFilterChanged()
方法把新的组件添加到EntityQuery filter中,比如, 例如,下面的EntityQuery只包含另一个系统已经写入转换组件的块中的实体:
protected override void OnCreate(int capacity)
{m_Query = GetEntityQuery(typeof(LocalToWorld), ComponentType.ReadOnly<Translation>());m_Query.SetFilterChanged(typeof(Translation));
}
注意,为了提高效率,更改过滤器应用于整个块,而不是单个实体。 change filter 还会检查系统是否声明了对组件的写入,即使没有真正的修改组件的数据,这个也会算数,也就是说如果一个job已经声明了那个组件是可写入的,则认为他已经更改了(这也是对不需要写入的组件声明为只读的另一个原因)
Executing the query
EntityQuery 当你在job中使用它或者你调用他其中的方法返回一个实体数组或者组件数组或者块数组的时候,就会执行它的查询:
ToEntityArray()
返回符合条件的实体数组.ToComponentDataArray<T>
返回选择实体上的组件数组.CreateArchetypeChunkArray()
返回包含选择的实体的块 (因为查询是基于原型, archetypes, shared component values, 、change filters这些属性在同一个块中的实体上都是相同的,存储在返回的块集中的实体集与ToEntityArray()返回的实体集完全相同.)
In Jobs
在 JobComponentSystem, 给系统的 Schedule()
method方法 传递EntityQuery object对象
// OnUpdate runs on the main thread.
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{var rotationType = GetArchetypeChunkComponentType<Rotation>(false); var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true);var job = new RotationSpeedJob(){RotationType = rotationType,RotationSpeedType = rotationSpeedType,DeltaTime = Time.deltaTime};return job.Schedule(m_Query, inputDependencies);
}
ntityQuery在内部使用作业创建所需的数组。当您将组传递给Schedule()方法时,EntityQuery作业与系统自己的作业一起被调度,并且可以利用并行处理。