[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形態? … Continued

為UE4++加入程式碼:初探UObject

前面幾個章節談了許多引擎在設計上的「為什麼」,但實際上我們到底該怎麼為專案加入C++類別? 基本上你可以純手動在${PROJECT_NAME}\Source\${PROJECT_NAME}下面新增.h跟.cpp檔,只不過會比較費力;但若所繼承的類別是屬於引擎預設支援的類別,你也可以選擇使用Editor中的工具。由於用Editor建立出來的程式碼會自動的套用所選擇繼承類別的模版,因此可以省下不少功夫。 UObject是整個UE4引擎中的核心類別,大部份引擎中的功能都是繼承這個類別後再開始發展各別的系統。 首先,讓我們先來看看該怎麼繼承這個類別。 點選左上角的File->New C++ Class後就會跳出視窗選擇欲繼承的類別,然後照著Figure 1.10.1、Figure 1.10.2、Figure 1.10.3的指示將類別建立出來。 Figure 1.10.1 在Editor中選擇File->New C++ Class後彈出的畫面,這裡只會出現常用類別,請接著選取右上方的Show All Classes。 Figure 1.10.2 選取右上角的Show All Classes之後,就會顯示所有引擎支援的模版類別,接著選取Object,按下Next。 Figure 1.10.3 選取成Public後,.h檔跟.cpp檔就會自動被放到Public跟Private中對應的資料夾。這裡我們在Public下建了一個叫Basic的資料夾,因此建出的MyBasicObject.h會被放到Public\Basic,而MyBasicObject.cpp則會被放到Private\Basic下面。 按下Create Class按鈕後,引擎就會根據內部設定好的Template幫我們將新的類別置放到所選擇的資料夾下面。對於引擎類別的Template有興趣的讀者可以查看${EngineVersion}\Engine\Content\Editor\Templates\這個資料夾下面的檔案,基本上就是根據這些檔案做字串置換的動作而已。 接著我們就可以在Source資料夾下找到建立出來的檔案,見Code 1.10.1。 UObject類別宣告 1. #pragma once 2. #include “UObject/NoExportTypes.h” 3. #include “MyBasicObject.generated.h” 4. … Continued

[UE4] Visual Studio編譯選項

打開Visual Studio後,我們在專案的組態中可以發現有好幾種編譯組態可以選擇,見Figure 1.12.1、Figure 1.12.1。 Figure 1.12.1 從Epic Games Launcher下載的版本,預設有5種編譯組態可以選擇。 Figure 1.12.2 從Github上下載引擎版本自己編譯的話,則會發現多了非常多的編譯選項。 每一個編譯選項主要是由二個關鍵字所組成,第一部份是用來描述引擎跟我們遊戲專案要用什麼狀態來編譯,例如DebugGame、Development跟Shipping。第二部份則是要編譯目標,主要是對應到Source資料夾下的Target.cs。讓我們先看看以下幾個例子: DebugGame:會使用${ProjectName}.Target.cs(TargetType.Game)中的相關設定資訊進行編譯,產生標準的standalone執行檔,在unreal的架構中預設是listen server,意即這個執行程序既是server也是client,別人若知道該listen server的ip,就可以使用open指令加入遊戲,例如:open 192.168.xx.xx:7777。 DebugGame Editor:會使用${ProjectName}Editor.Target.cs(TargetType.Editor)中的相關設定資訊進行編譯,產生帶有Unreal Editor的執行檔。 選擇DebugGame Server:這個組態預設並不存在,當我們在Source下建立完${ProjectName}Server.Target.cs後就會出現,選擇後會使用${ProjectName}Server.Target.cs(TargetType.Server)中的相關設定資訊進行編譯,產生Dedicated Server執行檔。 DebugGame Client:這個組態預設並不存在,當我們在Source下建立完${ProjectName}Client.Target.cs(TargetType.Client)後就會出現,選擇後會使用${ProjectName}Client.Target.cs中的相關設定資訊進行編譯,移除所有server端的能力,產生純粹的客戶端執行檔。 藉由編譯方式跟編譯目標的組合,我們可以得到各種不同的建置組合以符合不同的使用情境。其中若是我們使用的是Github上引擎版本的話,還會多種Debug跟Test這二種建置方式,到底它們的用途是什麼?表格 1表格 2跟整理了所有的建置狀態跟建置目標: 表格 1 建置狀態,主要是用來決定產生的執行檔是不是要進行最佳化。 建置組態 – 狀態  描述 Debug 這個建置組態,會將整個「引擎」跟「Game Code」用最方便除錯的方式進行編譯,其中會包含所有的Debug Symbol,程式碼不會進行任何的最佳化,因此非常適合進行完整的斷點除錯。只是,由於我們必須將整個UE4的原始碼從Github上拉下來自己編譯,因此非常的耗時。從Epic Games … Continued

[UE4] 認識UE4專案目錄架構與GamePlay Module

在建完專案之後,在專案底下看到下面幾個Folder跟檔案,見Figure 1.4.1: · .vs:為visual studio產生的meta data。 Binaries:用來存放UE4專案建置出來的東西(build artifact),如dll、exe跟debug用的pdb……等等。 Config:用來存放專案的各種設置資訊,一開始在裡面可以看到四個檔案: DefaultEditor.ini、DefaultEngine.ini、 DefaultGame.ini、DefaultInput.ini。從這裡我們可以看到UE4會根據配置的功能存放在不同的.ini標案中。基本上裡面大部份的配置我們都能夠在Unreal Editor進行變更,因此不太需要對這裡面的內容進行手動的編輯。 Content:存放著專案所使用的uasset跟umap,這二種副檔名為現階段引擎(4.15)內部處理素材所使用的格式。基本上引擎內部會有機制將你所匯入的檔案(如png、jpg)先轉換成uasset,然後在之後建置專案的時候再根據平台轉換成該平台的格式。例如:android的ETC1、ETC2、iOS的PVR。 Intermediate:存放著引擎編譯過程所產生的各種中間產物。 Saved:放log跟專案預設的寫出檔案路徑。 Source:跟我們專案相關的cpp code。 ${PROJECT_NAME}.sln:Visual Studio的專案檔。 ${PROJECT_NAME}.uproject:UE4專案的描述檔,裡面記錄著這個專案所使用的引擎版本,裡面分成了多少模組(Module)以及用了哪些Plugin。 ${PROJECT_NAME}.VC.db:為Visual Studio的IntelliSense database Figure 1.4.1 以下三項為構成UE4專案的基本核心:一、${PROJECT_NAME}.uproject:描述著我們的UE4專案相關資訊。二、Source資料夾:存放著我們的程式碼。三、Content資料夾:存放著專案所使用的asset。 在進到Source之後,首先會看到二個.Target.cs檔,見Figure 1.4.2,其主要的用途是用來描述我們所產出的執行檔是Editor、Game還是Dedicated Server與整個執行檔的編譯設定,大多時候我們都不需要對這個檔案進行編輯。 Figure 1.4.2 Source資料夾下面包含Target.cs描述檔,用來描述最後要產生的執行檔需要包含哪些Module以及相關環境資訊。 由於UE4是以「Module」的概念對程式碼的功能進行組織,在編譯過程中會去找到${PROJECT_NAME}.uproject底下所有的*.Build.cs當成該Module的根目錄,而每個找到的Module到最後都必須要產生或跟一個外部的library連接。(.dll或.lib) 在我們的初始專案資料夾下面,見Figure 1.4.3,我們可以看這個定義Module的檔案: ${PROJECT_NAME}.Build.cs:這個檔案主要用來描述這個Module跟其他Module的相依關係、其相依專案include檔所存放的相對路徑與其他各種編譯設定。預設做完建立初始專案的動作後在Binaries\Win64下面可以找到對應的UE4Editor-${PROJECT_NAME}.dll,見Figure 1.4.4。 Figure 1.4.3 Build.cs主要用來描述Module之間的相依性關係與相關的編譯設定,副檔名為.cs表示其所使用的語言是C#。 … Continued

[UE4] 如何使用git及git lfs來進行版本控制

本章節並不打算進行git的使用教學,對於不懂這套軟體運作機制的讀者可以很簡單的在其他地方找到更完整的教材。相對的,這個章節會專注於描述在使用git進行UE4專案開發時有哪些東西需要特別注意。 若是電腦沒有git,在Windows環境下請先下載並安裝下面二個網址中的檔案: Git for windows網址:https://git-scm.com/download/win TortoiseGit網址:https://tortoisegit.org/ Git for windows為git的主程式,必須要先安裝,而TortoiseGit則是給git主程式使用的GUI介面,若已有偏好的軟體則可略過這步。 安裝完成後,在程式集中尋找並打開Git Bash這隻程式,輸入Figure 1.6.1中的指令檢查是否有正確安裝: Figure 1.6.1 檢查git跟git lfs是否有正確安裝。 之後在Unreal Editor的Toolbar中選擇Connect to Source Control之後(見Figure 1.6.2),就會開啟設定視窗,如Figure 1.6.3。 Figure 1.6.2 啟用Source Control   Figure 1.6.3 Source Control設定視窗,先確認Add a .gitignore file這個選項有選取後,按下Initialize Project with Git之後,再按Accept Settings就可完成基本設定 在完成設定之後,我們會在專案目錄底下看到一個.git的資料夾以及.gitignore檔就代表我們已經設定成功。在.gitignore裡面描述的,是UE4官方所提供不該commit進到版本控制裡面的檔案跟資料夾。由於在.gitignore裡面列出來的都是可以由引擎方產生出來的,因此我們可以利用這個官方提供的初始化機制,讓我們可以省去設定.gitignore的步驟。 … Continued

[UE4]Module之間的相依性

在建立完專案並打開Primary Game Module中的Build.cs後,可以看到裡面預設了幾個相依的Module。 HorizonTutorialGame.Build.cs using UnrealBuildTool; public class HorizonTutorialGame : ModuleRules { public HorizonTutorialGame(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { “Core”, “CoreUObject”, “Engine”, “InputCore” }); PrivateDependencyModuleNames.AddRange(new string[] { }); } } Code 2.7.1 “Core”、”CoreUObject”、”Engine”、”InputCore”為引擎核心的幾個Module 其中: l … Continued

[UE4]為專案加入Plugin

在UE4中,所有的Plugin主要存在於二個地方: l Engine Plugin:${UE4_ENGINE_ROOT}/Engine/Plugin/,我們在Marketplace上購買的Plugin會下載到其中的Marketplace資料夾。 l Game Plugin:${PROJECT_NAME}/Plugin/,通常我們自己製作的Plugin都是放在這個地方。 製作Plugin的方法非常簡單,我們只需要使用引擎Editor中所提供的工具,就可以快速的製作出各種不同用途的Plugin給專案使用。這個工具提供了各種不同的Plugin範本,在建立的同時會同時為我們的Plugin建立一個預設的Module。 首先,讓我們試著建立一個空白的Plugin吧。 在Editor中選擇Edit->Plugins後,就會跳出Figure 2.6.1畫面。 Figure 2.6.1 按下NewPlugin後,就會出現Plugin範本讓我們選擇。 Figure 2.6.2 選擇Blank,填入必須資訊按下Create Plugin之後,就會在${PROJECT_NAME}/Plugins下面建立出一個新的Plugin。 在建立完Plugin之後,我們會得到Figure 2.6.3中的目錄結構跟檔案。 Figure 2.6.3 使用Blank Plugin範本所建立出來的Plugin。 其中: l Resources/Icon128.png:為一個128×128大小的png,主要是用來顯示在Editor中。 l ${PLUGIN_NAME}.uplugin:存儲有關於這個Plugin的相關資訊,例如版本號碼、Plugin需載入的Module、作者名稱……等等。 l ${PLUGIN_NAME}.Build.cs:用來描述這個Module跟其他Module的相依關係、其相依專案include檔所存放的相對路徑與其他各種編譯設定。 l ${PLUGIN_NAME}.h跟${PLUGIN_NAME}.cpp:Module Interface的相關定義與實作。 在打開.uplugin後,會看到一個用json格式來描述Module的資訊,見Code 2.6.1。 HorizonGameLibrary.uplugin { “FileVersion”: 3, … Continued

[UE4]為專案增加一個Game Module

上一節展示了Module相關的基本概念,本節就讓我們試著為專案加入一個Game Module吧。首先,我們可以把專案下面預設的Primary Game Module複制一份出來,並重新命名成希望的Module名稱,見Figure 2.5.1。   Figure 2.5.1 為HorizonTutorialGame專案增加一個HorizonTutorialGameEditor作為額外的Game Module以擴充遊戲內Editor相關功能。這個Editor Game Module我們可以在之後用來客製化UPROPERTY在editor中detail pnael的顯示方式,或者是追加新的編輯模式……等等。     然後必須更改新Module下面build.cs中的類別名稱,見Code 2.5.1。 HorizonTutorialGameEditor.Build.cs using UnrealBuildTool; public class HorizonTutorialGameEditor : ModuleRules { public HorizonTutorialGameEditor(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { “Core”, “CoreUObject”, … Continued

[UE4]GameModule與Plugin

當專案建立的時候,引擎會自動產生一個同名的Game Module在Source資料夾底下。我們當然可以將所有撰寫的C++類別全部放在這個Module中,可是當專案越來越大,若還是將所有的功能都放在同個Module下,不僅僅會造成管理上的混亂,而且編譯時間也會增加。 試著想像當我們隨便改動一個h檔或cpp檔的參數就要編譯幾10分鐘的情況?由於UE4在一個Module中的cpp數量到達32個的時候就會自動啟動編譯檔案合併的機制(Unity Build),雖然UE4中有參數可以控制這個機制是否啟用,但卻是捨棄掉了整體的編譯時間。因此,如何將功能適當的切到各別Module中,就變成了一件非常重要的程式架構設計問題。 那麼實際上UE4的Module系統是怎麼運作的呢? 每一個獨立的Module基本上是由${MODULE_NAME}.build.cs與其下的C++程式碼所組成,而在UE4中,遊戲中的Module主要被分成以下幾種實作Macro定義: IMPLEMENT_PRIMARY_GAME_MODULE IMPLEMENT_GAME_MODULE IMPLEMENT_MODULE 到底這些實作彼此之間有什麼不同? 其實,這些Macro定義在最後都會被展開成IMPLEMENT_MODULE中的實作,它們之間在本質上並沒有不同,主要是用提供Module初始化Function的實作給UE4中的ModuleManager呼叫,參照Code 2.4.1: IMPLEMENT_MODULE定義 #define IMPLEMENT_PRIMARY_GAME_MODULE( ModuleImplClass, ModuleName, GameName ) \ IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName )   #define IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName ) \ IMPLEMENT_MODULE( ModuleImplClass, ModuleName )   #define IMPLEMENT_MODULE( ModuleImplClass, ModuleName … Continued

Hello Unreal! 過去與現在

大部份的開發者對Unreal Engine的第一印象,應該都是它那陡峭的學習曲線;雖然可以做出非常精美的畫面,但相對的團隊中必須要有足夠的技術與美術能力才有辦法駕馭。確實,光是看官方技術文件所羅列的功能介紹與技術展示影片,是會有一種迷失在技術叢林中的感覺。 Figure 1 UnrealEngine4於GDC 2015的技術展示影片「A Boy and his Kite」,可以在Youtube的 Unreal Engine官方頻道上找到連結:https://www.youtube.com/watch?v=JNgsbNvkNjE 自初代引擎於1998年釋出開始到現在,引擎不斷的在實戰經驗中演化。光是在維基百科上(https://en.wikipedia.org/wiki/List_of_Unreal_Engine_games)所列出的知名遊戲清單就至少有上百個,所涉及的主機橫跨目前世界上所有的主流平台,如windows、linux、ps4、xbox、android、iOS……等等。 雖然引擎最初是為了Epic他們自己的「Unreal」這款第一人稱動作射擊遊戲而製作,但隨著世代的演進,目前引擎的應用範圍已經不再只是侷限在這類型的遊戲製作上,另外像是電影、建築、動畫、醫療……等等也都可以看到相關應用的身影。 Figure 2 由美國NASA主導開發的The Mars 2030 Experience,讓我們可以使用VR在火星上探險。Youtube影片連結:https://www.youtube.com/watch?v=prpQB0nlRV8 在過去,使用Unreal來開發遊戲是一件非常奢侈的事,光是授權費就是數十萬美金起跳。如此昂貴的價格讓不少公司打退堂鼓,更何況是後續的人員訓練與維持費用。 但在面對市場上其他引擎的競爭,Unreal也逐步的採取開放策略。最大的轉變是自2014年釋出第四世代引擎開始,從每月19美金的訂閱制,到隔年完全免費開放,可以感受出Unreal不斷的往獨立開發者的路線靠攏。現在,開發者只有在每季營業額超過3000美金之後,才需要提出5%的分成費用給Epic。 這對不少獨立開發者無疑是個好消息,而且最吸引人的一點是,所有人都能夠從Github上取得完整的原始碼,如果想要的話還能進行客製化的修改。 這種「開放式」的策略非常成功,不僅打消了引擎在使用上的疑慮,對於有志於技術研究的開發者,UE4更是一座充滿最新技術實作的寶庫。官方其實也不怕競爭對手抄襲,甚至在官方的Q&A中提供了下面這段聲明: Figure 3 官方對於釋出原始碼的態度,可以從這段聲明中看出:雖然Code有版權,但知識是免費的。截取自https://www.unrealengine.com/faq 拜開放原始碼的政策所賜,每天都有成千上萬的開發者在Github上為引擎提供新的功能與Bug修正。──這也是為何UE4能夠以如此飛快的速度釋出新版本並維持引擎的穩定性。 Figure 4 我們可以從github上取得UE4的原始碼,從圖中可以看到,有不少熱心的開發者幫引擎提供新的功能跟bug修正。只要曾經在github送過Pull Requests給官方並被接受的話,該名開發者就會被列入引擎的感謝清單中。 官方在推廣引擎的使用上其實非常的用心,我們不僅可以在網路上下載到所有的Tech Demo專案,而且還有不少文件教學、影片提供入門以及進階開發者學習。   Figure 5 官方網站上可以找到很多的學習資源。   Figure … Continued