Unreal Engine 4 跨平台開發經驗

Owen Chiang
NeoBards
Published in
15 min readMay 9, 2019

Project N 是 NeoBards 使用 Unreal Engine 4 (UE4) 研發的多人對戰類遊戲,目標為提供 PC 與家用主機玩家均可一同遊玩的跨平台遊戲體驗,就如同 Epic Games 的 Fortnite 一樣。

Project N by NeoBards

本文會以平台開發需要考慮到的「資源配置」以及「平台優化」為主,所以針對遊戲系統製作、網路架構建置、網路功能同步等議題皆不會提及。

此外,手機平台因為操作模式有很大的差別,在此專案中的功能定位與 PC 和家用主機不同,因此開發過程並沒有將手機平台的規格納入開發考量。

開發環境

Game Engine: Unreal Engine 4.20
Source Control: Perforce
DDC: Maya 2017
IDE: Visual Studio 2017
  1. 我們使用 UnrealGameSync (UGS) 發布專案開發環境,解決了以往使用 UE4 開發,因為 Editor Binaries 不一致而導致製作出的 Content 混亂的問題。
  2. 以 Epic Games 的 Naming Convention 為基礎,定義了明確的 Asset / Content 命名方式以及資料夾結構,讓開發人員可以有效率的使用以及找尋需要的 Content;並且能夠與 NeoBards 內部的 pipelines 做更緊密的整合,建立更完善的開發環境。
  3. 導入 NeoBards in-house art pipeline 讓美術素材能夠更快速且正確的匯入 UE4 的 Game Project 中,加速開發的迭代以及美術資料/匯入的正確性。

美術資源規格制定

我們於開發初期將所有的硬體分為了三個等級,並且以最低規的平台Switch做了基礎效能測試。

在測試效能之前,我們必須先說明 Project N 的基本遊戲內容:

  1. 是一個 45 度固定視角的遊戲
  2. 場上會出現大量的操作角色與 NPC ,並且擁有豐富的視覺效果
  3. 遊戲場景為一固定範圍的關卡且無大規模場景變動

依據上述的內容,可以發現主要的效能瓶頸會出現在 Character 的各種表現上,因此我們測試方法是使用 100 個可操作角色與 100 個 NPC 為基礎,來實測 GPU 的 vertex bound 以及 pixel bound。

  • Vertex bound
    Vertex bound 主要來自畫面中出現物件的數量以及複雜度(面數、骨架數量等),除此以外由於 Shadow Map 的建立過程中也需要 geometry processing ,所以也會增加 GPU 的負擔。
Vertex bound test (based on Nintendo Switch)
  • Pixel bound
    Pixel bound 主要來自 Pixel Shader 的複雜度、解析度大小是否已經超過 pixel fill rate可承受的範圍、貼圖大小造成 texture sampling 的負擔。但由於專案在這個階段尚無法預測出最後的美術風格呈現,因此僅針對 Switch Docked Mode 的 rendering features 做效能的測試。
Pixel bound test (based on Nintendo Switch)

在制定美術資源規格之前,我們基於專案的設計方向,將美術資源的配置分為角色 70% (玩家角色 40%,NPC 30%,包含相關特效物件),場景 30% (包含相關特效物件以及後製效果)。因為遊戲本身是一個固定 45 度視角的遊戲,我們也利用了這個特點將模型的 LOD 拿來做資料階層的分級,除了可以針對模型複雜度、骨架數量做更適合平台的設置之外,也可以根據 LOD 來配置不同 Material 做到平台的效能優化。

LOD 0: 展現模型細節為主的階層,用於特殊的使用目的,如選角畫面或最高階電腦使用
LOD 1: 畫面細節呈現以及效能綜合考量的階層,用於 PS4/Xbox One 等級的硬體平台
LOD 2: 完全以效能考量為目標的階層,主要使用於 Switch﹑低階電腦或手機平台

根據效能的測試以及 LOD 的使用方式,我們將 Character 的美術規格訂為:

Character 美術規格

場景資源部分由於會基於場景概念圖設計有所不同,在規劃階段僅訂立了可使用所有資源配置的 30%,但依舊設定使用 LOD 做資料階層的分級。

技術目標

專案的開發目標是製作一個可跨平台的對戰遊戲,因此在一開始我們便定下了以下的技術目標:

  1. 目標平台 (PC/PS4/Xbox One/Switch) 遊戲體驗一致
  2. PC/PS4/Xbox One: 60 fps、Switch: 30 fps
  3. 盡可能共用 Content,降低針對平台製作的 Content

實際製作

Asset 製作

針對 Asset 的創建,我們必須遵循:

  • Naming Convention
Project N Asset Naming Convention

Asset 的檔名可分為 (Prefix_)AssetName(_Suffix)三個部分:

字首 (Prefix_) 用來標示檔案的類別,一般來說可以明確表明該 Asset 的用途,如:SK_表示為 Skeletal Mesh。

檔名 AssetName 則遵循制訂規則產生出檔案名稱,最重要的是要讓開發人員可從 AssetName 了解該檔案的資料夾結構。如:Pl_Nja_ 即代表放置在 Character\Player\Nalja\ 下,它會是一個玩家的角色。

字尾 (_Suffix) 用來標示檔案的額外訊息,用來補充該 Asset 的詳細用途,如:當字首為 SK_ 表示為 Skeletal Mesh,字尾為 _Physics代表為 Physics Asset。

這些規則與類別內容,應該要在專案初期就確認下來,讓所有開發人員有所遵循。如果上一個專案的規則沒有太大問題,也應該盡可能繼續沿用,以習慣這樣的規則,減少問題的發生。

  • Character Asset LOD 架構

如同先前提及的,由於遊戲使用固定視角的緣故,我們使用 LOD 來針對平台硬體資源來做 Asset 的階層分級。除了面數以及骨架數量之外,也透過 LOD 的機制來做 Material 的平台優化。

Character Assets LOD

可以在 Character Pawn 加入依照平台選擇 LOD Level的程式碼:

void AProjNCharacter::BeginPlay()
{
Super::BeginPlay();
SetLODLevel(UProjNFunctionLibrary::GetLODLevelWithPlatform());
}

所有的 Character Blueprint 都會繼承此 Class,因此在 Character 在生成之後就會依照平台選定 LOD Level。

而 Static Mesh 則是透過 CVar 設置 “r.StaticMeshLODDistanceScale” 達到根據平台設定 LOD Level 的效果。

Map 架構

由於使用 Perforce 作為 Source Control,因此我們將 Content (如 uasset、 umap 等)的 File Type 設定為 binary+l (同時間僅有一人可以 check-out 編輯檔案),因此在 Map 的檔案架構是朝向「每個 map 檔案都有一個主要維護者」的概念,所以會以框架 *.umap 檔案參照內容 *.umap(Gameplay、 Collision、Sound、Event 等)檔案來建構關卡。

Map File Naming and Structure

Scalability 系統的運用

Epic Games 在進行 Fortnite 的跨平台及優化大量的運用了 UE4 針對各種平台以及硬體等級所建立的 Scalability 系統來達到效能以及遊戲體驗的平衡。所謂的可擴展性不僅僅是針對畫面設定而已,還包含平台設定、美術效果、遊戲性等。

在此次專案中,我們僅針對不影響遊戲性的畫面設定做了調整:

  • FPS ( Frames per second)
  • Render Quality
  • Post-Effects

我們透過 Device Profile 的機制讓 UE4 可針對每個平台以及硬體的差異選擇 *.ini 來設置畫面的設定,且大多可以透過設定 CVar 來達到效果調整的目的。

UE4 的 Device Profile 是階層式的改變 Engine 設置。會依序使用以下的Config 進行設置:

  • \Engine\Config\BaseDeviceProfiles.ini
    註冊支援的平台名稱,Texture LOD 設置,以及各平台的基礎設置。
  • \Engine\Config\[Platform]\Base[Platform]DeviceProfiles.ini
    各平台的預設 Device Profile,大多是設置畫面品質以及貼圖相關設定。例如 Switch 為了貼圖的效能會將 Texture Filtering 從 Anisotropic 8x 改為 Bilinear Filtering,這個舉動會提升 1 ms 的效能。
  • \GameProj\Config\[Platform]\[Platform]DeviceProfiles.ini
    專案針對各平台設置的參數,大多會依據遊戲類型以及畫面風格與效能做最適當的調整。在此專案中,我們有設置 SwitchDeviceProfiles.ini 來調整畫面效果來取得遊戲流暢度的體驗。
[Switch DeviceProfile]
; Change AA to FXAA
+CVars=r.DefaultFeature.AntiAliasing=1
; Shadow settings, according to BaseSwitchDeviceProfiles.ini
; Console: sg.ShadowQuality=2; Handheld sg.ShadowQuality=1
+CVars=r.DistanceFieldShadowing=0
+CVars=r.CapsuleShadows=0
; AO settings
+CVars=r.DistanceFieldAO=0
+CVars=r.AmbientOcclusionLevels=0
; Capping to 30 FPS
+CVars=nvn.SyncInterval=2
[SwitchHandheld DeviceProfile]
; Handheld CSM settings
+CVars=r.Shadow.CSM.MaxCascades=2

效能優化

在專案中,我們遇到的效能問題主要出現在大規模會戰以及發佈到 Switch 平台時由於硬體規格差異造成的 FPS 低下或不穩定的狀況,由於大規模會戰的問題也會在 Switch 平台發生,所以這個部分就以 Switch 的優化做說明。

在開始進行優化之前,我們必須先釐清效能的瓶頸是出現在 CPU 或 GPU 上。當遊戲發佈到 Switch 平台之後,可以透過 SwitchInputSender 傳送 command “stat unit” 就可取得 CPU 及 GPU 的 profiling 數值。

Switch profiling

Frame 是代表此 Frame 花費的時間,它通常是 “Game”、”Draw” 及 “GPU” 這三個數值最高的數字。若最高的數字出現在 “GPU” 則代表是 GPU-bound,若出現在 “Draw” 或 “Game” 則代表是 CPU-bound。

CPU

CPU 端的效能問題大多出現在以下情況:

  • 複雜 Blueprint 元件
    將 Blueprint 改寫為 C++ 版本,這個修正在 Switch 上有時候會有十到二十倍的效能提升。
  • 大量 draw instance 出現
    這部分可以利用 instanced mesh 大幅降低 draw instance,但同時也會因為 culling 數量降低造成 GPU 端的負擔,所以在實作上還需要注意到的地方,我們使用 Cull Distance Volume 來取得兩者之前的平衡。
  • Particle 含太多 Emitter 造成 CPU update tick 的負擔。這部分的優化只能調整美術做法以降低 emitter 數量或者多使用 animated texture 來降低 particle 的數量達到效能的提升。
  • Garbage Collection (GC)
    UE4 預設的 GC 時間間隔為 60 秒,而 GC 主要的用途是要用來釋放不需要的 UObject,因此會因為處理的數量影響 CPU 的效能。首先我們必須先理解 GC 的 Cost 計算方式:
GC Cost = Search Cost + Delete CostSearch Cost:簡單來說就是找出需要刪除的 UObjects ,又因為無法分散處理,因此是造成 CPU 負擔的主要因素。Delete Cost:切斷與其它 UObjects 的 reference 以及刪除欲刪除的 UObjects,但是可以分散至其他 Frame 處理,所以並不會造成 CPU 負擔。

我們優化 Search Cost 的方式是透過:
1. Cluster:減少搜尋 UObjects 的數量
2. Disregard Index:跳過不需要 GC 的 UObjects

GPU

GPU 的效能問題在 Switch 上被嚴重放大,這次我們主要的效能問題都是 GPU bound 且大多集中在 Pixel Shader 的負擔上。以下為較為顯著的優化方式:

  • Deferred Renderer ➮ Forward Renderer
    我們在開發過程中為了希望全平台的畫面效果以及 Light 配置能夠一致,所以決定 Switch 也使用 Deferred Renderder,但 1080p 解析度的狀況下,創建 G-Buffer 的過程需要針對五個 Render Targets 做畫面資訊的更新,想當然爾會造成 Pixel Shader 相當大的負擔。在希望至少維持 30 fps 畫面流暢度的狀況下,我們最終決定使用 Forward Renderer,而這個決定也為我們取得 GPU Time 6.8 ms 的空間,使得遊戲最終能夠以 1080p 的方式呈現更多美術細節。
    (註:由於我們使用的 UE4 版本為 4.20,所以使用的是 Clustered Forward Renderer;UE4 在 4.21 之後 Switch 平台就有完整的 Forward Renderer,狀況應會有所不同。)
  • Lighting 相關設置
    Sky Lights
    首先必須先理解 Sky Light 不同的 Mobility 造成的效能差異。Static 是速度最快的;Stationary 會需要一個額外的 Full Screen 計算處理所以會比 Static 慢 1~2 ms;Dynamic 是最慢的且大概會比 Stationary 慢 1~2 ms。由於我們是一個固定光源的遊戲,所以我們最後選用 Stationary 作為 Sky Light 的設定。
    Spot/Point Lights
    在有效能瓶頸的平台選用 Static 將 Lighting 的結果烘焙起來,因此 Light 設置也因為平台有所不同,也造成我們必須依照平台拆分 Map 檔案,因此無法完全共用所有 Content。
    Particle Lights
    由於 Particle 的光源通常都必須為 Movable,所以在使用上必須特別注意 Spawn Rate 以及光源的 Radius Scale。
Light Transform settings
  • Post-Process 調整
    為了取得效能的平衡,我們特別針對畫面的影響程度將以下效果關閉:
    SSAO
    由於畫面的風格以及遊戲視角此效果並不明顯所以僅使用 Material 內的 Ambient Occlusion Map。
    SSR (Screen Space Reflection)
    場景上較少高反射物件,所以將此功能關閉。
    Temporal AA ➮ FXAA
    由於 Temporal AA 需要創建 Velocity Map,等同需要一個 Full Screen 的運算,所以在效能上會比 FXAA 差 2 ms,因此最終我們選用了 FXAA 作為 Switch 平台的 Anti-Aliasing 方案。
  • Shadow Quality 調整
    主要針對以下方向做調整:
    1. 關閉不重要物件的 Cast Shadow。
    2. 調整 Cascade Shadow Map 的 cascades 數量。
    3. 調整 Shadow Map 的解析度。
  • Particle 調整
    Particle 影響效能無非來自數量以及 Particle Lights 的使用,在這個部分原先想要利用 Particle LOD 來做到平台優化的目標,但是由於 UE4 Cascade Particle System 的 GPU Particle 並沒有支援 Particle LOD,所以最終只能選擇針對效能較差的平台做簡化的 Particle。同樣的,這個部分也無法全平台共享相同 Content。

現階段結論

Project N 開發階段僅到達完成 Prototype 的狀態,但是由於:

  1. 事先針對遊戲規劃可跨平台共用的 Asset 規格
  2. 使用 UE4 提供的 Scalability 系統

在跨平台的製作上並沒有產生太多額外的程式/美術工作,但在共用 Content 上面有以下問題:

  1. 因為 Light 設置需要產生針對平台的 Map 檔案
  2. GPU Particle 無法使用 LOD 機制
  3. 若無 Mesh LOD 就無法針對平台配置各別 Material 設定

原則上,盡可能共用 Content 依舊是我們的主要目標之一,因為除了可以降低製作的數量之外,也可避免修改內容時遺漏修改相關對應平台內容的疏失。這部分也是我們後續開發會優先解決的問題。

--

--