ECS框架学习
DOTS
Unity DOTS是Unity官方基于ECS架構(gòu)開發(fā)的一套包含Burst編輯器和JobSystem的技術(shù)棧,它旨在充分利用多核處理器的特點(diǎn),充分發(fā)揮ECS的優(yōu)勢(shì)。
安裝
Entities、Burst、Jobs、Hybrid Renderer(必選,用于DOTS的渲染相關(guān))、Unity Physics(非必選,用于DOTS的高性能物理組件)
com.unity.Entities
com.unity.rendering.hybrid
ECS框架
實(shí)體(Entity):ID和組件列表
組件(Component):存儲(chǔ)Data
系統(tǒng)(System):對(duì)象關(guān)聯(lián)的Component進(jìn)行操作
適用情況:需要處理超多對(duì)象的同屏渲染問(wèn)題,如戴森球計(jì)劃;需要處理超多對(duì)象的管理和操作,如FPS游戲。
傳統(tǒng)Unity開發(fā),通常是把邏輯腳本掛載到GameObject上,很多時(shí)候還可能掛載多個(gè)不同功能的邏輯腳本。而ECS采用面向數(shù)據(jù)的方法,Entity代替了傳統(tǒng)的GameObject的概念,Component則保留了傳統(tǒng)組件的數(shù)據(jù)部分,所有邏輯交由System處理。寫ECS的基本思路就是創(chuàng)建Component、創(chuàng)建Entity、編寫System處理邏輯。
System可以重寫OnUpdate函數(shù),大家可以把它看成是MonoBehaviour的Update函數(shù)。這就是之前提到的,ECS把實(shí)體的邏輯專門放到System里處理了。OnUpdate函數(shù)也是會(huì)每幀調(diào)用一次的。
Component繼承IComponentData
Entity
創(chuàng)建方式
- [GenerateAuthoringComponent] 自動(dòng)將組件轉(zhuǎn)換成實(shí)體
- [RequiresEntityConversion] 通過(guò)EntityManager的AddComponentData將組件附加到實(shí)體上
- 通過(guò)Prefab創(chuàng)建實(shí)體 *方便
- EntityCommandBufferSystem *靈活
Component
Component需要繼承IComponentData
其他組件類型
ISharedComponentData(共享組件) // 生成大量相同怪物 ChunkComponent(塊組件) ISystemStateComponent(狀態(tài)組件) // 處理回調(diào)事件 ISystemStateSharedComponentData(狀態(tài)共享組件) IBufferElementData(動(dòng)態(tài)隊(duì)列/緩沖區(qū)/數(shù)組)System
System需要繼承ComponentSystem或JobComponentSystem
- ComponentSystem更適合舊項(xiàng)目遷移到ECS框架,類似于Monobehaviour,但ECS的性能在多線程下才能發(fā)揮到最大
- JobComponentSystem配合各種Job(IJobForEach、IJobChunk等),可以方便地實(shí)現(xiàn)并行(多線程、多核)執(zhí)行邏輯
System中可以重寫了OnUpdate(),該函數(shù)每幀調(diào)用一次,類似mono的Update()。System相當(dāng)于將一定的邏輯封裝起來(lái),例如RunSystem、JumpSystem等,那對(duì)應(yīng)的問(wèn)題就是System如何知道哪些Entity需要執(zhí)行邏輯,答案就是篩選,而篩選條件就是Component。
篩選方式
- Entities.ForEach
- IJobChunk
- IJobForEachWithEntity
- EntityQuery *靈活 All Any None
Entity Debuger
不同于傳統(tǒng)Debug,ECS框架下需要用Entity Debuger進(jìn)行調(diào)試,Unity菜單的Window->Analysis->EntityDebugger,可以打開ECS的調(diào)試窗口
Chunk和Archetype
對(duì)于擁有相同Component的Entity,ECS采用Chunk結(jié)構(gòu)統(tǒng)一存儲(chǔ),例如下圖中的EntityA和EntityB
Chunk不能無(wú)限存儲(chǔ)Entity,當(dāng)Entity數(shù)量過(guò)多時(shí),就需要用新的概念A(yù)rchetype來(lái)統(tǒng)一保存Chunk。
World
ECS會(huì)自動(dòng)創(chuàng)建一個(gè)默認(rèn)世界,包含了實(shí)體管理器(EntityManager)以及項(xiàng)目中所有可用的系統(tǒng)(System)。
系統(tǒng)分組(ComponentSystem Group)
System用來(lái)處理游戲邏輯,游戲邏輯肯定就會(huì)涉及到先后順序,為了解決這類問(wèn)題,System的OnUpdate函數(shù)的執(zhí)行是有先后順序的。
// ECS默認(rèn)分組 InitializationSystemGroup(負(fù)責(zé)初始化工作的系統(tǒng)分組) SimulationSystemGroup(負(fù)責(zé)邏輯運(yùn)算的系統(tǒng)分組) PresentationSystemGroup(負(fù)責(zé)圖形與渲染工作的系統(tǒng)分組)使用特性(Attribute)調(diào)整System執(zhí)行順序
UpdateInGroup:指定當(dāng)前System在哪個(gè)分組下 UpdateBefore:指定當(dāng)前System在哪個(gè)System之前執(zhí)行 UpdateAfter:指定當(dāng)前System在哪個(gè)System之后執(zhí)行EntityCommandBuffer
JobComponentSystem配合各種Job(IJobChunk等),可以方便地實(shí)現(xiàn)并行(多線程、多核)執(zhí)行邏輯。多線程邏輯中會(huì)出現(xiàn)數(shù)據(jù)一致性的問(wèn)題,ECS規(guī)定,以下行為都不能在Job中處理:創(chuàng)建實(shí)體(Create Entities)、銷毀實(shí)體(Destroy Entities)、給實(shí)體添加組件(Add Components)、刪除實(shí)體的組件(Remove Components),需要采用EntityCommandBuffer提前或延遲執(zhí)行。
每一個(gè)系統(tǒng)分組下都有兩個(gè)EntityCommandBufferSystem,并且分別都是Begin和End對(duì)應(yīng)的,通過(guò)World獲取System。
笨木頭的博客里講到“在JobComponentSystem中,如果Job沒(méi)有篩選出實(shí)體數(shù)據(jù),那么,OnUpdate是不會(huì)被調(diào)用的”,這也就解釋了為什么有些system最后會(huì)執(zhí)行DestoryEntity操作。這就是面向數(shù)據(jù)編程的關(guān)鍵,程序根據(jù)數(shù)據(jù)篩選實(shí)體,執(zhí)行邏輯,刪除數(shù)據(jù)保證后面篩選不到。(我需要執(zhí)行邏輯就添加,執(zhí)行完了刪掉,完全是數(shù)據(jù)有無(wú)在控制邏輯的有無(wú))。
EntityCommandBufferSystem每次執(zhí)行隊(duì)列的任務(wù)后,都會(huì)清空,所以不存在重復(fù)執(zhí)行的問(wèn)題。
參考鏈接
官方Demo ECS Samples
笨木頭與游戲開發(fā) 博客
總結(jié)
- 上一篇: 求最长回文串-从动态规划到马拉车之路(上
- 下一篇: 与非CCR代码互操作