1. 程式人生 > >虛幻4隨筆 三 從UE3到UE4

虛幻4隨筆 三 從UE3到UE4



筆者有幸參與過兩個UE3專案,完全不同的使用方法,總共用了5、6年。引擎學習最好還是能參與專案,自己看的話往往容易糾結到一些細節上去,而引擎之所以是引擎,重要的恰恰是在容易被人忽視的工作流上。單從細節上看,UE3的程式碼很多地方並不完美,甚至有些奇怪,但是一旦做到工作流上,就會發現整個UE3工作流的強大之處。
先回顧一下UE3系統的一些結構要點,權當做個記錄,看看UE4在這些方面有什麼不同,作為我們接下來讀碼的突破口。
如果真心想要學習這個引擎,最好還是能用它來做做專案,專案不分大小,只論完整程度,筆者短時間內看來是沒機會了。


1、BuildTools
UE4除了工具和外掛外,本體不分工程了,UE3的核心部分最後分了Core、Engine、Editor、WinDrv、Network、Renderer等十數個工程,UE4就一個UE4工程。但工程歸工程,編譯時還是分得很開的。


而且這個工程猜測是靠BuildTools來生成出來的(一開始那個bat)。看起來,散落在各個資料夾下的.cs檔案就像CMakeList.txt那樣,它們才是整個工程的組織核心。
事實上從UE3時代,就可以完全脫離Visual Studio IDE來工作了,UE3的工程本身都已經不再是典型的VC工程,程式碼編完後,實際上最後執行的是Build.bat、呼叫UnrealBuildTools.exe來編譯,所以要改工程設定,也是需要去修改UnrealBuildTools工程的。
** 估計,如果要改工程組織結構,增加檔案什麼的,也需要維護這個.cs檔案吧。這個可能得等做做才知道了。
不過應該可以看出來,UE4這裡是為了支援專案和引擎分離而進行的。

2、Core
個人觀點,UE3的Core重點是下面幾個部分:
作為整個虛幻構架基礎的UObject和由一大堆巨集和各種Classes.h這套組織結構反射系統——圍繞它的包括GC、UObject與UnrealScript的互操作性、與編輯器的互操作性、自動序列化、物件克隆等。這個非常重要,整個虛幻體系的核心就是這一套東西,如果前面不注意的話,後面遲早會在這裡栽點跟頭。


序列化和統一具名訪問:ULinkLoader,起這個名字可能主要是因為載入的時候,它會自動分析Object的引用鏈,並且根據需要繼續往下載入。另外,所有虛幻的Object都會有自己獨一無二的具名路徑,例如xxx.umap:persistentLevel.Pawn_0,xx.Material.Material_0,任何時候,只要使用LoadObject、FindObject並傳入這些名字,就可以訪問到對應的物件。這個在編輯器的維護中是相當方便的一個底層特性。甚至,這個具名路徑還可以訪問到指令碼中的類、內建模板資源等等。


MakeCommandlet和UC指令碼核心:Core中間編碼了整個Unreal Script的編譯和執行時環境。Unreal Script編譯過程中會生成相應工程的Unreal Script/C++互操作檔案:xClasses.h以及一堆自動生成的方法和呼叫。編譯後的結果是.u檔案,其實同時就是跟.upk資原始檔一樣的格式——虛幻2不清楚,但至少從虛幻3時代開始,資源和指令碼就被當作是同一個東西,指令碼是可執行的資源,資源是不可執行的指令碼。另外與此相關的就是一套偵錯程式——很多人用了半天虛幻3卻不知道虛幻指令碼是可以除錯的……AutoDebug命令列或者ToggleDebugger指令搜一下,印象中偵錯程式的核心介面是基於UDebuggerCore還是UDebuggerInterface這個類。VS裝nFringe外掛後、或者自帶的UDE都可以對指令碼進行除錯。


狀態機:指令碼的特殊語法,狀態機是在指令碼類內部的概念,每個狀態可以過載指令碼類某些函式的實現,這樣當狀態切換到這個狀態的時候,就只是執行狀態內的函式而非指令碼類的函式本身。Actor和Controller裡大量用到。


Latent:指令碼的特殊語法,基本類似於不通過連線的Kismet,latent類似於Erlang這樣的Coroutine語言,每個語句都是步驟而非過程,步驟可能會花很多幀去執行,執行完畢後接著進行下個步驟,傳統語言的過程只能當前幀執行完畢。

其它就是一系列的數學庫、記憶體管理、輔助函式。記憶體管理比較有意思,一開始看總覺得問題較大,當時組裡的記憶體專家Aman Jiang老師實際打出來報告後發現這塊兒管的還是很不錯的,碎片率遠低於我們的預期。

3、Engine
相當龐大的集合,個人觀點,重點在於:
Actor-Component體系:元件化結構的虛幻版,元件化現在應該是大多數引擎的標配了吧?這塊兒可以說中規中矩,主要元件還是得花心思去看看,否則極易在介面呼叫順序亂掉的情況下發生問題。渲染器與遊戲上層邏輯通過Component來介面,提供新的渲染技術後,只需要做一個對應的Component就可以了——SpeedTree什麼的就是這麼整合進來的。


Game-Player-Controller體系:Game Mode決定了當前關卡的遊戲玩法,一個關卡可以有不同的遊戲玩法——對於FPS你可以想象虛幻競技場中的很多地圖都同時支援Free for all、奪旗、Team計分。對於網路遊戲,你可以想象一個關卡資源可以用來做戰場、也可以用來做副本。Player是所有IO的總入口,一般一個遊戲只有一個Player,就是Local Player。主機遊戲可以設計同時存在兩個Player的場合,可以用分屏顯示來分離各自的IO。進入地圖後,會針對當前地圖生成Controller,來實際從Player截獲輸入和部分輸出操作,並真正影響到遊戲中。相應的概念還包括View(實際上的攝像機)、ClientViewport(遊戲和編輯器視窗)。


Controller-Pawn體系:Pawn是可以被Controller控制的東西,Controller把IO和UI訊息轉化為對Pawn的操作,通知Pawn完成其功能,並把這些功能執行過程反饋給IO和UI。在遊戲中可以切換Controller內部的不同狀態,例如根據Pawn是在走還是在爬牆,把輸入訊息轉化為對Pawn不同的指令。還可以切換Controller,比如進載具了,Controller一換就Ok。甚至技能中也可以切換Controller,比較經典的例子就是虛幻競技場3裡的榴彈炮:普通架起來的狀態下打出的是一般炮彈,炮彈飛行過程中滑鼠可以一直控制其方向,右鍵可以把這個炮彈展開使其定位在空中,然後你的視角一直停留在這個炮彈上,滑鼠變成在地面選擇一個區域,火炮變成一門發射榴散彈的大殺器,把致命散彈砸向這個區域。筆者接觸過不少遊戲的遊戲系統了——不幸的是這個流程很少有系統能夠不加大改地實現。AI也是一種Controller,操縱的是Bot這個特殊的Pawn,這塊兒有興趣也可以研究一下。


World-Level-Actor體系:World裡存一堆Level,Level裡存一堆Actor, Build後的光照跟Level走,Level是關卡部分的資源單位。但場景圖根是World裡的Hash,虛幻3裡是個八叉樹實現。World裡實現了基本的場景功能,角色的走跑、懸崖邊緣的檢測、走到碰撞體前被擋住、碰撞體位置變化時導致自己上面放置的其它碰撞體變化……你如果有自己的場景需求,可以修改World裡面的這個部分。注意,在虛幻3裡場景和物理雖然有關,但是本質上還是分離的,引擎提供了預設的整合方式,但你可以在Actor裡重新控制這種整合。


資源體系:沒什麼好說的,Material、Texture、各種Mesh、Particle。Material連線球很贊,但是跟渲染Stage有較深的關聯,筆者試圖做過一個自以為比他更好的,跟Stage可以一定程度脫耦的材質連線球系統——但是最終發現材質這東西根本上還是離不開渲染Stage,什麼都想控制的結局一定是什麼都控制不過來。
渲染體系:就不說什麼了吧,Deferred Lighting的Stage體系,網上的文章海了去了,做圖形的這個早就拋腦後了吧。最近一兩年的版本支援了Deferred Shading。這套Stage的低端化替換還是很方便的——畢竟現在需要考慮到遊戲可能更多是會在intel HD 3000/4000這種顯示卡上跑的可能性了。

4、UnrealEd
編輯器……怎麼說呢,這個沒法按體系來了,太多了,有些精品也有些糟粕,反正編輯器這東西,沒法說,需要擴充套件的時候自己去改吧。
注意屬性編輯器是如何發揮反射的強大效力的。
有些細節問題,實際做了可能會遇到:拷貝物件時,有些屬性是引用的(一般的Object屬性不加任何描述),有些是複製的(editinlinenew、instance和duplicate),有些是捨棄的(transient)。還有就是虛幻比較喜歡用Prototype + Clone的方式來實現Template-Instance這種需求,典型的例子是Animation Tree和Prefab,例項都是直接從資源拷貝出來的。主要是因為UE3的反射外圍有一個比較強大的Clone系統,但是前提是您得對剛剛列舉的那些關鍵字比較熟悉,否則為這些系統擴充套件時就比較容易遇到問題。
多說一句:編輯器選物體用的是把物體的ID渲到一張Render Target上再去這個Render Target上查詢滑鼠點Id的做法,叫HitProxy,如果你的遊戲想支援畫素點選,可以參考這個東西,很容易就能把這個Stage整合到遊戲Stage裡。

5、其他工程
就沒什麼好說的了:
渲染器:Renderer,DX9、DX10、DX11、OpenGL的實現都有,DX10只有一個版本曇花一現。
平臺庫:以XXXDrv為名,例如WinDrv。主要提供平臺方法,沒什麼好說的。
網路庫:IpDrv、Channel什麼的,也沒什麼好說的。

網遊的開發者很喜歡自己構造上層,我參與的第一個UE專案就是這麼做的,實際上最後做出來的上層後來看來比虛幻的整個上層體系差的太遠。第二個專案筆者就一直推動著在虛幻框架上的小修小改。最後能實現什麼呢?能實現在編輯器里根伺服器通訊,編輯器調整完Kismet連線圖、資源、布怪點後,不用退出,直接就可以啟動開發伺服器,然後在編輯器內測試剛剛自己做的東西對不對,可惜因為資金原因沒做下去,兩年前這個級別的工具整合,不知道有多少人做到了?至少我現在在這個專案裡還沒能推廣到,也不可能推廣到那個程度(筆者鼻子翹起來了~*^_^*)。不過未來不需要再弄了——看看虛幻4的Blueprint,可以考慮這個級別的集成了。

筆者一直認為,虛幻3強大的地方不在於他的圖形,而是在於這套強大而穩固的結構和迅速的工作流,而當你握這套結構和工作流後,筆者發現所有引擎在自己的面前索然無味,圖形雖然還在追求,但已經退居次席了,而自己重新去做引擎的衝動則不斷降低,直至完全消失。
用虛幻一定要首先明白一個原則,就是這是個解決方案式引擎,不是OGRE那樣的圖形工具庫,所以你的所有改動一定要符合虛幻的基本結構假設,否則你只是在給自己找麻煩。但是,相信我,符合這些基本的結構假設一點不會讓你的自由度降低,你的控制能力把控能力還是相當強的——不信你看,筆者上面列舉的框架部分,有哪個是會影響你的發揮的?M——World-Actor、V——Player、C——Controller,哪一環是可以省略的?當然,如果您的體系結構設計的比這個還好,那真的很恭喜您了,一山更比一山高,筆者只能感嘆於自己的時運不濟了……

這兩天還是在看例子,看幾個Blueprint的例子,順便回想一下UE3的那些事兒。最近的緊張局面可能會延續到4月中旬,屆時才有可能有更多時間來看UE4。目前感覺變化還是挺大的,不過,喜在框架方面的改動似乎很有限。
從另一個方面,也證明了UE3的這套遊戲上層邏輯框架,是多麼地穩固和強大。