關於軟體寫作風格被引擎限制的問題

1. 命題

這篇文章主要想要討論的是軟體實作被遊戲引擎限制的問題。
要討論這個問題,我們首先要知道「軟體實作(implementation)」在這個語境下指的是什麼

2. 軟體實作 (Implementation)

在這裡,軟體實作具體是指那些實現了 interface 或 abstract class 的 class 和 method,它們提供了實際的功能和行為。這些具體實作(concrete implementation)是抽象層背後的詳細實現,負責執行特定的功能。

理解為什麼需要具體實作,主要是因為現行的軟體設計模型通常強調系統設計的抽象化,通過 interface 定義功能契約,將系統依賴於抽象層而非具體實現,以提高靈活性和可擴展性。在這種模型中,我們希望 code to interface 而不是 code to implementation。這樣做是為了確保系統的靈活性和可維護性。

如果沒有抽象層,所有軟體功能將直接由具體實作來提供。這樣,物件之間的依賴關係就會變成實作依賴實作(concrete implementation depending on concrete implementation)。這種設計會導致高度耦合,使軟體功能的調整和擴展變得困難且繁瑣。

3. 遊戲引擎存在的淺在問題

在遊戲開發中,常用的遊戲引擎(如 Unity 和 Godot)提供了便捷的框架和工具,這讓開發者能夠迅速啟動項目並進行開發。這些引擎的設計通常依賴於自帶的設計模式,如 MonoBehaviour 或 Node。雖然這些模式能夠加速開發進程,但也可能會對開發者的創意和靈活性造成一定的限制,並且不完全符合傳統軟體工程的最佳實踐。

問題概述

設計模式的依賴性

Unity: 開發者的遊戲腳本通常依賴於 MonoBehaviour 來進行更新和行為管理。這種依賴雖然簡化了開發流程,但也會限制開發者在設計和實現特定功能時的靈活性。
Godot: 類似地,開發者需要使用 Node 系統來組織遊戲對象和管理行為,這種依賴性同樣限制了開發者的設計自由度。

框架的限制

遊戲引擎提供的框架通常預設了一種固定的遊戲架構,開發者需要在這個架構內工作,這可能限制了開發者根據具體需求設計自定義架構的能力。
與傳統軟體開發相比,遊戲引擎強加的設計模式和框架可能會增加開發者理解和追蹤系統行為的複雜度。

具體問題解析

1.方法的局限性

在 Unity 中,Update 方法是一種常見的模式,幾乎每個遊戲對象都會通過這個方法來更新其狀態。這樣做雖然簡單易用,但並不一定是所有情況下最有效的做法。
開發者需要思考:每個對象真的需要一個 Update 方法嗎? GameLoop 的實現是否應該僅限於 Update 方法?

2.依賴關係的管理

在使用 Unity 開發時,如果一個對象需要與其他對象進行互動,通常需要通過 MonoBehaviour 來設置依賴關係。這通常涉及在引擎中通過 [SerializeField] 屬性來管理對象之間的引用。
這樣做會導致依賴關係的管理變得複雜,因為理解一個對象的行為不僅需要查看程式碼,還需要在引擎中檢查對象之間的具體關聯設置。

3.與傳統軟體開發的對比

在傳統軟體開發中,系統的行為通常可以通過查看程式碼來完全理解。而在使用遊戲引擎時,理解一個系統的運作往往需要同時查看程式碼和引擎裡的設定。
例如,在傳統軟體中,對象之間的依賴關係和交互通常通過程式碼的 interface 和 abstraction 來管理,這樣可以更清楚地追蹤和理解系統行為。而在遊戲引擎中,很多行為和依賴關係是通過引擎裡的設置來管理的,這增加了理解和維護的難度。

優勢與挑戰

遊戲引擎的優勢

提供了豐富的工具和框架,簡化了開發流程,讓開發者能夠更快地建立和測試遊戲原型。
提供了多種高級功能(如物理引擎、渲染系統等),這些功能在傳統軟體開發中通常需要額外的開發時間和資源。

遊戲引擎的挑戰

創意和靈活性的限制: 固定的設計模式和框架可能限制了開發者根據具體需求進行創新設計的能力。
依賴管理的複雜性: 需要在程式碼和引擎設置之間切換來管理對象之間的依賴關係,增加了系統的複雜性和維護難度。

4. 衍伸討論,同時也是我自己的想法小結

上面提到的這些問題在當你想要使用軟體工程中的想法,比方說你想避免使用 singleton,所以想要用 dependency injection 來解決依賴關係的問題,或是比方你更想要追求高內聚、低耦合的程式碼的時候,或是你想要實現某些設計模式的時候,或是你被 SOLID 荼毒已久想要追求 SOLID 的時候,你會發現這些動作每每做一次都會受制於遊戲引擎給你的框架而模糊了你原本的意圖。

也是因為才會有噗首在說的「軟體實作被遊戲引擎綁住這件事情真的有夠白癡」這句話。

於是問題會變成有沒有可能在使用遊戲引擎提供的種種功能的前提下,讓我們自己有辦法控制物件的依賴關係跟遊戲的流程關係。
實務上是可能的,不過基本上作的方式都是在遊戲引擎上開洞的形式,通常還是會有一個 script 是用來作為 main function,但在那之下的實作我們就都以自己的架構去實現──只在需要遊戲引擎的幫助的時候,再來看要怎麼和引擎層面的資料互動。
這種作法本質上是把遊戲引擎當作某種 View 層的模式在運作,透過一些軟體介面來橋接引擎中的東西。不過一旦牽扯到比方物理引擎的部分,整個問題就會變得有些複雜(這個問題我自己也還沒想好要怎麼辦)