Unity:ECS的核心概念

來源:Unity官方平臺
作者:鄭洪智
時間:2020-12-21
5357
ECS架構(gòu)的核心是數(shù)據(jù),這也是為什么Unity會將這一套技術(shù)棧命名為DOTS。System會讀取entity上面的component的數(shù)據(jù)流,并處理數(shù)據(jù)。實體在這里其實更像是索引,它本身并不包含任何數(shù)據(jù)和邏輯。

ECS架構(gòu)的核心是數(shù)據(jù),這也是為什么Unity會將這一套技術(shù)棧命名為DOTS。System會讀取entity上面的component的數(shù)據(jù)流,并處理數(shù)據(jù)。實體在這里其實更像是索引,它本身并不包含任何數(shù)據(jù)和邏輯。

ECS包含三個部分:實體(entities)、數(shù)據(jù)(components)、行為(system)。具體看下圖:

ia_4300000008.jpg

這個圖中,System讀取了多個實體的Translation和Rotation組件,然后經(jīng)過計算處理,將結(jié)果更新到LocalToWorld組件中。

從圖中你可以看到,實體A和B還有Renderer組件,但是C并沒有。不過這并不會影響System的計算邏輯,因為這個系統(tǒng)不關(guān)心Renderer組件。

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

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

原型 Archetypes

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

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

ia_4300000009.jpg

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

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

內(nèi)存塊 Memory Chunks

為什么要先講原型的概念呢,因為一個實體的原型是什么,決定了ECS會將實體的components也就是數(shù)據(jù)存在什么地方。ECS按塊分配內(nèi)存,每塊用一個ArchetypeChunk對象表示。

一個塊只包含一種原型,可以包含的多個實體的數(shù)據(jù)。如果一個塊的內(nèi)存滿了,ECS會分配一個新的塊來存儲新的實體的components。

如果你修改了實體的組件,那就相當(dāng)于修改了實體的原型,這時候ECS會將實體的組件數(shù)據(jù)移到另外一個塊中。

ia_4300000010.jpg

原型和內(nèi)存塊的關(guān)系是一對多的關(guān)系。這就意味著,如果想查詢給定的一組component類型的所有實體,只需要在這些原型中搜索即可。這樣會比在所有的實體中查找效率高很多。

ECS在存儲實體到內(nèi)存塊中沒有特殊的排序,當(dāng)創(chuàng)建一個實體或者實體的原型發(fā)生變化時,ECS會將它放到對應(yīng)原型的第一個還有空間的內(nèi)存塊中。內(nèi)存塊中的數(shù)據(jù)會緊密排列。如果一個實體要被移出當(dāng)前原型的內(nèi)存塊,這時候會有個空位,ECS會把這個內(nèi)存塊最后的實體數(shù)據(jù)移動到這個空位中。

注意:原型中的共享組件(后面會具體說這是個什么東東)的數(shù)據(jù)也會影響實體會被存在哪個內(nèi)存塊。同一個內(nèi)存塊中的所有實體的共享組件中的數(shù)據(jù)值都是相同的。如果你修改了共享組件中的數(shù)據(jù),這個實體會被移到另外一個塊中,有點類似修改了實體的原型。

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

實體查詢

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

All 必須包含All中所有的組件類型

Any 必須包含Any中至少一個組件類型

None 不能包含None中任意一個組件類型

一次實體查詢的結(jié)果會返回所有符合查詢要求的內(nèi)存塊,你可以使用IJobChunk來迭代遍歷所有的組件。(IJobChunk后面會講。)

Jobs 作業(yè)

之前說過,ECS配合Job使用才能發(fā)揮多線程的威力。ECS提供了SystemBase類,其中包含Entities.ForEach方法,還包含了IJobChunk的Schedule()和ScheduleParallel()方法,可以在子線程中處理數(shù)據(jù)。Entities.ForEach是最簡單的方法,只需要幾行代碼就能實現(xiàn)。IJobChunk可以用來處理比較復(fù)雜的情況。

ECS會在主線程調(diào)度Job,根據(jù)System的順序。當(dāng)job調(diào)度后,ECS會追蹤哪些job在讀寫哪些組件。需要讀權(quán)限的job需要等待前面寫權(quán)限的job執(zhí)行完,反之亦然。Job調(diào)度器會使用job依賴來決定哪些job可以并行,哪些必須串行。

System的組織

ECS通過World和group來組織system。默認情況下,ECS會創(chuàng)建一個默認的World,包含一些預(yù)定義的group組。它會找到工程中所有的System,實例化他們,并添加到預(yù)定義的group中。

你可以指定同一個group中system的Update的執(zhí)行順序。Group也是一種system,所以你可以將一個group添加到另外一個group中。如果你沒有指定順序,system的執(zhí)行順序會不太確定,并不會按照它們創(chuàng)建的順序。不過,同一個group中的所有system都會比下一個group中的system先執(zhí)行。

System的Update是在主線程中執(zhí)行的,不過可以使用Job將工作分配到子線程中。

ECS世界的可視化創(chuàng)建(authoring)

在Unity中沒辦法直接可視化地創(chuàng)建DOTS世界,但是,可以先用GameOjbect和MonoBehaviour來創(chuàng)建,然后通過轉(zhuǎn)換系統(tǒng)將GameObject轉(zhuǎn)換成實體和組件。這個我們后面也會細講。

來源:Unity官方平臺

原文:https://mp.weixin.qq.com/s/Tvtqz51Np7vWeYwKcQtHdQ

立即登錄,閱讀全文
原文鏈接:點擊前往 >
文章來源:Unity官方平臺
版權(quán)說明:本文內(nèi)容來自于Unity官方平臺,本站不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。文章內(nèi)容系作者個人觀點,不代表快出海對觀點贊同或支持。如有侵權(quán),請聯(lián)系管理員(zzx@kchuhai.com)刪除!
掃碼關(guān)注
獲取更多出海資訊的相關(guān)信息
優(yōu)質(zhì)服務(wù)商推薦
更多