ECS丨Chunk块 筛选机制
提示:选中右侧目录,可快速找到所需内容
本系列博客地址:传送门
这样筛选的优势:
可以对整个块的实体进行操作,更加灵活。
在集群的物体中,如鱼群、兵群等,运行速度更快。
概念解析
块(Chuck)和原型(Archetype)
下图第一张我们看得出,因为拥有相同组件,两个实体Entity A、Entity B,放在了块Chuck A中,Entity C则放在了 Chuck B中。
那么:
块,就是拥有相同组件的很多个实体。
但,一个块能包含的实体是有限的,不能无限扩充,所以如右图所示,多了来的相同实体又组成了其他块,这些块组成了原形。
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!