虛幻引擎——場景動態載入

2020-09-20 11:00:38

藍圖(blueprint)是一種以C++為編譯目標的視覺化程式語言,首次出現在虛幻引擎4中,實質上也是為了緊跟low code的時代潮流,用圖論取代文字,讓開發者不必再拘泥於語法導致的基本問題。圖形語言最大的好處在於可以將程式碼中的各種樹形邏輯展示出來,大大提升了開發效率。

本文的重點是用藍圖實現UE4自帶的level streaming volume,這玩意翻譯過來叫「場景流觸發體」,在此之前需要介紹下什麼是level streaming(場景流)。

Level Streaming:場景動態載入

理論基礎:和LOD(level of details)始終佔用記憶體的特性不同,場景流可以選擇性的流入/流出記憶體,大大提升效率,尤其是那些躲在室內,從外面看不見的物體,就沒必要載入它們了。場景流主要有2個好處:

  • 選擇性載入場景:節省cpu/記憶體開銷

  • 模組化分工開發:多人獨立開發,最後組合起來

level(場景)本是content browser中的map型別的uasset檔案,但可以在Levels視窗中將它們以層級關係聯絡起來,本質上是對整個專案進行元件化劃分,但最常見的用途就是動態載入場景,比如:

  • 無縫地圖切換:大型開放世界遊戲中,人物走到哪,場景載入到哪

  • 被遮擋的物體:如在玩家到達房間門口,準備進門的時候臨時載入房間內的場景

  • 可見的載入場景:一些cyberpunk主題地圖或者恐怖遊戲中,走著走著,環境就變了,或者一回頭出現一個**

總之,場景動態載入是每一個大型3D遊戲的必備。

Tip:在無縫載入情況下,level streaming肉眼是看不到效果的(比如進入房間前),但可以根據World Outliner底部統計資料的變化來判斷效果。

Level Streaming Volume:場景流觸發體

所謂的Volume是一個三維的brush(BSP)幾何體,看不見摸不著,卻能觸發碰撞,將之放在Persistent Level中然後繫結到某個sublevel,人物進入volume體內後觸發sublevel中所有actor的載入。

藍圖實現Level Streaming Volume

做web開發的時候,每次出現一個新api,在所有瀏覽器相容它之前,我們總是習慣自己去實現這樣一個函數,作為過渡,順便證明自己理解了它。同理,在LevelStreamingVolume出現之前,通過藍圖或C++來實現這個Volume。

首先建立一個藍圖類,因為要實現可放置,所以藍圖類基於actor建立,同時建立一個等待載入的level:subLevel。

然後給這個actor準備3個元件和1個變數:

  • 一個用於碰撞檢測的Box Collision【看不見,摸得著】

  • 一個用於暗示觸發區的半透明cube【看得見,摸不著】

  • 一行提示資訊Text Render

  • 一個全域性變數levelId

其中,cube和box空間上完全重疊,cube用半透明的材質代表「可進入」,text位於它們的上方,效果如下:

希望實現的效果是:當肉體進入這個橙色區域時載入levelId,離開時載出,levelId可以讓使用者指定,所以要開啟「Expose on Spawn」選項。

整個邏輯很簡單:先對box監聽onComponentBeginOverlap事件,發生重疊時判斷引數otherActor是否等於玩家(getPlayerCharacter),如果相等則呼叫loadStreamLevel(levelId);

載出場景的邏輯與之相反,只要將對應的方法替換成onComponentEndOverlap和unloadStreamLevel即可。

場景流節流

人物進入volume的時候,2個不規則的幾何體發生重疊,可能會觸發好幾次重疊事件,所以有必要做一個節流(throttle)減少開銷。

呼叫loadStreamLevel之前判斷一下,如果場景已經載入,則停止向下執行:我們通過getStreamingLevel(levelId)獲得場景的參照,再傳入isLevelLoaded判斷載入狀態。所以整個藍圖如下:

這是載入部分,載出部分基本對稱,就不放圖了。自此,引擎自帶的Level Streaming Volume被藍圖成功地模擬了。