Look up arbitrary data
The most efficient way to access and change data is to use a system together with an entity query that runs in a job. This maximizes parallelism and minimizes cache misses. Use that fast path for the bulk of transformations. However, there are times when you might need to access a component on an arbitrary entity at an arbitrary point in your system update.
You can look up data in an entity's IComponentData and its dynamic buffers.
Look up entity data in a system
Inside a SystemBase you can iterate entities on the main thread with SystemAPI.Query and then perform targeted lookups on arbitrary entities using:
SystemAPI.HasComponent<T>(Entity)SystemAPI.GetComponent<T>(Entity)GetComponentLookup<T>(bool isReadOnly)GetBufferLookup<T>(bool isReadOnly)
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:
If you want to access data stored in a dynamic buffer on an arbitrary entity (not necessarily part of the current iteration), create a local BufferLookup<T> (via GetBufferLookup<T>(true) for read-only lookup). Capture it in the foreach loop and use it to test for the buffer and fetch it:
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:
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 = EntityPositions[targetEntity].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 = EntityPositions[targetEntity].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.