備審寫一寫還是忍不住手癢了,又去研究了一下 DOD,從頭用寫 ECS 架構做出簡單的遊戲 (簡單到連 game loop 都沒有)
之前有說過了 DOD 了,這次的重點也是在 ECS 上,所以稍微帶過 DOD
資料導向程式設計 Data Oriented Design 簡單來說就是透過改變資料結構的方式,減少 CPU 讀取資料時的快取未命中 cache miss,在巨量的遊戲實體中回收出可觀的效能。
基本上工廠類遊戲都得用這種方式做,光資料讀取時發生的大量 cache miss 就把效能拖垮ㄌ。
像異星工廠 factorio 就是開發者自己寫的 DOD 引擎 (沒記錯的話),
還有前陣子超強的那個戴森球計畫 dyson sphere program 就是在 Unity 裡自己搞 DOD,然後搭配 Compute Shader 用 CPU 並行 Update,所以遊戲才支撐的住 10 萬多個物件。
終於看的懂這張圖了ㄏㄏ
資料導向和物件導向差距很大,基本上相反了吧?
幹 DOD 是什麼有夠反人類的設計方法阿,現在看 OOP 真的有夠和藹實體、組件、系統 Entity Component SystemECS 就是一種符合 DOD 規則的設計方法
- 實體 Entity,組件的容器,不具有任何狀態與邏輯,基本上一個 int 存 index 就算 Entity 了
- 組件 Component,儲存實體的各種狀態 State,像 Transform(position、rotation、scale)、RigidBody(mass、drag、velocity),但不具有邏輯和行為。這也是和常規 OOP 組件模式不同的地方。
- 系統 System,行為和邏輯的部分就是 System 負責的,System 會尋找自己需要的組件,執行 Update 工作。系統自己也不會儲存任何狀態(變數),而系統與系統之間也不會互相溝通。
system have no state, component has no behaviour
與常規 OOP 不同的地方在於,OOP 通常都是一整條陣列存遊戲物件,然後
gameObject.Update()
{
RigidBody.Update();
Transform.Update();
Renderer.Render();
}
ECS 變成用好幾條陣列個別存 Component,實體只是 index,更新會由 System 找出需要的組件,直線遍歷陣列。
ECS 模式有幾個好處第一就是 DOD 的效能優勢,因為組件都擺在一起,資料結構的很緊湊,所以減少 cache miss 的的效能開銷 (但只有在實體數量大到一定量級才會顯現)
再來是並行有好,因為 System 不保存狀態,基本上同時對好幾個實體做 Update 也不會有問題。或是兩個需求 Component 不同的 System 也可以同時進行更新。
第三則是程式架構上的優勢,雖然 Component 裡所有變數都是 public 的,但 ECS 的架構方法不容易產生 OOP 那種亂七八糟的耦合。
缺點,主要都是 DOD 規範引起ㄉ第一就是不夠直覺(DOD),OOP 之所以最廣泛就是因為他的邏輯相對貼近現實ㄌ
不允許多形(DOD),多型的抽象調用會產生 cache miss,但如果不要求效能應該沒差
再來是物件的生命週期不好處理...牽涉到一堆東東,我還沒辦法解釋難在哪裡
最後是溝通困難,實體之間要交互變的比較困難,畢竟規範嚴格...也可能是我還不夠熟悉吧
學藝不精,如果理解有有任何錯誤情麻煩指正 Orz
總之為了深入研究,我從頭搞了一個 ECS 架構的遊戲,參考 Unity ECS 的做法。弄了個躲球球的遊戲,光這樣就弄好幾天==
實體基本沿用 Unity GameObject,只是其他組件都用 ECS 模式做。然後我的組件和 MonoComponent 混用,要和 Unity Component 完全隔離太難ㄌ。
實體 EntityBase兩個組件陣列,Unity 的和自己的,基本上是初始化要給 System 檢測用的,後續 Update 都不會從 Entity 接觸 Components
組件 IComponent基本上就是繼承特定 intrface 的 struct,用來確保多型 <T> 不會吃到奇怪的東西。
系統 SystemBase初始化的時候系統會掃過一次整個實體陣列,找哪些是要被自己更新的,然後存入 system 自己的的索引陣列。等更新的時候就根據 index array 跳著找A。
更新方法 GameEnities比較尷尬的是我得手動建立每種組件的陣列,找不出適合的方法自動產生各種類型的陣列(如果要避免多型)
System 結果還是用多型搞ㄌ,我弄了一個可以用字串找出 subclass 的自訂編輯器,還可以排序執行順序。白色正常、黃色是重複、紅色是找不到 class
Update 的時候,就讓 System 照順序更新而已,比較大的問題還是 System 得和 GameEnities 直接接觸,不知道 Unity ECS 那個 Entities.Foreach(ref Component)是怎麼做到ㄉ
其實我原本想讓玩家可以射子彈ㄉ,但做不出來,所以免強改成敵人跑來跑去讓玩家躲的樣子
開始懂為甚麼 Unity DOTS 毛一堆了,有夠難做 ==
渲染和碰撞都還是用 unity 原生程式做的,除非自己完成這兩項,不然不可能和 monobehaviour 分離
專案放 github 上,有興趣自己看 code ㄅ
我覺得我該找時間整頓下我的 github
就醬
這陣子都在忙備審和主專案山鴨,一段時間沒研究新東西我整個人都不好了,所以搞了這東東來回復些能量
繼續寫備審 _(:3 」∠ )_