docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Look up arbitrary data

    The most efficient way to access and change data is to use a system with an entity query and a job. This utilizes the CPU resources in the most efficient way, with minimal memory cache misses. It's best practice to use the most efficient, fastest path to perform the bulk of data transformations. However, there are times when you might need to access an arbitrary component of an arbitrary entity at an arbitrary point in your program.

    You can look up data in an entity's IComponentData and its dynamic buffers. The way you look up data depends on whether your code uses Entities.ForEach, or an IJobChunk job, or some other method on the main thread to execute in a system.

    Look up entity data in a system

    To look up data stored in a component of an arbitrary entity from inside a system's Entities.ForEach or Job.WithCode method, use GetComponent<T>(Entity)

    For example, the following code uses GetComponent<T>(Entity) to get a Target component, which has an entity field that identifies the entity to target. It then rotates the tracking entities towards their target:

    [RequireMatchingQueriesForUpdate]
    public partial class TrackingSystem : SystemBase
    {
        protected override void OnUpdate()
        {
            float deltaTime = SystemAPI.Time.DeltaTime;
    
            Entities
                .ForEach((ref Rotation orientation,
                in LocalToWorld transform,
                in Target target) =>
                {
                    // Check to make sure the target Entity still exists and has
                    // the needed component
                    if (!SystemAPI.HasComponent<LocalToWorld>(target.entity))
                        return;
    
                    // Look up the entity data
                    LocalToWorld targetTransform
                        = SystemAPI.GetComponent<LocalToWorld>(target.entity);
                    float3 targetPosition = targetTransform.Position;
    
                    // Calculate the rotation
                    float3 displacement = targetPosition - transform.Position;
                    float3 upReference = new float3(0, 1, 0);
                    quaternion lookRotation =
                        quaternion.LookRotationSafe(displacement, upReference);
    
                    orientation.Value =
                        math.slerp(orientation.Value, lookRotation, deltaTime);
                })
                .ScheduleParallel();
        }
    }
    

    If you want to access data stored in a dynamic buffer, you also need to declare a local variable of type BufferLookup in the SystemBase OnUpdate method. You can then capture the local variable in a lambda expression. For example:

    
    public struct BufferData : IBufferElementData
    {
        public float Value;
    }
    [RequireMatchingQueriesForUpdate]
    public partial class BufferLookupSystem : SystemBase
    {
        protected override void OnUpdate()
        {
            BufferLookup<BufferData> buffersOfAllEntities
                = this.GetBufferLookup<BufferData>(true);
    
            Entities
                .ForEach((ref Rotation orientation,
                in LocalToWorld transform,
                in Target target) =>
                {
                    // Check to make sure the target Entity with this buffer type still exists
                    if (!buffersOfAllEntities.HasBuffer(target.entity))
                        return;
    
                    // Get a reference to the buffer
                    DynamicBuffer<BufferData> bufferOfOneEntity =
                        buffersOfAllEntities[target.entity];
    
                    // Use the data in the buffer
                    float avg = 0;
                    for (var i = 0; i < bufferOfOneEntity.Length; i++)
                    {
                        avg += bufferOfOneEntity[i].Value;
                    }
                    if (bufferOfOneEntity.Length > 0)
                        avg /= bufferOfOneEntity.Length;
                })
                .ScheduleParallel();
        }
    }
    

    Look up entity data in a job

    To access component data at random in a job struct such as IJobChunk, use one of the following types:

    • ComponentLookup
    • BufferLookup

    These types get an array-like interface to component, indexed by Entity object. You can also use ComponentLookup to determine whether an entity's enableable components are enabled or disabled, or to toggle the state of these components.

    To use them, declare a field of type ComponentLookup or BufferLookup, set the value of the field, and then schedule the job.

    For example, you can use the ComponentLookup field to look up the world position of entities:

    [ReadOnly]
    public ComponentLookup<LocalToWorld> EntityPositions;
    
    Note

    This declaration uses the ReadOnly attribute. Always declare ComponentLookup objects as read-only unless you want to write to the components you access.

    The following example illustrates how to set the data fields and schedule the job:

    protected override void OnUpdate()
    {
        var job = new ChaserSystemJob();
    
        // Set non-ECS data fields
        job.deltaTime = SystemAPI.Time.DeltaTime;
    
        // Schedule the job using Dependency property
        Dependency = job.ScheduleParallel(query, this.Dependency);
    }
    

    To look up the value of a component, use an entity object inside the job's Execute method:

    float3 targetPosition = entityPosition.Position;
    float3 chaserPosition = transform.Position;
    float3 displacement = targetPosition - chaserPosition;
    float3 newPosition = chaserPosition + displacement * deltaTime;
    transform.Position = newPosition;
    

    The following, full example shows a system that moves entities that have a Target field that contains the entity object of their target towards the current location of the target:

    [RequireMatchingQueriesForUpdate]
    public partial class MoveTowardsEntitySystem : SystemBase
    {
        private EntityQuery query;
    
        [BurstCompile]
        private partial struct MoveTowardsJob : IJobEntity
        {
    
            // Read-only data stored (potentially) in other chunks
            [ReadOnly]
            public ComponentLookup<LocalToWorld> EntityPositions;
    
            // Non-entity data
            public float deltaTime;
    
            public void Execute(ref LocalTransform transform, in Target target, in LocalToWorld entityPosition)
            {
                // Get the target Entity object
                Entity targetEntity = target.entity;
    
                // Check that the target still exists
                if (!EntityPositions.HasComponent(targetEntity))
                    return;
    
                // Update translation to move the chasing entity toward the target
                float3 targetPosition = entityPosition.Position;
                float3 chaserPosition = transform.Position;
    
                float3 displacement = targetPosition - chaserPosition;
                transform.Position = chaserPosition + displacement * deltaTime;
            }
        }
    
        protected override void OnCreate()
        {
            // Select all entities that have Translation and Target Component
            query = this.GetEntityQuery
                (
                    typeof(LocalTransform),
                    ComponentType.ReadOnly<Target>()
                );
        }
    
        protected override void OnUpdate()
        {
            // Create the job
            var job = new MoveTowardsJob();
    
            // Set the component data lookup field
            job.EntityPositions = GetComponentLookup<LocalToWorld>(true);
    
            // Set non-ECS data fields
            job.deltaTime = SystemAPI.Time.DeltaTime;
    
            // Schedule the job using Dependency property
            Dependency = job.ScheduleParallel(query, Dependency);
        }
    }
    

    Data access errors

    If the data you look up overlaps the data you want to read and write to in the job, then random access might lead to race conditions.

    You can mark an accessor object with the NativeDisableParallelForRestriction attribute, if you're sure that there's no overlap between the entity data you want to read or write to directly, and the specific entity data you want to read or write to at random.

    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)