docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Iterate over component data with SystemAPI.Query

    To iterate through a collection of data on the main thread, you can use the SystemAPI.Query<T> method in both ISystem and SystemBase system types. It uses C#’s idiomatic foreach syntax.

    You can overload the method with up to seven type parameters. The supported type parameters are:

    • IAspect
    • IComponentData
    • ISharedComponentData
    • DynamicBuffer<T>
    • RefRO<T>
    • RefRW<T>
    • EnabledRefRO<T> where T : IEnableableComponent, IComponentData
    • EnabledRefRW<T> where T : IEnableableComponent, IComponentData

    SystemAPI.Query implementation

    Whenever you invoke SystemAPI.Query<T>, the source generator solution creates an EntityQuery field on the system itself. It also caches an EntityQuery that consists of the queried types, with their respective read-write/read-only access modes in this field. During compilation, the source-generation solution replaces the SystemAPI.Query<T> invocation in a foreach statement with an enumerator that iterates through the cached query’s data.

    Additionally, the source-generation solution caches all the required type handles, and automatically injects TypeHandle.Update(SystemBase system) or TypeHandle.Update(ref SystemState state) as necessary before every foreach. This ensures that the type handles are safe to use.

    The source generators also generate code to automatically complete all necessary read and read-write dependencies before each foreach statement.

    Query data

    The following is an example that uses SystemAPI.Query to iterate through every entity that has both LocalTransform and RotationSpeed components:

    public partial struct MyRotationSpeedSystem : ISystem
    {
    
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            float deltaTime = SystemAPI.Time.DeltaTime;
    
            foreach (var (transform, speed) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotationSpeed>>())
                transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.RadiansPerSecond * deltaTime);
        }
    }
    

    Because the example modifies the LocalTransform data, it's wrapped inside RefRW<T>, as a read-write reference. However, because it only reads the RotationSpeed data, it uses RefRO<T>. RefRO<T> usage is entirely optional and you can use the following instead as valid code:

    float deltaTime = SystemAPI.Time.DeltaTime;
    
    foreach (var (transform, speed) in SystemAPI.Query<RefRW<LocalTransform>, RotationSpeed>())
        transform.ValueRW = transform.ValueRO.RotateY(speed.RadiansPerSecond * deltaTime);
    

    RefRW<T>.ValueRW, RefRW<T>.ValueRO, and RefRO<T>.ValueRO all return a reference to the component. When called, ValueRW conducts a safety check for read-write access, and ValueRO does the same for read-only access.

    Accessing entities in the foreach statement

    Unity.Entities.Entity isn't a supported type parameter. Every query is already an implicit filter of all existing entities. To get access to the entity, use WithEntityAccess. For example:

    foreach (var (transform, speed, entity) in SystemAPI.Query<RefRW<LocalToWorld>, RefRO<RotationSpeed>>().WithEntityAccess())
    {
        // Do stuff;
    }
    

    Note that the Entity argument comes last in the returned tuple.

    Known limitations

    SystemAPI.Query has the following limitations, which are outlined below.

    Dynamic buffer read-only limitation

    DynamicBuffer<T> type parameters in SystemAPI.Query<T> are read-write access by default. However, if you want read-only access, you have to create your own implementation, similar to the following:

    var bufferHandle = state.GetBufferTypeHandle<MyBufferElement>(isReadOnly: true);
    var myBufferElementQuery = SystemAPI.QueryBuilder().WithAll<MyBufferElement>().Build();
    var chunks = myBufferElementQuery.ToArchetypeChunkArray(Allocator.Temp);
    
    foreach (var chunk in chunks)
    {
        var numEntities = chunk.Count;
        var bufferAccessor = chunk.GetBufferAccessor(ref bufferHandle);
    
        for (int j = 0; j < numEntities; j++)
        {
            var dynamicBuffer = bufferAccessor[j];
            // Read from dynamicBuffer and perform various operations
        }
    }
    

    Reusing SystemAPI.Query

    You can't store SystemAPI.Query<T> in a variable and then use it in multiple foreach statements: there isn't a way to reuse SystemAPI.Query. This is because the implementation of the API relies on knowing what the query types are at compile time. The source-generation solution doesn't know at compile-time what EntityQuery to generate and cache, which type handles to call Update on, nor which dependencies to complete.

    In This Article
    • SystemAPI.Query implementation
    • Query data
    • Accessing entities in the foreach statement
    • Known limitations
      • Dynamic buffer read-only limitation
      • Reusing SystemAPI.Query
    Back to top
    Copyright © 2024 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)