ECS丨Chunk块 筛选机制

提示:选中右侧目录,可快速找到所需内容

本系列博客地址:传送门

 

这样筛选的优势:

可以对整个块的实体进行操作,更加灵活。

在集群的物体中,如鱼群、兵群等,运行速度更快。

 

 

概念解析

块(Chuck)和原型(Archetype)

下图第一张我们看得出,因为拥有相同组件,两个实体Entity A、Entity B,放在了块Chuck A中,Entity C则放在了 Chuck B中。

那么:

块,就是拥有相同组件的很多个实体。

但,一个块能包含的实体是有限的,不能无限扩充,所以如右图所示,多了来的相同实体又组成了其他块,这些块组成了原形。

ECS丨Chunk块 筛选机制          ECS丨Chunk块 筛选机制

ECS通过块、原型的分组形式,提高了数据的读取效率。

 

原型下的所有块,它们的大小是一样的。

相当于预先申请了内存空间,不管塞没塞满,都占用一样的内存空间,一个块塞满之后,就又申请一个新的空的块。

 

继承了JobComponentSystem 的 ForEach 的这种处理机制,只能一个个实体进行处理,下面要介绍的IJobChunk,是可以对某个块(Chunk)里的所有实体进行批量处理。

好,原理讲完了,那我们讲讲怎么写吧。

 

 

System

我们的Job System,红色就是固定写法,写就完了。

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;

public class RotationSpeedSystem_IJobChunk : JobComponentSystem
{
    EntityQuery m_Group;

    protected override void OnCreate ()
    {
        //传递多个组件的类型作为参数,筛选出包含指定类型的实体
       
m_Group = GetEntityQuery(typeof(Rotation), ComponentType.ReadOnly<RotationSpeed_IJobChunk>());
    }

    // OnUpdate runs on the main thread.
   
protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        // ArchetypeChunkComponentType<Rotation>:Rotation 的块(Chuck)的组件类型
        // 通过GetArchetypeChunkComponentType函数,可获得某个原型的块的组件类型。
       
ArchetypeChunkComponentType<Rotation> rotationType = GetArchetypeChunkComponentType<Rotation>();
        var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed_IJobChunk>(true);

        var job = new RotationSpeedJob()
        {
            RotationType = rotationType,
            RotationSpeedType = rotationSpeedType,
            DeltaTime = Time.DeltaTime
        };

        // 这儿执行/并行了 RotationSpeedJob 中的 Execute,且返回了数据
       
return job.Schedule(m_Group, inputDependencies);
    }

    // IJobChunk 里放我们的逻辑
    // Use the [BurstCompile] attribute to compile a job with Burst. You may see significant speed ups, so try it!
   
[BurstCompile]
    struct
RotationSpeedJob : IJobChunk
    {
        public float DeltaTime;
       
public ArchetypeChunkComponentType<Rotation> RotationType;
        [ReadOnly] public ArchetypeChunkComponentType<RotationSpeed_IJobChunk> RotationSpeedType;

        // chunk 就是经过 EntityQuery 筛选后获得的 块
        // 我们筛选出来的实体可能存放在不同的块里,Execute函数每次只能获得一个块里的数据。
        // 同一时间可能会有很多个RotationSpeedJob被执行(并行),大幅提升了性能
       
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            // GetNativeArray函数,可以获得这个块里的所有实体的某个类型的组件
           
var chunkRotations = chunk.GetNativeArray(RotationType);
            var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var rotation = chunkRotations[i];
                var rotationSpeed = chunkRotationSpeeds[i];

                chunkRotations[i] = new Rotation
                {
                    Value = math.mul(math.normalize(rotation.Value),
                        quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime))
                };
            }
        }
    }
}

 

 

Component

记录数据。不必挂到物体上,放在Assets即可。

你可以看到,下方的组件里面的特性,不是上篇博客的 [GenerateAuthoringComponent] 了。现在这个脚本也无法添加到物体上。这个脚本也没有将物体转化为实体的功能了。下面实体上的另一个脚本实现转化功能。

这个组件是通过下面实体上的方法,将该组件添加到实体上。

using System;
using Unity.Entities;

[Serializable]
public struct RotationSpeed_IJobChunk : IComponentData
{
    public float RadiansPerSecond;
}

 

 

Entity

场景中要操作的物体,添加 ConvertToEntity。

同时挂载如下脚本。

  • 该脚本继承 IConvertGameObjectToEntity 并实现 Convert,是将该物体转化为实体。
  • 该脚本 Convert,非常灵活的new了要添加的脚本,并通过 AddComponentData 添加到该实体上。且意味着你可以new N多个组件,初始化添加到实体上。

using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

[RequiresEntityConversion]
public class RotationSpeedAuthoring_IJobChunk : MonoBehaviour, IConvertGameObjectToEntity
{
    public float DegreesPerSecond = 360.0F;


    public void Convert (Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var data = new RotationSpeed_IJobChunk { RadiansPerSecond = math.radians(DegreesPerSecond) };
        dstManager.AddComponentData(entity, data);
    }
}

 

 

好,你现在可以运行一下,看看方块是否转起来了。

本节到此结束,下节讲通过预制体创建实体的方法,

Enjoy!