認識Editor環境與UE4檔案命名系統
UE4所提供的Editor非常強大,裡面的功能非常的多,實在不是在一個章節中有辦法全部介紹完畢。其實要熟悉編輯器的介面最快的方法,就是根據需求再去找出功能對應的位置。本節並不試圖在這裡就去解釋所有的功能,相對的,這裡會列出一些初學者常用區塊。 在打開初始專案後者先引擎會先載入一個預設的map,見Figure 1.5.1,該map儲存於${EngineVersion}\Engine\Content\Maps\ Templates\Template_Default.umap底下。 Figure 1.5.1 引擎預設載入的map:/Engine/Maps/Templates/Template_Default,其實體路徑存在於${EngineVersion}\Engine\Content\Maps\ Templates\Template_Default.umap。 A區:基本上會根據所選擇的工具會有不同的內容,預設是顯示引擎中可以放到Level中的Actor。 B區:為editor常用的一些工具列,最常用的按鈕是按下Play後,運行被稱為PIE(Play In Editor)模式。 C區:主要用來調整視角光影或者是一些開發時除錯用的資訊,例如要不要顯示mesh的collision範圍,或者是特定的post process效果……等等。 D區:一些編輯關卡時有用的工具,例如該actor的編輯模式是要移動、旋轉還是放大,移動或旋轉時操作一次要更動多少數值,以及編輯時camera移動的速度要多快。這邊自己與其用文字描述,不如自己操作一次會比較有感覺。 E區:顯示目前關卡上置放的Actor,可以讓我們用來視覺化對該actor進行編輯。 F區:顯示現在的關卡名字。 G區:顯示目前關卡上置放的Actor,跟E區不同的是,這邊是顯示所有關卡上的instance。 H區:讓我們一次將所有的更動作儲存,由於目前引擎的存檔機制還不是很穩定,有時候直接關掉引擎時跳出的儲存視窗不會成功的將結果保存起來,因此這個按鍵就發揮了很大的功用。 I、J、K區:Content Browser,用來顯示目前我們可用的素材。在I區顯示Folder分別會對應到以下幾個實體路徑: Game:${PROJECT_NAME}\Content,顯示我們import到專案中的assets。 C++ Classes\${PROJECT_NAME}:${PROJECT_NAME}\Source\${PROJECT_NAME},顯示我們所實作的UClass。 若是我們在K區將Show Engine Content選取後,我們就可以看到Figure 1.5.2的內容: Figure 1.5.2 在K區將Show Engine Content選取後可以看到Engine Content Figure 1.5.1所提到的/Engine/Maps/Entry,我們可以在Engine Content/Maps下找到。到這裡我們應該可以看出來Unreal內部有機制將實體目錄對應到其內部的檔案命名系統。其中: /Content/:對應到的是${PROJECT_NAME}\Content。 /Engine/:對應到的是${EngineVersion}\Engine\Content\。 /Script/:對應到的是${PROJECT_NAME}\Source。 … 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
關於UE4中的目錄結構與Module相依性
在用Editor加入程式碼的時候,會發現上面有Public跟Private二個目錄選項,見上圖。到底它們的用途是什麼?對於熟悉C++的人可能已經猜到原因:為了讓Module「介面」跟「實作」的部份有著更明確的區分,我們將所有的.h檔都放在Public;.cpp都放在Private。──大部份的情況是可以適用這個規則。只是這個目錄架構並不是絕對,我們當然可以在Private中放.h檔或在Public中放.cpp,甚至創立自己的目錄結構,這在編譯程式上並不會有任何的問題。 只是並不是所有的檔案都適合放在Public中,要知道,UE4是用Module的概念來組成各個系統的功能。為了Module的維護性與使用端的簡便,我們必須要想辦法將實作的細節從使用端隱藏起來。 將檔案放在Public中所代表的,就是任何人都能夠隨意的去include這個類別並使用裡面的方法。這所造成的結果,會讓我們沒辦法輕易的修改程式,例如更改類別中某個方法的名字。隨意修改名子的後果,會造成繼承該類別的物件無法編譯,又若是所依賴的專案使用大量的Blueprint繼承該類別,可想而知這些檔案就會全部損壞而無法回復。 其實這邊所提的就只是物件導向中『封裝』的概念,只是這裡從是檔案目錄結構的面向來思考。要怎麼設計Module裡面的目錄組成方式,就端看這個Module的目的與設計者的巧思。理論上放在Public目錄下的類別必須要具有穩定性,不管是類別的名字還是所開放方法的參數。 當然,若是我們所實作的是末端的Game Module,沒有其他的Module參照到我們的話,不管怎麼組織目錄都不會有太大的影響。 另外我們在引擎中還會看到許多Module建立了一個叫Classes的資料夾,如Figure 28。 Figure 28 Engine Module定義了Classes、Private、跟Public三個目錄結構。 Public跟Private我們可以理解用途,但為什麼多了一個Classes?根據官方的回答,在以前所有的UObject都只能放在Classes這個目錄下面。現在當然沒有這個限制,為了歷史的兼容,這個目錄也跟Public一樣自動被設定進include搜尋路徑之中。 本系列文章為個人原創,未經授權,謝絕轉載
關於UE4++、GC與標準庫的一些雜談
從上一節 (http://dorgon.horizon-studio.net/archives/773) 的內容我們知道官方堅持不將Script導入引擎的原因,但對於稍微進階一點的程序員可能就會開始思考:既然都使用C++了,為什麼還要在引擎中實作Garbage Collection(GC)?直接使用RAII的機制不是能夠更進一步的避掉因為GC而產生的效能耗損? 先撇開效能問題,其實導入GC的優點很明顯:它幫助我們從複雜的記憶體管理議題中逃脫出來,進而能夠專心在遊戲邏輯的撰寫上。C++雖然是一個非常強大的語言,但反過來說,卻也是非常的複雜。 例如上面所提到的RAII,一般最常使用smart pointer的概念來實現。若沒有經過特別的訓練的話,則很有可能就會迷失在shared pointer跟weak pointer的使用情境之中。更糟的是,若使用者打從一開始就不知道weak pointer的概念,則有很大的可能會寫出物件間循環參照的邏輯,進而造成物件的記憶體永遠不會被釋放。 因此,並不是所有的使用者都能夠正確掌握語言的特性,為了理解並正確使用這些功能,是需要好幾年的經驗與養成才有辦法好好的揮舞這把強力武器。 而UE4為了將C++複雜的特性從一般使用者中隔離出來,所採用的並不是跟其他大部份的遊戲引擎一樣導入Script創造一個Sandbox環境的方法。相反的,它在原本的C++語法之上更進一步的設計了一套Reflection(UProperty)跟Garbage Collection(GC)的機制。 雖然導入GC會影響效能表現,但相對的C++整體在使用上的複雜度也降低到跟其他高階語言差不多的程度。而且,由於UE所實作的這套GC機制是為了遊戲這類即時互動性高的系統所設計的,因此裡面的GC機制不僅有許多回收的優化機制(見Figure 18),而且其採用的「漸進式」方法不會在GC發動時讓整個遊戲卡住,意即,每次進行回收的動作不會超過所設定的秒數(預設為0.002秒)。 Figure 18 GC預設設定,於Project Setting->Engine->Garbage Collection選項。其中在Optimization中的Create Garbage Collector UObject Clusters這個選項,在目前的版本(4.15)只支援Material跟Particle。 大部份的應用在這套系統下面其實不會有什麼大問題。 當然,C++裡面一個非常重要的設計哲學:不要為不需要的功能付出效能上的代價 (Stroustrup, March 1994)。對於更進階的效能需求,其實我們還是可以使用原本標準庫中所有的C++方法。當我們的類別不是繼承自UObject體系時,是可以使用自己的記憶體管理機制。基本上,UE4++包含了所有C++的語法特性,然後再加上一些給UHT自動產生reflection資訊的標記語言。在4.15版本釋出時,引擎更是在全平台支援C++14所有語法的特性。 只是當我們開始深入研究相關的方法的時候,會發現:為什麼引擎中實作了許多跟標準C++相同概念的方法?例如TArray對應到std::vector;TMap對應到了std::map。為什麼不使用標準庫中的方法而要自己實作一套呢?難不成UE4自己實作的方法有比標準庫中的還要快嗎? 其實,速度並不是主要的考量。 根據官方於論壇上的說法,歷史因素才是造成目前設計的主因。要知道,UE4是一個已經存在好幾十年的引擎,在第一代引擎釋出的那個年代其實也正是 C++發佈第一套標準 (C++98)。由於標準剛發佈時的各方實作並不是很穩定,各個不同平台下的行為模式還不是很一致。為了保證穩定性,當時的引擎有了自己的一套實現,並且一直延用並演化到現在。 由於現在C++標準庫已經非常的穩定,官方是有在思考將引擎中對應的方法全部替換成標準庫相關實作的可能性。只是,這部份的變更目前並不考慮在第四世代的引擎中實現。或許在「第五世代(UE5)」釋出的時候,我們就可以看到引擎跟標準庫有著緊密的結合。 本系列文章為個人原創,未經授權,謝絕轉載