docs.unity3d.com
    Show / Hide Table of Contents

    Querying data with EntityQuery

    To read or write data, you must first find the data you want to change. ECS stores data in Components, which it groups together in memory by the Archetype of the Entity to which they belong. To get a view of the ECS data that contains only the specific data you need for a given algorithm or process, use EntityQuery.

    You can use EntityQuery to do the following:

    • Run a job to process the selected Entities and Components
    • Get a NativeArray that contains all the selected Entities
    • Get NativeArrays of the selected Components (by Component type)

    The Entity and Component arrays that EntityQuery returns are parallel. This means that the same index value always applies to the same Entity in any array.

    Note

    The SystemBase Entities.ForEach constructions create internal EntityQuery instances based on the Component types and attributes you specify for these APIs. You can't use a different EntityQuery object with Entities.ForEach. However, you can get the query object that an Entities.ForEach instance creates and use it elsewhere.

    Defining a query

    An EntityQuery query defines the set of Component types that an Archetype must contain for ECS to include its chunks and Entities in the view. You can also exclude Archetypes that contain specific types of Components.

    For simple queries, you can create an EntityQuery based on an array of Component types. The following example defines an EntityQuery that finds all Entities with both RotationQuaternion and RotationSpeed Components:

    
    EntityQuery query
        = GetEntityQuery(typeof(RotationQuaternion),
                         ComponentType.ReadOnly<RotationSpeed>());
    

    The query uses ComponentType.ReadOnly<T> instead of the typeof expression to show that the System doesn't write to RotationSpeed. Always specify read-only when possible, because there are fewer constraints on read access to data, which can help the job scheduler execute the jobs more efficiently.

    EntityQueryDesc

    For more complex queries, you can use an EntityQueryDesc object to create the EntityQuery. An EntityQueryDesc provides a flexible query mechanism to specify which Archetypes to select based on the following sets of Components:

    • All: All Component types in this array must exist in the Archetype
    • Any: At least one of the Component types in this array must exist in the Archetype
    • None: None of the Component types in this array can exist in the Archetype

    For example, the following query includes Archetypes that contain the RotationQuaternion and RotationSpeedComponents, but excludes any Archetypes that contain the Frozen Component:

    
    var queryDescription = new EntityQueryDesc
    {
        None = new ComponentType[] { typeof(Static) },
        All = new ComponentType[]{ typeof(RotationQuaternion),
                               ComponentType.ReadOnly<RotationSpeed>() }
    };
    EntityQuery query = GetEntityQuery(queryDescription);
    
    Note

    Don't include optional Components in the EntityQueryDesc. To handle optional Components, use the ArchetypeChunk.Has method to determine whether a chunk contains the optional Component or not. Because all Entities within the same chunk have the same Components, you only need to check whether an optional Component exists once per chunk: not once per Entity.

    Query options

    When you create an EntityQueryDesc, you can set its Options variable which is for specialized queries:

    • Default: No options set; the query behaves as expected.
    • IncludePrefab: Includes Archetypes that contain the special Prefab tag Component.
    • IncludeDisabled: Includes Archetypes that contain the special Disabled tag Component.
    • FilterWriteGroup: Considers the WriteGroup of any Components in the query.

    When you set the FilterWriteGroup option, ECS includes only Entities with Components in a WriteGroup that are explicitly included in the query in the view. ECS excludes any Entities that have any additional Components from the same WriteGroup.

    In the following example C2 and C3 are Components in the same WriteGroup based on C1. This query uses the FilterWriteGroup option that requires C1 and C3:

    
    public struct C1 : IComponentData { }
    
    [WriteGroup(typeof(C1))]
    public struct C2 : IComponentData { }
    
    [WriteGroup(typeof(C1))]
    public struct C3 : IComponentData { }
    
    public partial class ECSSystem : SystemBase
    {
        protected override void OnCreate()
        {
            var queryDescription = new EntityQueryDesc
            {
                All = new ComponentType[] { ComponentType.ReadWrite<C1>(),
                                            ComponentType.ReadOnly<C3>() },
                Options = EntityQueryOptions.FilterWriteGroup
            };
            var query = GetEntityQuery(queryDescription);
        }
    
        protected override void OnUpdate()
        {
            throw new NotImplementedException();
        }
    }
    

    This query excludes any Entities with both C2 and C3 because C2 isn't explicitly included in the query. While you can use None to design this into the query, doing it through a Write Group provides an important benefit: you don't need to change the queries other Systems use (as long as these Systems also use Write Groups).

    Write Groups are a mechanism that you can use 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 can put C3 into the same Write Group as C2 to change how C1 is updated. For any Entities which you add to the C3 Component, the System updates C1 and the original System doesn't. For other Entities without C3, the original System updates C1 as before.

    For more information, see Write Groups.

    Combining queries

    To combine multiple queries, you can pass an array of EntityQueryDesc objects rather than a single instance. You must use a logical OR operation to combine each query. The following example selects any Archetypes that contain a RotationQuaternion Component or a RotationSpeed Component (or both):

    
    var desc1 = new EntityQueryDesc
    {
        All = new ComponentType[] { typeof(RotationQuaternion) }
    };
    
    var desc2 = new EntityQueryDesc
    {
        All = new ComponentType[] { typeof(RotationSpeed) }
    };
    
    EntityQuery query
        = GetEntityQuery(new EntityQueryDesc[] { desc1, desc2 });
    
    

    Creating an EntityQuery

    Inside a System class, you get a query from the System rather than creating it from scratch. Systems cache any queries that your implementation creates and return the cached instance rather than creating a new one when possible.

    When your System uses Entities.ForEach, use WithStoreEntityQueryInField to get an instance of the query used by an Entities.ForEach construction:

    
    public partial class RotationSpeedSys : SystemBase
    {
        private EntityQuery query;
    
        protected override void OnUpdate()
        {
            float deltaTime = Time.DeltaTime;
    
            Entities
                .WithStoreEntityQueryInField(ref query)
                .ForEach(
                (ref RotationQuaternion rotation, in RotationSpeed speed) => {
                    rotation.Value
                        = math.mul(
                            math.normalize(rotation.Value),
                                quaternion.AxisAngle(math.up(),
                                    speed.RadiansPerSecond * deltaTime)
                         );
                })
                .Schedule();
        }
    }
    

    In other cases, such as when you need an instance of a query to schedule an IJobChunk job, use the GetEntityQuery function:

    
    public partial class RotationSystem : SystemBase
    {
        private EntityQuery query;
    
        protected override void OnCreate()
        {
            query = GetEntityQuery(typeof(RotationQuaternion),
                   ComponentType.ReadOnly<RotationSpeed>());
        }
    
        protected override void OnUpdate()
        {
            throw new NotImplementedException();
        }
    }
    

    Note that filter settings aren't considered when caching queries. Also, if you set filters on a query, ECS sets the same the next time you access that same query with GetEntityQuery. Use ResetFilter to clear any existing filters.

    Defining filters

    Filters exclude Entities that ECS would otherwise include among those returned by a query based on the following:

    • Shared Component filter: Filter the set of Entities based on specific values of a shared Component.
    • Change filter: Filter the set of Entities based on whether the value of a specific Component type has changed.

    The filters you set remain in effect until you call ResetFilter on the query object.

    Note

    Write Groups use a different mechanism. See Query options.

    Shared Component filters

    To use a shared Component filter, include the shared Component in the EntityQuery - along with other needed Components - and call the SetSharedComponentFilter function. Then pass in a struct of the same ISharedComponent type that contains the values to select. All values must match. You can add up to two different shared Components to the filter.

    You can change the filter at any time, but if you change the filter, it doesn't change any existing arrays of Entities or Components that you received from the group ToComponentDataArray<T> or ToEntityArray functions. You must recreate these arrays.

    The following example defines a shared Component named SharedGrouping and a System that only processes Entities that have the Group field set to 1.

    
    struct SharedGrouping : ISharedComponentData
    {
        public int Group;
    }
    
    partial class ImpulseSystem : SystemBase
    {
        EntityQuery query;
    
        protected override void OnCreate()
        {
            query = GetEntityQuery(typeof(Position),
                typeof(Displacement),
                typeof(SharedGrouping));
        }
    
        protected override void OnUpdate()
        {
            // Only iterate over entities that have the SharedGrouping data set to 1
            query.SetSharedComponentFilter(new SharedGrouping { Group = 1 });
    
            var positions = query.ToComponentDataArray<Position>(Allocator.Temp);
            var displacements = query.ToComponentDataArray<Displacement>(Allocator.Temp);
    
            for (int i = 0; i < positions.Length; i++)
                positions[i] =
                    new Position
                    {
                        Value = positions[i].Value + displacements[i].Value
                    };
        }
    }
    
    

    Change filters

    If you only need to update Entities when a Component value has changed, you can use the SetChangedVersionFilter function to add that Component to the EntityQuery filter. For example, the following EntityQuery only includes Entities from chunks that another System has already written to the Translation Component:

    
    EntityQuery query;
    
    protected override void OnCreate()
    {
        query = GetEntityQuery(typeof(LocalToWorld),
                ComponentType.ReadOnly<Translation>());
        query.SetChangedVersionFilter(typeof(Translation));
    
    }
    
    Note

    For efficiency, the change filter applies to whole chunks, not individual Entities. The change filter also only checks whether a System has run that declared write access to the Component, not whether it actually changed any data. In other words, if another job which had the ability to write to that Component type accesses the chunk, then the change filter includes all Entities in that chunk. This is why you should always declare read-only access to Components that you don't need to modify.

    Executing the query

    Typically, you execute a query when you schedule a job that uses it. You can also call one of the EntityQuery methods that returns arrays of Entities, Components, or chunks:

    • ToEntityArray returns an array of the selected Entities.
    • ToComponentDataArray returns an array of the Components of type T for the selected Entities.
    • CreateArchetypeChunkArray returns all the chunks that contain the selected Entities. Because a query operates on Archetypes, shared Component values, and change filters, which are all identical for all the Entities in a chunk, the set of Entities stored in the returned set of chunks is the same as the set of Entities ToEntityArray returns.

    Queries in the Editor

    In the Editor, the following icon represents a query: . You’ll see this when you use the specific Entities windows and Inspectors. You can also use the Query window to see the Components and Entities that match the selected query.

    Back to top
    Terms of use
    Copyright © 2023 Unity Technologies — Terms of use
    • Legal
    • Privacy Policy
    • Cookies
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)
    "Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
    Generated by DocFX on 18 October 2023