孫廣東 2018.5.20git
關於Unity 2018的實體組件系統(通用名稱ECS)二github
將介紹如何在Unity上使用實體組件系統(一般稱爲ECS)。json
此次的內容是Unity提供的ECS API的基本用法,一個小應用程序和並行化。編輯器
它不包括與Unity的GameObject / Component的合做,以及實際使用。ide
獲取可使用ECS的編輯器優化
Unity2018 和以後的版本均可以!this
您能夠從 https://github.com/Unity-Technologies/EntityComponentSystemSamples 下載官方的一個Demo。操作系統
以後,下載與您本身的操做系統匹配的編輯器並安裝它。.net
建立一個可使用ECS的項目翻譯
我將建立一個項目,但我沒法使用ECS。
要啓用ECS,須要兩件事。
正常啓動Unity並打開 Edit> PlayerSettings> PlayerSettings。
以後,將Scripting Runtime Version腳本運行時版本更改成Stable (.net 4.x)。
接下來是重寫manifest.json。
因爲在項目的Root文件夾/ Packages中有一個名爲manifest.json的文件,所以咱們將按照https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/TwoStickShooter/Pure/Packages/manifest.json 與此處相同的方式重寫內容。
準備工做完成。
最小的ECS項目
首先,嘗試儘量地構建最有意義的功能。
此次要組織的功能就是這樣
一、 沒有使用 ECS的代碼 :
首先我會試着用MonoBehaviour來組織它。這是一個很是簡單的代碼。
編寫完成後,您能夠將Counter組件添加到適當的GameObject中。
using UnityEngine; public class Counter : MonoBehaviour{ public int count; void Update () { count++; }} |
接下來,讓咱們對應於ECS。有三件事要作
它是什麼?麻煩? ECS就是這樣
ComponentDatas.cs
using Unity.Entities;
// 實體 public struct CountData : IComponentData { public int count; } |
CountSystem.cs
using Unity.Entities;
public class CountSystem : ComponentSystem { // System所需的ComponentData列表 struct Group { public int Length; public ComponentDataArray<CountData> countData; }
[Inject] Group group; // 注入請求的ComponentData
// 調用每一幀 protected override void OnUpdate() { for(int i=0; i<group.Length; i++) { var countData = group.countData[i]; countData.count++; group.countData[i] = countData; } } } |
ECSMain.cs
using UnityEngine; using Unity.Entities;
public class ECSMain : MonoBehaviour { void Start () { // 獲取EntityManager var entityManager = World.Active.GetOrCreateManager<EntityManager>();
// 定義實體的原型 var sampleArchetype = entityManager.CreateArchetype(typeof(CountData));
// 實際上基於原型生成實體 entityManager.CreateEntity(sampleArchetype); } } |
以後,若是您將ECSMain附加到適當的對象並Play,則第一步完成。
在Play期間,打開Window > EntityDebugger,當它從Systems列表中找到CountSystem時,它會變白,而且若是實體存在 。
若是沒有實體,那麼您有可能在沒有CountData的狀況下建立實體,或者您沒有首先建立實體。另外,若是您沒有系統,則建立ComponentSystem的代碼有問題。
一點評論
看如下內容。
對於每一個角色的 ECS 是以下所示。
那麼,首先ComponentData,這裏重要的是struct(結構)並繼承IComponentData。
將它用做ComponentData時,這兩個都是必需的。
建立實體ECSMain是一種建立實體的翻譯,但它從MonoBehaviour事件中調用它,等等。
正如你在註釋中看到的那樣,
這是一個流程。
彷佛沒有必要考慮ComponentData存儲在原型中的順序以及建立它的時機。只有內在的東西纔是重要的。
最後一個關於CountSystem做爲ComponentSystem。這是一個兩部分組成。
首先是Group的定義和實際injection的代碼。
ComponentDataArray包含(指向)請求組件的指針,而Length包含具備請求ComponentData的實體數目。
在 [Inject] 中,自動注入對請求組的引用。
下半部分代碼... OnUpdate() 部分只需添加數字。請注意,組的內容是一個結構,所以您不能直接分配它。
增長系統的例子
接下來我會嘗試增長系統。
若是計數器超出範圍,則該過程的內容就像刪除同樣。
功能解釋忽略了更多的效率。
ECS基本上由相似皮帶輸送機的流程任務組成,系統處理一些ComponentData並將其傳遞給下一個系統....
所以,「添加計數器並在計數器超出範圍時移除計數器」
爲了實現這一點,我會再增長一些。
代碼在這裏:
ComponentDatas.cs
using Unity.Entities;
// 実體 public struct CountData : IComponentData { public int count; }
[System.Serializable] public struct RangeData : ISharedComponentData { public int min, max; } |
ECSMain.cs
using UnityEngine; using Unity.Entities; using Unity.Collections;
public class ECSMain : MonoBehaviour { [SerializeField] RangeData range;
EntityArchetype sampleArchetype;
void Start() { var entityManager = World.Active.GetOrCreateManager<EntityManager>(); sampleArchetype = entityManager.CreateArchetype(typeof(CountData), typeof(RangeData)); }
private void Update() { if(Input.anyKey) { var entityManager = World.Active.GetOrCreateManager<EntityManager>(); var entity = entityManager.CreateEntity(sampleArchetype); entityManager.SetSharedComponentData<RangeData>(entity, range); } } } |
RangeSystem.cs
using Unity.Entities; using Unity.Collections; using Unity.Jobs;
[UpdateAfter(typeof(CountSystem))] public class RangeSystem : ComponentSystem { struct Group { public int Length; public EntityArray entities; [ReadOnly] public ComponentDataArray<CountData> countData; [ReadOnly] public SharedComponentDataArray<RangeData> rangeData; }
[Inject] Group group;
protected override void OnUpdate() { for (int i=0; i<group.Length; i++) { var range = group.rangeData[i]; var data = group.countData[i]; if ( data.count > range.max || data.count < range.min ) { PostUpdateCommands.DestroyEntity(group.entities[i]); } } } } |
解釋一下代碼:
首先,咱們添加了RangeData,它是用於範圍判斷的ComponentData。
可是,因爲該「範圍信息」在大多數數據中具備相同的值,所以使用對多個實體公用的IShardComponentData。
還添加了Serializable屬性,以即可以使用Inspector進行設置。
ECSMain添加了這個CountData。
(1) 首先,RangeData顯示在Inspector上,以便編輯,(2) RangeData做爲類型添加到原型中,(3) RangeData設置在建立的Entity中。
以後,咱們經過按下按鈕來改變實體的類型。
最後是系統。
雖然它是一個系統,當它超出範圍時刪除實體......固然,它有必要得到「範圍」。所以,在(2) 中,RangeData包含在組中。
在③中,使用DestroyEntity刪除實體。
這裏值得注意的是Update ① 和 [ReadOnly] 。這兩個用法大體相同,彷佛若是[ReadOnly]存在,它將在使用[WriteOnly]等的系統以後被調用。一樣,若是有UpdateAfter,它將在指定的系統以後被調用。
並行化
在這段時間結束時,嘗試並行化ECS處理。目標是增長計數。
當處理負載至關長或者有不少對象時,並行化多是一個好結果。
CountSystem.cs
using Unity.Entities; using Unity.Jobs;
public class CountSystem : JobComponentSystem { AddCounterJob job;
protected override void OnCreateManager(int capacity) { base.OnCreateManager(capacity); // 作一份工做 job = new AddCounterJob(); }
// 發佈工做 protected override JobHandle OnUpdate(JobHandle inputDeps) { return job.Schedule(this, 64, inputDeps); }
[ComputeJobOptimization] // Burst編譯器的優化屬性 struct AddCounterJob : IJobProcessComponentData<CountData> //請求ComponentData { public void Execute(ref CountData data) { data.count++; } } } |
下圖是處理大約6000個實體的時間比較。因爲它將並行和burst結合在一塊兒,因此存在很大的差別。
可是,有不少地方不能使用並行,由於這種數據處理和行爲在可用數據和行爲方面受到限制。在某些狀況下,若是您使用C# Job System,您可能能夠作更多...