繼承體系的問題,為什么要用ECS
面向對象的問題
- 當一個新的類型需要多個老類型的不同功能的時候,不能很好的繼承出來
- 游戲開發后期會有非常多的類,很難維護
- 游戲中子系統很多,它們對一個對象的關注點往往互不相關,比如渲染.網絡,戰斗數據,如果都對應一個基礎角色對象,這個類就會很大
ECS,通過組合而不是繼承的方法來進行實體的構建
- ECS的設計目的是用來把大量的模塊進行集成並解耦,用最小的耦合來集成大量分散的系統
- 每個System可以只關注實體有什么,而不是實體是什么,這是與OOP的最大區別
- 在網絡同步的預測錯誤后可以很方便的糾正(所有元素都用Component分離了)
- ECS的一個重要特性就是並發優勢,因為提供了數據隔離
Unity推薦ESC的原因
- ECS專注於您正在解決的實際問題,即構成游戲的數據和行為。
- 為了配合使用Job System和Burst 編譯器。
- 從以對象為導向的設計轉到以數據為導向的設計,代碼更為容易,也更易於他人掌握。
示例
假設一個簡單的游戲,有石頭,樹,敵人,玩家三種物體
這個時候需要一個新的類型,可以攻擊的樹
- 按照OOP的設計大致是這樣的

- ECS的設計大致是這樣的
EvilTree擁有: Position,AI,Sprite,AABB
ECS結構圖示

ECS基礎規則
* Entity輕量級,甚至只有一個Id;
* System沒有狀態, Component 不帶行為
* System不能調用其他System的函數,共享代碼要放到Utils里(如敵對關系),Utils函數無副作用;
* 組件里復雜的副作用要通過隊列的方式推遲處理,尤其是單例組件;
實體
一個實體指的是存在於你的游戲世界中的物體。實體在代碼上就是一個組件的列表。
由於實體的結構實在是太簡單了,所以很多實現都沒有專門的設計一個實體的數據結構。相反的,一個實體就是一個ID,所有組成這個實體的組件將會被這個ID給標記,從而明確的知道哪些組件是屬於哪個實體的。如果你想的話,你可以在運行時,動態的將組件從實體中移除或者增加一個或多個你感興趣的組件。比如說,如果玩家發出了一個冰系魔法,將敵人凍住,你只要簡單的將它的速度組件移除,那么敵人就靜止住了。
組件
沒有行為(改變數據),只是用來存儲一些數據(全部公開), 每一個組件都描述了實體的某個屬性特征。
每個System會以自己的角度對待組件,不同的觀察者區別對待主體
單例組件: 屬於單一匿名實體,可以直接訪問,存放System大部分狀態.比如Ipnut單例,從InputSys中剝離的數據.
系統
真正處理游戲邏輯的地方.
System不關注實體到底是什么,只關心組件集合,在這個集合上執行一組行為
只會有很少的System改變組件狀態,自己管理復雜性
系統會指明所需要的組件集合,由主邏輯篩選出所有滿足要求的實體
守望先鋒的ECS
EntityAdmin是個World,存儲了一個所有System的集合,和一個所有實體的哈希表(ID為unit32)。
單例組件
示例:命中處理System
- ModifyHealthQueue組件,記錄實體身上所有傷害和治療效果
- MovementState組件,移動數據處理
- 一組Utility函數處理錯誤糾錯,回滾相關Component
幾個建議
- System定義組件的組合時,可以把組件標記為只讀
- 實體生命周期建議立即創建,延時銷毀
- 某些遺留子系統不能接入ECS時,就不要強行接入,保持子系統的整潔
- 事實證明,網絡同步真的很復雜,所以必須盡可能的與引擎其余部分解耦,ECS是解決這個問題的好辦法。
Unity中的ECS
在PackageManager中下載Entities
