docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Define and execute an Entities.ForEach lambda expression

    To use Entities.ForEach, you must pass it a lambda expression, which Unity uses to generate an entity query based on the lambda parameter types. When the generated job runs, Unity calls the lambda expression once for each entity that matches the query. ForEachLambdaJobDescription represents this generated job.

    When you define the Entities.ForEach lambda expression, you can declare parameters that the SystemBase class uses to pass in information about the current entity when it executes the method.

    A typical lambda expression looks like this:

    Entities.ForEach(
        (Entity entity,
            int entityInQueryIndex,
            ref ObjectPosition translation,
            in Movement move) => { /* .. */})
    

    You can pass up to eight parameters to an Entities.ForEach lambda expression. If you need to pass more parameters, you can define a custom delegate. For more information, refer to the section on Custom delegates in this document.

    When you use the standard delegates, you must group the parameters in the following order:

    1. Parameters passed-by-value (no parameter modifiers)
    2. Writable parameters (ref parameter modifier)
    3. Read-only parameters (in parameter modifier)

    You must use the ref or in parameter modify keyword on all components. If you don't, the component struct that Unity passes to your method is a copy instead of a reference. This means that it takes up extra memory for the read-ony parameters, and Unity silently throws any changes you make to the components when the copied struct goes out of scope after the function returns.

    If the lambda expression doesn't follow this order, and you haven't created a suitable delegate, the compiler provides an error similar to:

    error CS1593: Delegate 'Invalid_ForEach_Signature_See_ForEach_Documentation_For_Rules_And_Restrictions' does not take N arguments

    This error message cites the number of arguments as the issue even when though the problem is the parameter order.

    Custom delegates

    If you want to use more than eight arguments in a ForEach lambda expression, you must declare your own delegate type and ForEach overload. Declaring your own type means you can use an unlimited amount of arguments and put the ref, in, and value parameters in any order you want.

    You can also declare the three named parameters entity, entityInQueryIndex, and nativeThreadIndex anywhere in your parameter list. Don't use ref or in modifiers for these parameters.

    The following example shows 12 arguments, and uses the entity parameter within the lambda expression:

    
    static class BringYourOwnDelegate
    {
        // Declare the delegate that takes 12 parameters. T0 is used for the Entity argument
        public delegate void CustomForEachDelegate<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
            (T0 t0, in T1 t1, in T2 t2, in T3 t3, in T4 t4, in T5 t5,
             in T6 t6, in T7 t7, in T8 t8, in T9 t9, in T10 t10, in T11 t11);
    
        // Declare the function overload
        public static TDescription ForEach<TDescription, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
            (this TDescription description, CustomForEachDelegate<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> codeToRun)
            where TDescription : struct, Unity.Entities.CodeGeneratedJobForEach.ISupportForEachWithUniversalDelegate =>
            LambdaForEachDescriptionConstructionMethods.ThrowCodeGenException<TDescription>();
    }
    
    // A system that uses the custom delegate and overload
    [RequireMatchingQueriesForUpdate]
    public partial class MayParamsSystem : SystemBase
    {
        protected override void OnUpdate()
        {
            Entities.ForEach(
                    (Entity entity0,
                        in Data1 d1,
                        in Data2 d2,
                        in Data3 d3,
                        in Data4 d4,
                        in Data5 d5,
                        in Data6 d6,
                        in Data7 d7,
                        in Data8 d8,
                        in Data9 d9,
                        in Data10 d10,
                        in Data11 d11
                        ) => {/* .. */})
                .Run();
        }
    }
    
    

    Component parameters

    To access a component associated with an entity, you must pass a parameter of that component type to the lambda expression. The compiler automatically adds all components passed to the lambda expression to the entity query as required components.

    To update a component value, you must use the ref keyword in the parameter list to pass it to the lambda expression. Without the ref keyword, Unity makes any modifications to a temporary copy of the component.

    To declare a read-only component passed to the lambda expression use the in keyword in the parameter list.

    When you use ref, Unity marks the components in the current chunk as changed, even if the lambda expression doesn't actually change them. For efficiency, always use the in keyword to declare components that your lambda expression doesn't change as read only.

    The following example passes a Source component parameter to a job as read-only, and a Destination component parameter as writable:

    
    Entities.ForEach(
        (ref Destination outputData,
            in Source inputData) =>
        {
            outputData.Value = inputData.Value;
        })
        .ScheduleParallel();
    
    Important

    You can't pass chunk components to the Entities.ForEach lambda expression.

    For dynamic buffers, use DynamicBuffer<T> rather than the component type stored in the buffer:

    
    [RequireMatchingQueriesForUpdate]
    public partial class BufferSum : SystemBase
    {
        private EntityQuery query;
    
        //Schedules the two jobs with a dependency between them
        protected override void OnUpdate()
        {
            //The query variable can be accessed here because we are
            //using WithStoreEntityQueryInField(query) in the entities.ForEach below
            int entitiesInQuery = query.CalculateEntityCount();
    
            //Create a native array to hold the intermediate sums
            //(one element per entity)
            NativeArray<int> intermediateSums
                = new NativeArray<int>(entitiesInQuery, Allocator.TempJob);
    
            //Schedule the first job to add all the buffer elements
            Entities
                .ForEach((int entityInQueryIndex, in DynamicBuffer<IntBufferData> buffer) =>
            {
                for (int i = 0; i < buffer.Length; i++)
                {
                    intermediateSums[entityInQueryIndex] += buffer[i].Value;
                }
            })
                .WithStoreEntityQueryInField(ref query)
                .WithName("IntermediateSums")
                .ScheduleParallel(); // Execute in parallel for each chunk of entities
    
            //Schedule the second job, which depends on the first
            Job
                .WithCode(() =>
            {
                int result = 0;
                for (int i = 0; i < intermediateSums.Length; i++)
                {
                    result += intermediateSums[i];
                }
                //Not burst compatible:
                Debug.Log("Final sum is " + result);
            })
                .WithDisposeOnCompletion(intermediateSums)
                .WithoutBurst()
                .WithName("FinalSum")
                .Schedule(); // Execute on a single, background thread
        }
    }
    

    Named parameters

    You can also pass the following named parameters to the Entities.ForEach lambda expression, which Unity assigns values based on the entity the job is processing.

    Parameter Description
    Entity entity The entity instance of the current entity. You can name the parameter anything as long as the type is Entity.
    int entityInQueryIndex The index of the entity in the list of all entities that the query selected. Use the entity index value when you have a native array that you need to fill with a unique value for each entity. You can use the entityInQueryIndex as the index in that array. Use entityInQueryIndex as the sortKey to add commands to a concurrent entity command buffer.
    int nativeThreadIndex A unique index of the thread executing the current iteration of the lambda expression. When you use Run to execute the lambda expression, nativeThreadIndex is always zero. Don't use nativeThreadIndex as the sortKey of a concurrent entity command buffer; use entityInQueryIndexinstead.
    EntityCommands commands You can name this parameter anything as long as the type is EntityCommands. Use this parameter only in conjunction with either WithDeferredPlaybackSystem<T> or WithImmediatePlayback. The EntityCommands type has several methods that mirror their counterparts in the EntityCommandBuffer type. If you use an EntityCommands instance inside Entities.ForEach, the compiler creates extra code where appropriate to handle the creation, scheduling, playback, and disposal of entity command buffers, on which counterparts to EntityCommands methods are invoked.

    Execute an Entities.ForEach lambda expression

    You can execute a job lambda expression in the following ways:

    • Use Schedule and ScheduleParallel to schedule the job
    • Use Run to execute the job immediately on the main thread.

    The following example illustrates a SystemBase implementation that uses Entities.ForEach to read the Velocity component and write to the ObjectPosition component:

    
    [RequireMatchingQueriesForUpdate]
    partial class ApplyVelocitySystem : SystemBase
    {
        protected override void OnUpdate()
        {
            Entities
                .ForEach((ref ObjectPosition translation,
                in Velocity velocity) =>
                {
                    translation.Value += velocity.Value;
                })
                .Schedule();
        }
    }
    

    Capture variables

    You can capture local variables for the Entities.ForEach lambda expression. When you call one of the Schedule methods instead of Run to use a job to execute the lambda expression, there are some restrictions on the captured variables and how you use them:

    • You can only capture native containers and blittable types.
    • A job can only write to captured variables that are native containers. To return a single value, create a native array with one element.

    If you read a native container, but don't write to it, always use WithReadOnly(variable) to specify read-only access. For more information about setting attributes for captured variables, see SystemBase.Entities. Entities.ForEach provides these as methods because the C# language doesn't allow attributes on local variables.

    To dispose of captured native containers or types that contain native containers after Entities.ForEach runs, use WithDisposeOnCompletion(variable). If you call this in Run, this disposes of the types immediately after the lambda expression runs. If you call this in Schedule andScheduleParallel, it schedules them to be disposed of later with a job, and returns the JobHandle.

    Note

    When you execute the method with Run you can write to captured variables that aren't native containers. However, you should still use blittable types where possible so that the method can be compiled with Burst.

    In This Article
    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)