[UE4]淺談UE4 Modules

對於大型軟體系統而言, 各別功能之間的相依性控管一直都不是個簡單的問題。 為了減少程式碼在管理上的複雜度, 導入Module這個概念,將整個系統拆分成各別獨立且互相緊密合作的單位, 變成了一個非常常見的設計手法。

其實就目前而言,C++還沒有一個真正意義上的「Module System」, 雖然在 C++20[1] 的標準中已經有加入Module相關機制的草案, 但很顯然的,UE4所使用的Module機制並不是對標準庫中該草案的實現。

作為一個走在技術前端的引擎,很多功能當然不會等到標準庫穩定後才導入。 UE4所實現的這套Module System並不依賴於編譯器的實作, 相對的,它在現有的C++標準庫的基礎上設計了自己的一套跨平台建構系統,Unreal Build Tool(UBT)。 這套建構系統,不僅可以控管每個Module載入的時機,並且也擁有許多編譯優化機制。

為了正確理解UE4中這套Module System的運作方式, 首先,我們必須要先理解到底什麼是「Module」。

在抽象概念上,所謂的Module,其實就是一些功能的「集合」。 在C++中,我們可以想成它是一堆 Translation Unit(或稱Compliation unit) [2] 所產生的.obj檔的集合;而這些.obj檔的集合,就是UE4中所定義的Module。

 

接著,UBT根據不同的編譯環境有以下幾個選擇:

  • 將Module所包含的.obj檔編譯進到最後的.exe檔中。
  • 將Module所包含的.obj檔做成Static Library(.lib)。
  • 將Module所包含的.obj檔做成Dynamic Library(.dll)進而實現在Runtime時期動態載入功能。

UE4在預設情況下,若編譯的TargetType是Editor, 則TargetLinkType會設成Modular,意即所有的Module都會被做成(.dll); 若是TargetType是其他的型態,引擎則會視所編譯的Module是PluginModule還是GameModule進行不同的動作。 前者會被做成(.lib),後者則會直接編譯連結進到最後的exe檔中。

為什麼Editor要特別設定成Modular形態? 這是UE4為了實現「 HotReload[3]  」而特別進行的設計。這個功能的目的, 就是希望能夠讓我們在editor中修改C++程式碼後就馬上看到結果。 因此,為了實作動態載入修改後程式的邏輯,就只能在Editor模式下將Module編譯成Dynamic Library才行。

 

當然我們也可以在Target.cs中將LinkType手動設定成TargetLinkType.Modular, 這樣不管TargetType是不是Editor,所有的Module都會編譯成dynamic library。 只是,若是想要更改這項設定,就必須要取得引擎的源碼自行編譯才行,不然會產生編譯錯誤。

所有關於個別Module的編譯行為,都可以在Build.cs這個檔案中進行設定, 例如這個Module與其他Module的相依關係、 其相依專案include檔所存放的相對路徑、其他各種編譯設定、想要傳入的Compiler definitions, 又或者這個Module必須去載入外部編譯好的External Library也都是在這裡進行設定。

Target.cs跟Build.cs分別管控不同層級的編譯行為, Target.cs比較高層, 裡面所設定的編譯選項會影響到所有需要編譯的module, 並決定最後編譯成品的型態(TargetType); 而Build.cs則是較底層,用來決定各別module的編譯方式, 擁有一些編譯選項會覆寫Target.cs所設定的內容。

最後,下圖為從源碼中截取的幾段本文所提到的enum。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[1] 見wikipedia C++20,本章撰寫時UE4已全面支援C++14

[2] Translation Unit是由一個cpp與所有include展開後的header檔,最後會產生一個object file

[3] HotReload: https://wiki.unrealengine.com/Hot_Reload

Leave a Reply

Your email address will not be published. Required fields are marked *