Temp Queries
February 19, 2025 · View on GitHub
Temp Queries are entity queries that can be constructed and evaluated within
jobs. While not as performant as a cached EntityQuery from a system, Temp
Queries can come in handy when you can’t easily predict which queries you need
in advance. This is most common in scripting systems such as Unika.
Creating a Query
You will need two things in your job in order to create and evaluate your query.
First, you need a NativeArray<EntityArchetype>.
struct TempQueryExampleJob : IJob
{
public NativeArray<EntityArchetype> archetypes;
public EntityStorageInfoLookup entityStorageInfoLookup;
You can obtain these from EntityManager and SystemAPI respectively.
EntityStorageInfoLookup can also be found inside a ComponentBroker.
var archetypes = new NativeList<EntityArchetype>(state.WorldUpdateAllocator);
state.EntityManager.GetAllArchetypes(archetypes);
var job = new TempQueryExampleJob
{
entityStorageInfoLookup = SystemAPI.GetEntityStorageInfoLookup(),
archetypes = archetypes.AsArray()
};
Next, in your job, you can construct a new TempQuery and pass in the two
arguments we’ve already created. The remainder of the arguments are used to
describe the query. They use the “All”, “Any”, and “None” categorization, as
well as EntityQueryOptions. Component enabled states are always ignored in
Temp Queries. You must specify at least 1 required component. The rest of the
arguments are optional.
Components of each category are specified via a ComponentTypeSet.
ComponentTypeSet requires quite a bit of boilerplate to populate. You can use
TypePack<> which implicitly casts to ComponentTypeSet to reduce it.
var query = new TempQuery(archetypes, entityStorageInfoLookup, new TypePack<WorldTransform, LocalTransform, Parent>());
Evaluating a Query
Once you have a TempQuery instance, you can easily iterate various details of
it. The most common use case is to iterate the entities that match the query.
foreach (var entity in query.entities)
{
// ...
You can also access the query’s archetypes and chunks. It is up to you to
use ComponentLookup, ComponentTypeHandle, or ComponentBroker to access
other data on the entities.
Extending Evaluation
The query evaluation API is designed to be easily extendable so that you can provide your own filtering or other logic.
A query can produce a TempArchetypeEnumerator which implements the
ITempArchetypeEnumerator interface. You can define your own implementation of
this interface and use the convenient MatchesArchetype() method on a
TempQuery. TempArchetypeEnumerator excludes archetypes with zero chunks for
performance.
TempChunkEnumerator can wrap any ITempArchetypeEnumerator and iterate
through the chunks in each provided archetype. This type implements the
ITempChunkEnumerator interface.
Next, there’s TempMaskedChunkEnumerator which wraps any
ITempChunkEnumerator. This type produces MaskedChunk instances, which each
contain an ArchetypeChunk, a v128 bitmask, and a bool specifying whether
to use the bitmask. These have the same meaning as in IJobChunk. Note that the
provided TempMaskedChunkEnumerator does no filtering and always specifies the
useEnabledMask to false. To perform filtering, you would want to replace it
with an alternative implementation of the ITempMaskedChunkEnumerator
interface.
Lastly, TempEntityEnumerator can wrap any ITempMaskedChunkEnumerator. It
returns individual entities.
Thus, TempQuery.entities actually produces the following type:
TempEntityEnumerator<TempMaskedChunkEnumerator<TempChunkEnumerator<TempArchetypeEnumerator>>>
Performance Considerations
As stated above, TempQuery does not perform as well as Unity’s built-in cached
EntityQuery type. The biggest factor behind this is that TempQuery must scan
all archetypes to find ones that match. Future optimizations are possible. If
your use case suffers performance problems because of this, please report the
problems and specify your use case.
One universal way to mitigate this issue is to reduce the number of archetypes
your project generates. You can do this by adding and removing multiple
components at a time. Core provides extension methods to EntityManager and
EntityCommandBuffer to help with this.
Once you have a filtered list of archetypes, evaluating chunks and entities may be slightly slower than Unity’s implementations, but not significantly slower.