refs: https://zhuanlan.zhihu.com/p/30538626
1 什么是ECS架構
ECS是 Entity-Component-System(實體-組件-系統)的縮寫,其模式遵循組合優於繼承的原則。
- Entity 游戲內的每個基本單元都是一個實體,每個實體里面有多個組件。
- Component 每個組件僅包含代表其特征的數據,例如:移動相關的組件僅僅包含速度、位置、朝向等屬性。一旦一個實體擁有了MoveComponent
組件便可以認為它擁有了移動的能力。
- System 系統是用來處理擁有一個或者相同的幾個組件的實體集合的工具,其只擁有行為(在系統中沒有任何的數據)。在移動相關的組件的例子中,處理移動的系統僅僅關心擁有移動能力的實體,它會遍歷所有擁有MoveComponent
組件的實體,並且根據相關的數據(速度、位置、朝向等),更新實體中的組件的屬性。
實體與組件之間是一對多的關系,實體擁有怎么樣的能力,完全取決於其擁有哪些組件,通過動態的刪除與修改實體中的組件,可以改變實體的行為。
2 ECS的基本結構
一個使用ECS架構開發的游戲的基本結構如下:
world是實體與系統的集合,而實體就是一個ID,這個ID對應了組件的集合。組件用來存儲游戲狀態並且沒有任何行為,系統擁有處理實體的行為但是沒有狀態。
3 詳解實體、組件與系統
3.1 實體
實體是一個概念上的定義,是游戲世界中的一個獨特的物體,是一系列組件的集合,為了方便區分不同的實體,在代碼層面上一般用一個ID來進行表示。所有組成這個實體的組件將會被這個ID標記,從而明確哪些組件屬於該實體。由於其是一系列組件的集合,因此可以在游戲運行的時候在實體中添加一個或者刪除一個組件。
樣例:
- Player(Position, Sprite, Velocity, Health)
- Enemy(Position, Sprite, Velocity, Health, AI)
- Tree(Position, Sprite)
注:括號前為實體名,括號內為該實體擁有的組件
3.2 組件
一個組件是一堆數據的集合,可以使用C語言中的結構體來進行實現。它沒有方法,即不存在任何的行為,只用來存儲狀態。
樣例:
- PositionComponent(x, y)
- VelocityComponent(X, y)
- HealthComponent(value)
- PlayerComponent()
- EnemyComponent()
注:括號前為組件名,括號內為該組件擁有的數據
3.3 系統
系統是游戲中用來處理邏輯的部分,一個系統就是對擁有一個或多個相同組件的實體集合進行操作的工具,它只有行為,沒有狀態,即不應該存放任何數據。
舉個例子,游戲中玩家要操作對應的角色進行移動,由上面兩部分可知,角色是一個實體,其擁有位置和速度組件,那么怎么根據實體擁有的速度去刷新其位置呢,MoveSystem
(移動系統)登場,它可以得到所有擁有位置和速度組件的實體集合,遍歷這個集合,根據每一個實體擁有的速度值和物理引擎去計算該實體應該所處的位置,並刷新該實體位置組件的值,至此,完成了玩家操控的角色移動了。
系統這里比較麻煩,還存在一個常見問題:由於代碼邏輯分布於各個系統中,各個系統之間為了解耦又不能互相訪問,那么如果有多個系統希望運行同樣的邏輯,該如何解決,總不能把代碼復制 N 份,放到各個系統之中。UtilityFunction(實用函數) 便是用來解決這一問題的,它將被多個系統調用的方法單獨提取出來,放到統一的地方,各個系統通過 UtilityFunction 調用想執行的方法,同系統一樣, UtilityFunction 中不能存放狀態,它應該是擁有各個方法的純凈集合。
樣例:
- MoveSystem(Position, Velocity)
- RenderSystem(Position, Sprite)
注:括號前為系統名,括號內為該系統關心的組件集合