ECS的核心概念

鄭洪智發表於2020-12-21
ECS架構的核心是資料,這也是為什麼Unity會將這一套技術棧命名為DOTS。System會讀取entity上面的component的資料流,並處理資料。實體在這裡其實更像是索引,它本身並不包含任何資料和邏輯。

ECS包含三個部分:實體(entities)、資料(components)、行為(system)。具體看下圖:

ECS的核心概念

這個圖中,System讀取了多個實體的Translation和Rotation元件,然後經過計算處理,將結果更新到LocalToWorld元件中。

從圖中你可以看到,實體A和B還有Renderer元件,但是C並沒有。不過這並不會影響System的計算邏輯,因為這個系統不關心Renderer元件。

你還可以寫一個系統,需要處理Renderer元件,這樣系統就會忽略實體C。你還可以寫一個系統排除包含Renderer元件的實體,這樣系統就會忽略實體A和B。

下面對ECS中比較重要的幾個核心概念做一個梳理:

原型 Archetypes

多個元件的組合叫做一個原型。

比如一個3D物體可能會包含用於transform的元件,包括移動、旋轉、渲染,每個3D物體對應一個實體,但是他們都有同樣的元件,所以ECS會把他們分類成是一類原型。

ECS的核心概念

在上圖中,實體A和B的原型都是M,實體C的原型是N。

你也可以通過在執行時新增或者移除component來改變一個實體的原型。例如:如果將實體B的Renderer元件移除,實體B的原型就會變成N。

記憶體塊 Memory Chunks

為什麼要先講原型的概念呢,因為一個實體的原型是什麼,決定了ECS會將實體的components也就是資料存在什麼地方。ECS按塊分配記憶體,每塊用一個ArchetypeChunk物件表示。

一個塊只包含一種原型,可以包含的多個實體的資料。如果一個塊的記憶體滿了,ECS會分配一個新的塊來儲存新的實體的components。

如果你修改了實體的元件,那就相當於修改了實體的原型,這時候ECS會將實體的元件資料移到另外一個塊中。

ECS的核心概念

原型和記憶體塊的關係是一對多的關係。這就意味著,如果想查詢給定的一組component型別的所有實體,只需要在這些原型中搜尋即可。這樣會比在所有的實體中查詢效率高很多。

ECS在儲存實體到記憶體塊中沒有特殊的排序,當建立一個實體或者實體的原型發生變化時,ECS會將它放到對應原型的第一個還有空間的記憶體塊中。記憶體塊中的資料會緊密排列。如果一個實體要被移出當前原型的記憶體塊,這時候會有個空位,ECS會把這個記憶體塊最後的實體資料移動到這個空位中。

注意:原型中的共享元件(後面會具體說這是個什麼東東)的資料也會影響實體會被存在哪個記憶體塊。同一個記憶體塊中的所有實體的共享元件中的資料值都是相同的。如果你修改了共享元件中的資料,這個實體會被移到另外一個塊中,有點類似修改了實體的原型。

將共享元件的實體分到一個記憶體塊中會提高處理他們的速度。比如Hybird Renderer(混合渲染)定義了RenderMesh元件來達成這個目的。

實體查詢

一個System根據什麼來決定處理哪些實體呢?這時候會用到一個叫實體查詢(Entity Query)的東西。實體查詢首先需要一些元件型別,然後根據你傳入的元件型別的組合,在包含這些元件的原型中查詢符合要求的實體。查詢時可以指定下面三種型別:

  • All 必須包含All中所有的元件型別
  • Any 必須包含Any中至少一個元件型別
  • None 不能包含None中任意一個元件型別

一次實體查詢的結果會返回所有符合查詢要求的記憶體塊,你可以使用IJobChunk來迭代遍歷所有的元件。(IJobChunk後面會講。)

Jobs 作業

之前說過,ECS配合Job使用才能發揮多執行緒的威力。ECS提供了SystemBase類,其中包含Entities.ForEach方法,還包含了IJobChunk的Schedule()和ScheduleParallel()方法,可以在子執行緒中處理資料。Entities.ForEach是最簡單的方法,只需要幾行程式碼就能實現。IJobChunk可以用來處理比較複雜的情況。

ECS會在主執行緒排程Job,根據System的順序。當job排程後,ECS會追蹤哪些job在讀寫哪些元件。需要讀許可權的job需要等待前面寫許可權的job執行完,反之亦然。Job排程器會使用job依賴來決定哪些job可以並行,哪些必須序列。

System的組織

ECS通過World和group來組織system。預設情況下,ECS會建立一個預設的World,包含一些預定義的group組。它會找到工程中所有的System,例項化他們,並新增到預定義的group中。

你可以指定同一個group中system的Update的執行順序。Group也是一種system,所以你可以將一個group新增到另外一個group中。如果你沒有指定順序,system的執行順序會不太確定,並不會按照它們建立的順序。不過,同一個group中的所有system都會比下一個group中的system先執行。

System的Update是在主執行緒中執行的,不過可以使用Job將工作分配到子執行緒中。

ECS世界的視覺化建立(authoring)

在Unity中沒辦法直接視覺化地建立DOTS世界,但是,可以先用GameOjbect和MonoBehaviour來建立,然後通過轉換系統將GameObject轉換成實體和元件。這個我們後面也會細講。


來源:Unity官方平臺
原文:https://mp.weixin.qq.com/s/Tvtqz51Np7vWeYwKcQtHdQ

相關文章