(轉\整)UE4游戲優化 多人大地型游戲的優化(一)游戲線程的優化


   

   

   

施主分享隨緣,評論隨心,@author:白袍小道

   

小道暗語:

1、因為小道這里博客目錄沒自己整,暫時就用隨筆目錄結構,所以二級目錄那啥就忽略了。標題格式大致都是(原or轉) 二級目錄 (標題)

   

2、因為所看和以前記錄太過雜亂,所以只能手動一點點搬移(回憶,整理)。歡迎討論,知識和能力總是被問出來了不是(嘿嘿,這樣才能成長), 若有不對別噴就好哈哈。

   

引言:

文章四方面包括了從游戲線程渲染線程GPU內存等的優化,提升游戲技術底子。

原作者:王禰,Epic Games 資深開發者技術支持,管理虛幻引擎技術支持的程序員團隊,擁有近15年虛幻引擎使用經驗。

   

正文:

優化肯定是有個前提和需求背景的,本文的前提:在移動設備上做大地型的多人游戲。

需求背景:

1、開放地圖:視野寬,視距遠,地圖大

2、場景:風格變化多

3、同屏人不少

4、交互也不少

(看到這里對吧,UE +上述 就直接說堡壘之夜就好了唄)

   

(由於篇幅較長,小道就直接拆開了,各位看官)

一、游戲線程的優化

(重要度管理,這個東西提出挺不錯)

   

1、重要度管理Significance Manager

 

例子LOD:

結論:重要度管理(可以自己整一個Manager,再在Manager下划分多個Manager,實現Iman)

的引入

 

原文:

我們怎么樣讓各個游戲模塊從游戲邏輯層去修正LOD的計算?這時我們引入Significance Manager,我們會分配針對每個平台的 Bucket,大家可以看到右下示意圖中藍色的小點代表玩家控制的角色,邊上的小點是別的玩家和交互的動態對象。我們根據離主角玩家的距離,在屏幕上的尺寸或者可見性,決定使用什么Bucket。例如基於可見性的計算,雖然離我很近,但是因為在我的背后,可能很多時候我都感受不到,Bucket就可以分得不一樣,通過Bucket我們會用來控制、修正LOD的各種計算。

   

這里是一個例子,我們這個系統本身用於我們自己比較火熱的游戲《堡壘之夜》它在手機、掌機、電腦都可以跑,我們兼容所有的平台可以連機玩,游戲在不同平台上的場景、復雜程度其實是一樣的。這種情況下,硬件的計算能力有非常大的差別,所以我們針對移動平台和主機Bucket也不一樣,除了自身控制的角色給的Bucket比較高,剩下的角色的比較低,主機有四個,手機有一個,這個設置不僅按平台來,也可以按設備來,移動設備好的和差的硬件計算能力差很多,我們可以在Device profile指定當前這台設備Bucket的規划。

   

剛才是比較全局的系統,接下來我們看游戲線程里開銷最大的部分就是我們的動畫,動畫系統大部分角色是可以定制的,角色會分為幾個部分,繪制調用的數量、動畫骨骼更新、不同部件的不同動畫計算量非常大,針對Fortnite這樣的游戲有一些特殊的游戲模式,例如50V50,這種情況下,最終在縮圈以后,同屏會出現超過50甚至80個角色,每個角色還分了好幾個部件,背包、武器都有不同的動畫,這個時候計算量非常大,我們需要對動畫做非常大量的優化。

   

   

2、動畫

剛剛我們已經說到角色可能分為幾個部分,有一些不同的策略,引擎提供各種方式,一種是將不同的部位的Mesh合成為一個,這個模型有一個問題,材質是要合並起來的,你的表情的動畫就沒有了,在這個方案上我們做了一些取舍,最終決定不在Fortnite用這種方式。

   

另一種,身上不需要動畫的剛體掛件可以方便的掛在角色骨骼的Socket上面,這是比較簡單的方式。還有Master-Slave的方式,主體動畫是一套完整的骨骼,身上掛載的動畫是這個骨骼的子支,這個時候我們可以把這些掛載的部件的動畫完全跳過自己的動畫更新計算,完全用Master驅動。這樣的骨骼動畫直接使用Master的骨骼矩陣,沒有辦法擴展,比如Master Skeleton沒有尾巴或是披風的骨骼,尾巴或是披風的獨立動畫或者物理模擬就沒辦法做。針對這種情況,我們還有一個解決方案是Copy Pose,可以把主體的計算完的骨骼矩陣拷貝給附屬的骨骼矩陣,只要保持目標骨骼和原骨骼的層級結構一致,就可以在目標骨骼上增加擴展性的骨骼,可以根據自己的狀態播自己的動畫,也可以模擬物理

四種多部件角色setup的方案,無論使用哪一種,都需要對骨骼模型和骨架設置LOD,這是下面提到多種優化的前提。

   

2.1

Event Graph

在動畫更新的時候會有大量的邏輯事件的計算,我們稱之為Event Graph,這是UE4提供的圖形化的腳本功能,Event Graph是需要經過圖形化的腳本虛擬機,這個調用在動畫邏輯比較復雜的時候開銷有點高,我們把在虛擬機上計算的Event Grape轉到C++,省掉了大量開銷。

   

   

2.2

Anim Graph

我們根據當前的狀態選擇不同的骨骼層級,播放哪個動畫,或是經過哪些骨骼控制節點,比如說IK、物理模擬最終的POSE的計算

 

   

在這個計算中間有一些步驟會用到數學計算,因為是在Graph,會有一些額外的開銷。我們做了一些優化,我們把所有這些獨立計算的模塊通通納入到一些基礎的骨骼動畫混合節點,包括偏移和縮放,這樣可以減少虛擬機的調用開銷,我們把這些包含簡單計算項的動畫混合節點叫做Fast Path節點(右上角有閃電小圖標),骨骼混合的計算邏輯通通是用Fast Path可以完全消除在虛擬機上的開銷。

   

同屏有那么多的角色要做骨骼動畫計算,大家知道移動設備是多核設備,為了更好的利用多核的定性,我們需要把剛剛這種虛擬機上的調用更好的平攤到不同的線程。基於上面兩個優化方向,我們不要使用Event Graph,把游戲邏輯更新的部分放在AnimInstanceProxy上,這樣引擎會自動判斷這個Event Graph是不是可以放在別的線程上更新。如果你用了Fast Path,我們就可以把骨骼的update和evaluation都放到working thread上面去,例如有50個角色,在任意一角色更新開始,就把計算分到別的線程上面,主線程繼續往下走。

   

即使我們能利用多線程,計算量還是非常大的,我們要減少動畫更新的數據量,已經有些設置可以幫助動畫在不渲染的時候跳過 Tick pose,也可以通過Singnificance Manager跳過附屬武器、背包的更新,除了自己的主角,別的角色離你遠一些,信息不更新其實你是注意不到的。

   

我們的掉落物會模擬物理,是骨骼物體。骨骼計算有一個問題,是走的Dynamic Path。我們引擎的中的靜態對象,會在加到場景中的時候就直接排序分組到自己的Drawing Policy,繪制的時候可以很大程度減少渲染狀態的切換。而動態的單位,是每一幀在渲染開始的InitViews階段動態獲取到數據,它和靜態獲取數據的方式不一樣,不會進入到靜態排序的表里,繪制的效率比較低。針對這種實際每一幀渲染數據不發生變化的骨骼物體,我們把這些物體額外加到了一個StaticRenderPath,加速了這些物體的渲染。(這里可以跳Unreal搬山)

   

2.3

URO(Update Rate Optimization)

   

URO(Update Rate Optimization),我們其實沒有必要對所有的角色在每一幀都做骨骼計算(頻率看着辦,公開吧)

   

比如畫面中一個角色的POSE上半身動作是怎么樣,下半身動作是怎么樣,是否需要融合,什么頻率融合,中間是不是要插值,這些設置可以非常大程度決定骨骼更新的計算量。大家可以看到下面的圖,左一是每一幀都更新,左邊二是每四幀更新一次,中間用插值,第三張圖是每十幀更新一次,中間用插值,最后一張圖是每四幀更新一次,不用插值(跳跳)。大家可以看到當角色占屏面積比較小,離得比較遠的時候其實是沒有大差別的。

剛才講的這些是針對骨骼動畫更新的優化,其實伴隨着骨骼LOD的設置,我們在AnimGraph中可以設置骨骼控制節點從某一級LOD下不計算,比如說IK、物理模擬。

   

   

3Scene Component

游戲線程還有大量的Scene Component,Scene Component是指世界中有坐標位置的對象,它的Transform更新都是在游戲線程中計算.

 

當你大地圖、大場景動態更新對象非常多,同時每個對象身上會掛很多Scene Component的時候,計算量是非常大的。盡管我們會把Scene Component的計算踢到異步線程,但是計算量依然很大。我們做了一些改進,針對一些掛載在人物身上,不是處於激活狀態的Scene Component做了自動的管理。

   

打開Auto Manage Attachment,對於音頻和粒子特效,可以自動根據它是否激活的狀態,決定是否掛在父級Scene Component。如果Detach掉,它的Transform就不會再更新

當Scene Component發生位置變化的時候會觸發Overlap的檢查,每一幀有大量運動對象時會產生大量Overlap事件,耗費比較大的開銷。

   

優化的原則是盡可能把不需要產生Overlap的事件關掉,注意引擎默認是打開的。

   

我們對層級結構比較復雜的做了子Component是否打開overlap事件的引用計數,會看自己是不是打開了Overlap事件,以及自己的子對象有沒有打開。這個時候我們在做Overlap檢查的時候可以很快的跳過,這個節點往下都沒有,就不需要再檢查自己的子節點,這在場景的對象結構比較復雜的情況下是可觀的優化。

   

   

4Character Movement

( 小道說就注意一個檢查這種邏輯若多的話,可以按重要度減少,再減少。單機游戲主角隨意,對吧嘿嘿)(網絡同步,特別VR上,需要同步的數據若可以插值盡量插值,簡單粗暴效果也不差)

Character Movement,因為角色比較多,角色的移動更新是非常大的游戲線程的計算。針對這個計算,一部分是角色在移動的時候要檢查新的位置是不是能站立,要做一些掃描,要做一些碰撞,還要找落腳點是不是斜坡,這個斜坡的斜率是不是角色可以站上的,往前走的高度變化是不是可以超過跨過階梯最大的高度,角色一多計算量就非常大。所以除了玩家自己控制的角色,需要比較精確的計算外,其余角色分到的Significance Manager的Bucket我們最終是用了插值,通過網絡同步過來的位置做簡單的插值來模擬預測計算,在大部分時候都不容易注意到明顯的差異,只有在幀數較低或者網絡帶寬受限比較嚴重的時候,對於落地點會有顯著的偏差,大家可以對比看到這兩個視頻中左邊是預測計算,右邊是插值。

   

5Physics

(物理是真實,但也會穿幫,一些程序麻煩點模擬模擬還是做的.前提還是重要度)

替代的Physics優化物理注冊的對象

Physics,我們會盡可能的用一些替代的Physics優化物理注冊的對象,有一組對象,比如說邊界,不需要很細致的碰撞模型,我們可以用簡單的volume來表達物理碰撞對象,減少注冊到物理場景中的對象數量。物理的一個場景會有兩個樹,一個用以做Query,一個用於做Simulation,我們要盡可能保證注冊進去的對象最優化。因此需要盡可能的簡化每個物理對象的復雜度,以及減少整個場景注冊的物理對象數。可以同時以比較小的內存開銷打開異步的物理場景,Physics注冊的對象是一樣的,只不過他會用Shared Shape的方式加到Async Scene里,這樣在場景做物理模擬的同時,他可以在異步的scene里做其他的query。

另外我還嘗試過把同樣mesh的不同實例對象用Shared shapes減少注冊的物理對象的內存開銷,在內存敏感的場景下也可以嘗試。

還有一個思路是我們可以把物理對象和視覺對象解耦(就是碰的和看得丫的不是一個),默認的情況下,引擎的Mesh對象打開碰撞就會注冊物理對象到PhysX Scene,增加了物理場景的復雜度和物理的內存占用。因此當你的Mesh加載到內存里,即使不被渲染出來,這些開銷就在了,但是其實很多情況下視覺會看得更遠一些,實際需要物理計算交互的距離在有些游戲中沒那么遠,我們可以用一些手段把視覺上對象的物理關掉,把這個物理屬性轉到一些新的Component和Actor上面放到新的Streaming Level里,用更近的加載卸載距離來管理,這樣實際的物理場景復雜度和內存占用都會小很多。

另外移動端的布料,計算量和網格數量相關,在移動端會不太推薦使用那么復雜的模擬,引擎也就沒有提供移動端的NvCloth的lib,所以我們一般會用剛體來模擬

   

7Ticking

XD關鍵詞:

在藍圖事件卡上Tick更新,虛禮機調用開銷。

Tick事件觸發的執行隊列

   

8UI

多利用我們新出的SlatLayoutCaching和Invalidation Box來Cache Prepass減少widget transform更新的計算,這些Cache可以把計算的位置和大小記錄下來,有一些可以把頂點Buffer Cache下來

   

另外,我們也需要盡量讓UI的Widget可以Batching起來。引擎的一些布局空間會自動幫你布局子控件,例如Horizental和Vertical Box,Grid等,這時候子控件是在同一層(Layout)上,引擎會優先Batch起來。

   

當使用比較靈活的Canvas Panel時,會導致引擎默認的行為會把每個加入的子空間的Implicit Zorder自動增一,這時候如果你確定這些子Widget不重疊,其實可以手動控制這個ZOrder。當然Batch的前提還是你用了同樣的材質和貼圖。那么如果做一個背包界面,里有很多不同東西的圖標,我們又希望這些圖標有一些特效,我們可以用同一個材質,這只同一個Texture Altas,針對每個子控件設置不同的Vertex Color,在Vertex Shader里通過VC的值做為uv來使得這些子控件可以被Batching起來。

   

音頻和特效,音頻是比較大的開銷,我們之前的堡壘之夜又是從主機到移動端兼容的項目,為了優化音頻在移動端的開銷,我們增加了做了很多設置,使得在移動端不同的設備可以設置不同的SoundCue並發的數量,以及SoundSource的數量。

其中SoundSource默認在移動端上總數是16個,主機上可能是32個。簡單說明一下什么是SoundCue,這就是原始的SoundWave資源拿過來做一些實時處理封裝后的音頻資源,例如可以在多個SoundWave中做一些隨機、拼接,以及一些聲音效果的實時處理,這些處理效果對計算量要求比較大,我們可以針對不同的硬件設備做一些LOD的設置,比如說在比較差的CPU移動設備上,可以把Reverb,EQ等關掉,或者減少隨機的Wave的數量等。

   

Particle比較顯著的開銷是Overdraw,我們在PC上有自動把貼圖的Alpha切割出八面體,減少Overdraw的功能,但是這個功能之前在移動端無法使用,最近我發現其實只要支持SRV的設備,是完全可以用這個功能的,移動端上也可以打開。

   

(XD記下)另外,所有的半透也可以以獨立的RenderPass以低分辨率繪制在upscale回來以減少overdraw帶來的大量的fragment的開銷。

   

9、關卡流

(異步,裁分)

   

10Server

(丟包,Cache下來合並再發)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM