官方案例解析3
開始之前的準備工作:
0下載Unity編輯器(2019.1.0f1 or 更新的版本),if(已經下載了)continue;
1下載官方案例,打開Git Shell輸入:
git clone https://github.com/Unity-Technologies/EntityComponentSystemSamples.git --recurse
or 點擊Unity官方ECS示例下載代碼
if(已經下載了)continue;
2用Unity Hub打開官方的項目:ECSSamples
3在Assets目錄下找到HelloCube/3. IJobChunk ,並打開IJobChunk 場景
3. IJobChunk
這個案例演示了基於Jobs的ECS系統如何旋轉一對方塊兒,與同樣使用Jobs系統的案例2不同的是,在循環遍歷的時候,前者迭代的是實體,而這個案例迭代的是塊(內存塊,這個塊是被一開始基於相同原型分配好了的,也就是說,如果這20個實體有相同的組件,那麼他們會被緊密的安排在一塊內存中,從而方便處理器進行操作。這是相對於面向對象的散列內存而言的,在面向對象中要操作內存中的某個對象時,你不得不在整個內存中尋找它,這樣會降低讀取速度,也不方便批量操作。ECS則會非常緊密地分配實體在內存中的位置,相同的組件會被統一放在塊中,讀取速度快,也方便批量操作)。下面來一探究竟:
- Main Camera ……主攝像機
- Directional Light……光源
- RotatingCube……旋轉的方塊
- ChildCube……子方塊
和案例2一樣RotatingCube上同樣掛了ConvertToEntity腳本,它將Unity的遊戲對象GameObject轉化成Entity,從而讓遊戲運行更加高效,腳本的工作原理已經在上一篇講過了,此處跳過。
RotatingCube上還掛了另外一個腳本RotationSpeedAuthoring_IJobChunk,下面我們來看一下這個腳本:
[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);
}
}
代碼和上一篇的腳本RotationSpeedAuthoring_IJobForEach幾乎一毛一樣,區別在於Chunk關鍵字上了,我這裏略過儲存數據的RotationSpeed_IJobChunk腳本不講,實在沒有什麼可說的,就儲存了一個數據而已:
/// <summary>
/// 我啥也不幹,就放數據
/// </summary>
[Serializable]
public struct RotationSpeed_IJobChunk : IComponentData
{
public float RadiansPerSecond;
}
這裏的IComponentData上次講過,它就是一個空接口而已,作用只是表明自己是Component的身份,從而讓System識別,非常純粹的一個腳本。這裏我們將理解到表明身份的重要性,下面是這個案例的重點腳本RotationSpeedSystem_IJobChunk:
/// <summary>
/// 按塊操作實體系統
/// </summary>
public class RotationSpeedSystem_IJobChunk : JobComponentSystem
{
EntityQuery m_Group;//查詢到特定組件的實體,將其放入這個組中
/// <summary>
/// 這裏根據類型來查詢到特定的實體
/// </summary>
protected override void OnCreate()
{
// Cached access to a set of ComponentData based on a specific query
///typeof(Rotation)=帶有Rotation組件的;ComponentType=對應RotationSpeed_IJobChunk組件類型的
/// ReadOnly=只讀會加快獲取實體的速度,ReadWrite=讀寫 則相對較慢
m_Group = GetEntityQuery(typeof(Rotation), ComponentType.ReadOnly<RotationSpeed_IJobChunk>());
}
// Use the [BurstCompile] attribute to compile a job with Burst. You may see significant speed ups, so try it!
[BurstCompile]//同樣使用Burst編譯器來加速,區別是使用了塊接口:IJobChunk
struct RotationSpeedJob : IJobChunk
{
public float DeltaTime;
/// <summary>
/// 原型塊組件類型=Rotation
/// </summary>
public ArchetypeChunkComponentType<Rotation> RotationType;
/// <summary>
/// 只讀 原型塊組件類型=RotationSpeed_IJobChunk
/// </summary>
[ReadOnly] public ArchetypeChunkComponentType<RotationSpeed_IJobChunk> RotationSpeedType;
/// <summary>
/// 找出滿足條件的實體來執行
/// </summary>
/// <param name="chunk"><原型塊/param>
/// <param name="chunkIndex">塊索引</param>
/// <param name="firstEntityIndex">第一個實體索引</param>
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
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];
// Rotate something about its up vector at the speed given by RotationSpeed_IJobChunk.
chunkRotations[i] = new Rotation
{
Value = math.mul(math.normalize(rotation.Value),
quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime))
};
}
}
}
// OnUpdate runs on the main thread.
/// <summary>
/// 這個方法在主線程上運行
/// </summary>
/// <param name="inputDependencies">輸入依賴</param>
/// <returns></returns>
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
// Explicitly declare: 聲明
// - Read-Write access to Rotation 讀寫的方式訪問旋轉
// - Read-Only access to RotationSpeed_IJobChunk 只讀的方式訪問旋轉速度
var rotationType = GetArchetypeChunkComponentType<Rotation>();
var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed_IJobChunk>(true);
var job = new RotationSpeedJob()
{
RotationType = rotationType,
RotationSpeedType = rotationSpeedType,
DeltaTime = Time.deltaTime
};
return job.Schedule(m_Group, inputDependencies);
}
}
思路是明確的,一個案例比之前一個更快,切更有目的性!
這就是代碼的優化迭代!
小結
我們來對比案例二:
ECS | Scripts | Inherit |
---|---|---|
Entity | RotationSpeedAuthoring_IJobForEach | IConvertGameObjectToEntity |
Component | RotationSpeed_IJobForEach | IComponentData |
System | RotationSpeedSystem_IJobForEach | JobComponentSystem |
和案例三:
ECS | Scripts | Inherit |
---|---|---|
Entity | RotationSpeedAuthoring_IJobChunk | IConvertGameObjectToEntity |
Component | RotationSpeed_IJobChunk | IComponentData |
System | RotationSpeedSystem_IJobChunk | JobComponentSystem |
變化不大,System都利用了Jobs和Burst編譯器,Chunk會跑得更快!
DOTS 邏輯圖表
流程大體如下:
更新計劃
作者的話
如果喜歡我的文章可以點贊支持一下,謝謝鼓勵!如果有什麼疑問可以給我留言,有錯漏的地方請批評指證!
如果有技術難題需要討論,可以加入開發者聯盟:566189328(付費羣)爲您提供有限的技術支持,以及,心靈雞湯!
當然,不需要技術支持也歡迎加入進來,隨時可以請我喝咖啡、茶和果汁!( ̄┰ ̄*)