天美GDC分享SLG新技術:千人同屏實時戰(zhàn)斗是怎么做出來的?

更宏大的戰(zhàn)斗場面,一直是很多傳統(tǒng)MMO和SLG產品開發(fā)者追求的目標。
千人同屏,則是這個目標的縮影。例如下面視頻中,騰訊《重返帝國》在復原大規(guī)模戰(zhàn)爭場面、拉高游戲策略爽度同時,也在思考如何向下兼容,從而保證玩家們戰(zhàn)斗時的流暢體驗。

比如最近,騰訊天美T2工作室戶端組主程序肖健,以及引擎負責人侯倉健在全球游戲開發(fā)者大會(GDC)上,分享了使用Unity DOTS技術在《重返帝國》實現(xiàn)「千人同屏實時戰(zhàn)斗」的相關思考和實踐經驗。
GDC對于非贊助類主題演講的要求一向比較苛刻,往往會提前半年面向全球游戲開發(fā)者征集提案,由組委會層層篩選評比最終確認。包括重返帝國這一技術分享在內,騰訊天美工作室群在此次GDC共入圍了4項非贊助主題演講,意味著天美在游戲領域的多項技術探索獲得了行業(yè)的廣泛認可。
演講重點內容如下:
《重返帝國》是一款全3D的SLG手游,游戲場景規(guī)模宏大,經常會出現(xiàn)超過1000個士兵一起戰(zhàn)斗的場景。
在有限的移動設備性能上,如何同時兼顧性能與品質?團隊在嘗試過C#、C++以及DOTS等多種技術方案的選型與研究后,最終選擇了Unity DOTS。
Unity DOTS對于團隊可以說是一次敢為人先的選擇,當時市面上并沒有比較知名的使用這項技術的游戲項目,所以這項技術最后呈現(xiàn)出來的效果其實是沒有太多參考的。
其次,當時DOTS是處于一個比較初期的版本,Unity官方還在不停的修改和完善,這意味著團隊享受不到新的features,甚至可能需要處理一些潛在的隱患,這對團隊來說是不小的挑戰(zhàn)。
01 實戰(zhàn)分享
團隊一方面與Unity官方保持密切的合作與交流,另一方面經過多次的技術迭代與優(yōu)化,最終在《重返帝國》項目上取得了很好的實踐效果,在移動設備上為玩家呈現(xiàn)了極高品質的視覺效果。同時團隊也積累了一套行之有效的方法論,以下總結了幾點分享給大家。
1.Job數(shù)據(jù)依賴分析與優(yōu)化,提升整個系統(tǒng)的并發(fā)性
2.將部分ECS System的Job與非ECS邏輯并行,充分發(fā)揮多核
3.邏輯數(shù)據(jù)顯示分離,提升chunk內存利用率,減少資源加載帶來的卡頓
4.針對System進行邏輯降頻,保證效果同時也提升性能
當我們完成了整體的框架設計和核心的實現(xiàn)后,在進行性能分析的時候發(fā)現(xiàn)Job的并發(fā)性并不高,且worker存在大量的idle狀態(tài),導致系統(tǒng)的整體耗時偏高。
為此,我們專門開發(fā)了靜態(tài)分析工具輔助我們找出System之間的讀寫沖突與依賴,通過數(shù)據(jù)拆分、數(shù)據(jù)備份來解決沖突,讓耗時較高的Job能夠并行。

數(shù)據(jù)依賴靜態(tài)分析工具

數(shù)據(jù)拆分解決寫入沖突

數(shù)據(jù)備份解決讀寫沖突
基于工具的分析,我們不斷的細化和調整,解決了數(shù)據(jù)的沖突依賴,顯著提高了Job的并發(fā)性,最終達到了我們相對滿意的并行效果。

數(shù)據(jù)依賴優(yōu)化后Job的并發(fā)執(zhí)行
之后,我們還將System按照功能進一步的細化拆分,把一部分Job的執(zhí)行提前到與非ECS代碼邏輯并行,進一步從整體上提高了我們的游戲幀率。

ECS Job與非ECS邏輯并行
在進行了Job并行性優(yōu)化之后,我們發(fā)現(xiàn)在大地圖上拖動時存在由于Entity資源同步加載導致的一些耗時峰刺,這對玩家來說是體驗上的損失。
所以我們針對Entity,使用邏輯與顯示分離,一方面讓資源可以異步加載減少卡頓,另一方面也提升了單個chunk的內存利用率減少CPU的cache missing。

邏輯顯示分離-資源異步加載
最后,我們在不影響效果的前提下,針對部分System進行邏輯降頻與錯幀(如移動邏輯計算相關的System降到12幀、耗時較高的MoveJob與AnimatorJob錯幀執(zhí)行),讓整體的耗時更加平滑,并且有效的降低了游戲的功耗。
02 性能優(yōu)化
當時我們使用的是 Unity2019版本,Hybrid Render V1 版本,為了能更順利的將DOTS適配到我們的項目中,我們也在原始框架的基礎上,在資產與渲染方面也進行了大量的按需開發(fā)。
我們在接入 DOTS 技術棧時,主要面臨了以下3個問題:
1.資源兼容性:因為在接入時已經處于項目中期,很多游戲資產及對應的生產流水線已經成型,所以如何將已有游戲資產轉變成可在 DOTS 技術棧中運行的資產,是我們需要解決的問題。
2.邏輯階段的基礎開銷過大:可能會導致千人同屏場景出現(xiàn)時出現(xiàn)卡頓。
3.渲染階段無法修改自定義的材質屬性:因為我們對于戰(zhàn)斗場景的還原重度依賴 GPU Instancing 技術,所以需要很多自定義的材質屬性可以在運行時被復寫。
于是,我們針對以上問題逐個研究核心痛點,找到了適合我們項目的解決方案。
在資源兼容性方面,在綜合評估了各種方案之后,我們決定實現(xiàn)一套自己的序列化和反序列化流程。
我們的方案分為離線和運行時兩個階段:
離線時,我們將游戲中各類資產對應的prefab拆分成二進制文件和引用到的資源文件;
運行時,我們創(chuàng)建了一個“deserialize world”,用來把離線時生成的二進制文件和資源文件反序列化,生成entity。
當entity生成好后,我們再把它們移入default world進行運行。這樣我們既可以在資產制作階段使用我們熟悉的prefab,也可以減少運行時的轉換時間。

資產Entity實例化
對于HybridRenderV1在邏輯階段的開銷過大,我們定位到了核心的瓶頸是主線程阻塞。比如整個生成合批信息的過程都是放在主線程中進行的,這個過程有很大的優(yōu)化空間。
我們的優(yōu)化方向就是多線程化,充分利用移動端的多核優(yōu)勢。其實在生成合批信息時,不同的RenderMesh一定對應不同的batch,任務本身具有可多線程化的特性。
所以如下圖所示,我們分配了一個較大的緩存數(shù)組,數(shù)組的大小與線程數(shù)量和RenderMesh數(shù)量相關。多個線程并行完成對含有RenderMesh的Chunk進行篩選,并填入緩存數(shù)組的指定位置。因為在緩存數(shù)組中,每個線程都有自己的寫入空間,所以多線程并行時,不會產生數(shù)據(jù)寫入沖突。

多線程RenderMesh Batch
我們還對游戲中LOD的結構進行了優(yōu)化。我們游戲中的模型一般有4層LOD,在轉換成entity后,將會有6個相關的entities生成。
過多的entity不僅浪費內存,同時也會導致很多冗余計算(比如同步位置信息),而根據(jù)LOD的特點,我們可以只記錄單個LOD的信息,在渲染時按需替換成應當顯示的LOD Mesh即可,這樣我們就可以把原本的4個LOD網格當做一個單獨的網格來對待。
同時,我們也將LOD Group節(jié)點和Root節(jié)點進行了合并,Entity的數(shù)量也從原來的6個下降到2個,性能也有了提升。
這種方式帶來的一個額外好處是當我們更高層級的LOD還未加載完成或渲染壓力過大時,我們可以只加載低層級的LOD模型來顯示。

LOD結構優(yōu)化
為了在C#中更改材質的Instance屬性,我們定義一個和Instance屬性完全匹配的IComponentData Struct,在數(shù)據(jù)對齊方面,我們遵循std140內存數(shù)據(jù)對齊原則。如下圖所示


Instance屬性對齊
在渲染運行時,我們根據(jù)entities的數(shù)量預先分配一塊大的緩存,之后利用多線程把各個可見的entity的InstanceParam數(shù)據(jù)復制到Buffer中的指定位置。最后將整個緩存直接提交至GPU,我們就可以按照傳統(tǒng)的GPU Instance方式來使用緩存中的數(shù)據(jù)了。
在有了RenderMesh上的材質信息和mesh數(shù)據(jù)之后,我們的InstanceBuffer也組織好了,這樣通過調用Unity的DrawMeshInstanced接口就可以進行渲染了。

Instance Data Buffer
以上都是團隊在實踐中不斷迭代總結出來的寶貴經驗,希望能對那些同樣想使用Unity DOTS技術的團隊能有所啟發(fā)。
消息來源:
騰訊天美工作室群&GDC