關於 web service, unity, blogger 等軟體工程筆記

Unity C# Inheritance vs Composition 繼承與組件式設計之戰鬥系統經驗談

Edit icon 沒有留言
Slime with sword

基於一些機緣而回憶之前身為初心者所掉過的坑,到底應該是使用繼承 (Inheritance) 抑或是組合 (Composition) 方式來設計遊戲中的戰鬥系統,哪一種比較適合持續變動的遊戲開發

故事從菜鳥開始

一切的開始得從開發的巨獸浩劫 (Monter Eye) 遊戲回憶起,一款大型機台上的光槍遊戲,玩家在包廂機台內,控制槍枝硬體瞄準遊戲內的怪物,開槍射擊擊倒怪物,便隨著劇情起伏有著噴氣與機台平台震動等效果。

當初團隊在開發這款專案時,技術主力從 OGRE C++ 轉換到使用 Unity C# 開發,軟體團隊對於 Unity 也都只是剛起步學習,身為一位職場新鮮人,也是跟著從頭開始學習起。

一邊進行遊戲架構的開發設計,一點跟著團隊一起學習如何使用 Unity,想當然爾在沒有任何資深前輩可以傳承經驗的情況下,依賴過往在研究所以及大學的專案經驗,將遊戲企劃的設計需求拆解,大量使用物件繼承 (Object inheritance) 來設計整個戰鬥系統架構。

(如果要問為什麼一個職場菜鳥,剛開始就能負責接近遊戲核心的邏輯,主要原因是自找的,相信自己可以 Carry 全團隊,承接各種工作內容,細節可以見這篇文章:在鈊象遊戲業工作三年心得

物件繼承 (Object inheritance) 設計

一開始,根據遊戲企劃的初始規劃設計出以下架構(這是一個演示的範例,實際上更加複雜),分別為一般怪物 Monster 以及魔王 Boss

一開始的設計

一開始的設計

經過企劃們幾次的討論與規格修改,修改關卡以及世界觀,又新增新的怪物類型,會游泳的魔王 SwimBoss 以及飛行的一般怪物 FlyMonster,嘗試使用物件繼承來實作:

調整後的設計

調整後的設計

在展示給大老闆看後,發現游泳的遊戲效果很好,因此追加新的會游泳怪物,依賴物件繼承設計多出 SwimBoss 以及 SwimMonster,開始產生錯亂,這兩個差別在哪裡:

再次追加的設計

再次追加的設計

又不知道哪個企劃突發奇想,想要追加游泳又會飛的一般怪物,原先的繼承設計都不知道該怎麼修改了:

已經不知道該怎麼辦的設計

已經不知道該怎麼辦的設計

但隨著專案時程的進程,漸漸發現遊戲企劃在每個階段後,都會冒出新的想法,規劃出新的需求,而這些要求在以物件繼承的設計架構裡,根本是難以修改與實現,十分痛苦……。

物件組合 (Object composition) 設計-採用 Components

得感謝主管帶我去參加 IGD Share 的分享會,從石川将光先生的分享中,認知到有新的設計可能,以及團隊的包容給我至少一個月的時間 (有點忘記確切花多少時間),重新翻修打掉之前花了之前至少六個月磨合的戰鬥系統,將原先採用物件繼承的設計架構,全部改用物件組合 (Object composition) 的設計架構,大量使用 Unity 組件設計模式 (Components pattern)。

把每一項功能設計成組件運作,利用多個組件在遊戲物件中 (GameObject) 來組合出想要的怪物敵人,不管是要游泳或是飛行,甚至要升級成 Boss,新增移除組件來達成,或是暫關閉開啟其組件 (MonoBehaviour.enabled = false) 來達到暫時的能力效果:

怪物組件式的設計,功能獨立成單一組件

怪物組件式的設計,功能獨立成單一組件

之後專案新功能都採取如此設計,為了讓遊戲企劃編輯方便,也得為這些新功能加入客製化的 Unity 編輯器,當然使用繼承 (Inheritance) 的類別設計還是會有,但不像以前那樣的完全倚賴,使得程式開發較能夠跟上專案的時程,一直不斷得修改功能。

有設計過的組件,其修改彈性才會高,怎麼根據需求設計有彈性的組件架構,那又一個很長的故事與經驗,但大體還是依據高內聚與低耦合 (high cohesion and low coupling) 的原則來設計,這裡簡單筆記幾個點:

  • 不會有互相耦合的組件 (耦合)
    • 例如不該出現:組件 A 需要呼叫組件 B 播放動畫的函數,當組件 B 播放動畫完後,會呼叫組件 A 的函數通知動畫已結束
    • 使用 event 或是 interface 來降低不同組件間的耦合度
  • 單一組件功能過於太複雜 (內聚)
    • 組件程式碼不會包山包海,什麼功能都會放在一起
  • 過度設計 (Over-design)
    • 一件功能不需要設計太多的組件來完成
    • 例如:一個扣血的功能,設計需要數十個組件運作才能完成…

Reference

沒有留言: