Unity DOTS中ECS核心架構(gòu)詳解
最近DOTS終于發(fā)布了正式的版本,?我們來(lái)分享一下DOTS中ECS的幾個(gè)關(guān)鍵概念與結(jié)構(gòu),方便大家上手學(xué)習(xí)掌握Unity?DOTS開(kāi)發(fā)。 ?
ECS中的World
??Unity?DOTS?ECS架構(gòu)中所有的Entity都是被放到了World對(duì)象里面,每個(gè)Entity在World里面都有唯一的Id號(hào)。Unity?DOTS?可以同時(shí)支持很多個(gè)World,?DOTS會(huì)在運(yùn)行的時(shí)候創(chuàng)建一個(gè)默認(rèn)的World。World包含了它所需的所有System,?System迭代計(jì)算的時(shí)候,使用World里面的Entity中的Component數(shù)據(jù)。如果不想要一運(yùn)行就創(chuàng)建一個(gè)默認(rèn)的World,我們可以通過(guò)以下宏開(kāi)關(guān)來(lái)控制: #UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD 關(guān)閉運(yùn)行模式下啟動(dòng)的時(shí)候,創(chuàng)建一個(gè)默認(rèn)的World; ? #UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_EDITOR_WORLD 關(guān)閉編輯器模式下創(chuàng)建默認(rèn)的World對(duì)象; #UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP 關(guān)閉掉編輯器模式/運(yùn)行模式下的默認(rèn)的World的創(chuàng)建;
EntityManager對(duì)象
??每個(gè)World,?有且只有一個(gè)EntityManager對(duì)象。EntityManager對(duì)象負(fù)責(zé)Entity的管理,包含但不限于:?創(chuàng)建Entity,?銷(xiāo)毀Entity,?修改Entity中的數(shù)據(jù)等。
Entity對(duì)象
????DOTS?中的每一個(gè)實(shí)體,我們稱(chēng)為Entity,?它本身是一個(gè)容器,可以理解為一個(gè)輕量級(jí)的gameObject,?它的數(shù)據(jù)全部存放在它的對(duì)應(yīng)的組件里面。Entiy里面的所有組件數(shù)據(jù)內(nèi)存會(huì)并行一起排布。這樣保證了Entity?數(shù)據(jù)內(nèi)存的高效訪(fǎng)問(wèn)與Cache命中。相當(dāng)于把Entity所包含的所有組件數(shù)據(jù)的內(nèi)存打包到一個(gè)內(nèi)存塊中。比如一個(gè)A類(lèi)Entity,?它包含了有M,N兩個(gè)組件數(shù)據(jù),當(dāng)我們給Entity來(lái)分布組件數(shù)據(jù)內(nèi)存的時(shí)候,是把M+N兩個(gè)組件的內(nèi)存看作一種類(lèi)型,線(xiàn)性排布在一起分配。存儲(chǔ)數(shù)據(jù)的時(shí)候M,就操作這個(gè)內(nèi)存塊的M的部分。操作N組件數(shù)據(jù)的時(shí)候,就操作這個(gè)內(nèi)存塊的N的部分。Entity中的所有組件組成的內(nèi)存塊看作的這個(gè)類(lèi)型,我們叫做ArchType。相同類(lèi)型的Entity(所有組件的組成結(jié)構(gòu)相同)屬于同一種ArchType。 ?
ArchType與Chunk高效的內(nèi)存分配器
高效的內(nèi)存分配器需要具備幾個(gè)特點(diǎn): ??1:?高效地分配與釋放; ? ??2:?避免大量的分配與釋放后造成的內(nèi)存碎片; 上文提到,Entity中的所有組件數(shù)據(jù)是排布在一塊內(nèi)存里面的,每一種不同的排布,就會(huì)對(duì)應(yīng)一種"類(lèi)型"ArchType,?DOTS?高效的內(nèi)存分配器只需要基于ArchType所對(duì)應(yīng)的內(nèi)存塊大小來(lái)進(jìn)行分配就可以了。DOTS的entity組件數(shù)據(jù)的內(nèi)存分配器基于Chunk設(shè)計(jì),每個(gè)Chunk的大小為16kb,每個(gè)Chunk只會(huì)分配同一種類(lèi)型的ArchType,?根據(jù)ArchType的組件組合,我們就可以計(jì)算出它們一共所需要的內(nèi)存大小,我們就從Chunk中固定分配對(duì)應(yīng)的內(nèi)存大小就可以了,這樣Entity對(duì)應(yīng)的所有組件內(nèi)存非常高效的分配與釋放,同時(shí)每種Chunk只存放一種ArchType類(lèi)型的內(nèi)存塊,每次分配的內(nèi)存大小都是一樣,這樣可以避免內(nèi)存碎片?;贏rchType的內(nèi)存排布如下所示: =============================== ? ArchType1: chunk1【e1(c1c2),e2(c1c2),e3(c1c2)】 chunk2【e4(c1c2),e5(c1c2),e6(c1c2)】 ... =============================== ArchType2: chunk1【e1(c3c4),e2(c3c4),e3(c3c4)】 chunk2【e4(c3c4),e5(c3c4),e6(c3c4)】 ... =============================== ArchType3:? chunk1【e1(c5c6),e2(c5c6),e3(c5c6)】 chunk2【e3(c5c6),e4(c5c6),e5(c5c6)】 ... ===============================
System與JobSystem
???我們學(xué)C語(yǔ)言的時(shí)候,聽(tīng)到過(guò)一句名言,程序=數(shù)據(jù)結(jié)構(gòu)+算法。Entity解決了數(shù)據(jù)存儲(chǔ)的問(wèn)題,System就是算法。算法所需要的數(shù)據(jù),來(lái)源于Entity中的Component。DOTS提供機(jī)制,System可以訪(fǎng)問(wèn)到entity中的組件數(shù)據(jù),拿到這些數(shù)據(jù)后再做邏輯迭代計(jì)算與處理。默認(rèn)System是運(yùn)行在Unity的main?thread上的,為了發(fā)揮多核優(yōu)勢(shì),把可以用多線(xiàn)程處理的任務(wù)使用多線(xiàn)程,Unity?還提供了JobSystem機(jī)制,通過(guò)多線(xiàn)程的線(xiàn)程池來(lái)迭代計(jì)算JobSystem,不放main?thread上提升程序的效率。 ? ? 總結(jié)如下: ? DOTS中會(huì)有一個(gè)World對(duì)象,每個(gè)Word對(duì)象會(huì)有一個(gè)EntityManager負(fù)責(zé)Entity的管理,內(nèi)部使用了高效的基于ArchType與Chunk機(jī)制的內(nèi)存分配。所有的system會(huì)加入到World里面來(lái)進(jìn)行統(tǒng)一迭代,System可以訪(fǎng)問(wèn)Entity中的Component數(shù)據(jù)。同時(shí)JobSystem可以讓我們的算法迭代基于多線(xiàn)程處理。配上總結(jié)圖如下: