Unity ECS Manual [18]

63 篇文章 11 订阅

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对象来查询一个组件的值:

float3 targetPosition = EntityPositions[targetEntity].Position;

  下面这个完整的例子显示了一个系统,该系统将有一个包含其目标实体对象的目标字段的实体向目标的当前位置移动:

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属性来标记访问器对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值