天天看點

Unity ECS Manual [18]

Looking up data(查閱資料)

  通路和修改ECS資料的最有效方式是使用一個具有實體查詢和作業的系統。這提供了對CPU資源的最佳利用,同時記憶體緩存失誤最少。事實上,你的資料設計的目标之一應該是使用最有效、最快的路徑來執行大部分的資料轉換。然而,有時你需要在程式中的一個任意點通路一個任意實體的任意元件。

  給定一個實體對象,你可以在其IComponentData和動态緩沖區中查找資料。方法不同,取決于你的代碼是在系統中使用Entities.ForEach或使用[IJobChunk]作業執行,還是在主線程的其他地方執行。

Looking up entity data in a system(在一個系統中查詢實體資料)

  使用GetComponent(Entity)從系統的Entities.ForEach或[Job.WithCode]函數中查找存儲在任意實體的一個元件中的資料。

  例如,如果你有Target元件,并有一個Entity字段來識别目标實體,你可以使用下面的代碼将跟蹤實體向其目标旋轉。

public partial class TrackingSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = this.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 (!HasComponent<LocalToWorld>(target.entity))
                    return;

                // Look up the entity data
                LocalToWorld targetTransform
                    = 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();
    }
}
           

  通路存儲在動态緩沖區的資料需要一個額外的步驟。你必須在OnUpdate()方法中聲明一個BufferFromEntity類型的局部變量。然後你可以在你的lambda函數中 "捕獲 "這個局部變量。

public struct BufferData : IBufferElementData
{
    public float Value;
}
public partial class BufferLookupSystem : SystemBase
{
    protected override void OnUpdate()
    {
        BufferFromEntity<BufferData> buffersOfAllEntities
            = this.GetBufferFromEntity<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.HasComponent(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();
    }
}

           

Looking up entity data in IJobEntityBatch(在IJobEntityBatch中查詢實體資料)

  要随機通路IJobEntityBatch或其他Job結構中的元件資料,請使用以下類型之一,以獲得類似數組的元件接口,按Entity對象索引:

  • ComponentDataFromEntity
  • BufferFromEntity

  聲明一個ComponentDataFromEntity或BufferFromEntity類型的字段,并在排程作業前設定該字段的值。

  例如,如果你有Target元件,并有一個Entity字段來識别目标實體,你可以在作業結構中添加以下字段來查詢目标的世界位置。

[ReadOnly]
public ComponentDataFromEntity<LocalToWorld> EntityPositions;
           

  請注意,這個聲明使用了ReadOnly屬性。你應該始終将ComponentDataFromEntity對象聲明為隻讀,除非你對你通路的元件進行了寫入。

  你可以在安排工作時設定這個字段,如下所示:

var job = new ChaserSystemJob();
job.EntityPositions = this.GetComponentDataFromEntity<LocalToWorld>(true);
           

  在作業的Execute()函數中,你可以使用一個Entity對象來查詢一個元件的值:

  下面這個完整的例子顯示了一個系統,該系統将有一個包含其目标實體對象的目标字段的實體向目标的目前位置移動:

public class MoveTowardsEntitySystem : SystemBase
{
    private EntityQuery query;

    [BurstCompile]
    private struct MoveTowardsJob : IJobEntityBatch
    {
        // Read-write data in the current chunk
        public ComponentTypeHandle<Translation> PositionTypeHandleAccessor;

        // Read-only data in the current chunk
        [ReadOnly]
        public ComponentTypeHandle<Target> TargetTypeHandleAccessor;

        // Read-only data stored (potentially) in other chunks
        [ReadOnly]
        public ComponentDataFromEntity<LocalToWorld> EntityPositions;

        // Non-entity data
        public float deltaTime;

        public void Execute(ArchetypeChunk batchInChunk, int batchIndex)
        {
            // Get arrays of the components in chunk
            NativeArray<Translation> positions
                = batchInChunk.GetNativeArray<Translation>(PositionTypeHandleAccessor);
            NativeArray<Target> targets
                = batchInChunk.GetNativeArray<Target>(TargetTypeHandleAccessor);

            for (int i = 0; i < positions.Length; i++)
            {
                // Get the target Entity object
                Entity targetEntity = targets[i].entity;

                // Check that the target still exists
                if (!EntityPositions.HasComponent(targetEntity))
                    continue;

                // Update translation to move the chasing enitity toward the target
                float3 targetPosition = EntityPositions[targetEntity].Position;
                float3 chaserPosition = positions[i].Value;

                float3 displacement = targetPosition - chaserPosition;
                positions[i] = new Translation
                {
                    Value = chaserPosition + displacement * deltaTime
                };
            }
        }
    }

    protected override void OnCreate()
    {
        // Select all entities that have Translation and Target Componentx
        query = this.GetEntityQuery
            (
                typeof(Translation),
                ComponentType.ReadOnly<Target>()
            );
    }

    protected override void OnUpdate()
    {
        // Create the job
        var job = new MoveTowardsJob();

        // Set the chunk data accessors
        job.PositionTypeHandleAccessor =
            this.GetComponentTypeHandle<Translation>(false);
        job.TargetTypeHandleAccessor =
            this.GetComponentTypeHandle<Target>(true);

        // Set the component data lookup field
        job.EntityPositions = this.GetComponentDataFromEntity<LocalToWorld>(true);

        // Set non-ECS data fields
        job.deltaTime = this.Time.DeltaTime;

        // Schedule the job using Dependency property
        this.Dependency = job.ScheduleParallel(query, 1, this.Dependency);
    }
}
           

Data access errors(資料通路錯誤)

  如果你要查詢的資料與你在作業中直接讀寫的資料重疊,那麼随機通路會導緻競争條件和微妙的bug。當你确定你在作業中直接讀寫的特定實體資料和你随機讀寫的特定實體資料之間沒有重疊,那麼你可以用NativeDisableParallelForRestriction屬性來标記通路器對象。