Unity技術分享:歷時4年研發,《鬼泣-巔峰之戰》的製作幕後

5 月 28 日,雲暢遊戲 CEO 高雲崢和 Unity 的高階解決方案工程師李中元兩位大咖進入了 Unity 直播間,為大家帶來了技術分享。大波粉絲提問之下,問答區共計收到了 50 多個問題,還挖出了不少製作幕後。

本文精選了本次線上分享會的精彩內容,歡迎大家前往 B 站觀看完整版。

01

基於Unity的改進與定製

實現Console級的動作手遊

大家好,我是雲暢遊戲的 CEO 高雲崢,可以叫我 Bobby。今天我演講的主題是《基於 Unity 的改進與定製,實現 Console 級的動作手遊》。

首先,我們公司非常榮幸在 2016 年開始獲得了 CAPCOM 的授權,開始以《鬼泣》為 IP 開發手遊,就是《鬼泣-巔峰之戰》。這款手遊我們歷經 3、4 年的時間,與 CAPCOM 一起打造了一款真正意義上的《鬼泣》空戰手遊。

我先從整個產品的特性開始。首先大家都知道《鬼泣》這個 IP 是一款非常經典的主機 IP,它從 PS 一代開始一直延續到 PS 五代。鬼泣的作品也從一代一直到五代,包括各個特別家庭版,包括 DMC,每一代的作品動作片,基本上都是天花板的水平。當我們想要把這款遊戲帶到手機上來的時候,我們首先要藉助一個很好用的工具。我們在評估下來之後發現 Unity 是一款非常合適的引擎,去幫助我們在手機平臺上打造鬼泣。

下面就以幾點主要核心的說明,具體闡述 Unity 的表現能力以及在《鬼泣-巔峰之戰》研發中的應用。

01 頭髮的製作

首先我們講頭髮。為了表現更柔順的效果,我們使用了兩個 pass 進行渲染,同時,我們把頭髮分成了內層、外層和修飾層三個方面。左上角的圖在做三個方面的切割。

內層是我們防止穿模的層,帖圖幾乎是不透明的;頭髮的外層是我們在做但丁基礎的外形,這個主要的目的是角色設計;最後一層是修飾層,主要是為了修飾外形的,主要是為了增加一些細節,我們看到的頭髮的一些撅起的部分,一些髮梢劉海的效果,都是我們修飾的效果,這個是為了讓頭髮顯得更加生動,因為貼圖髮量很少。

02 動作系統

我將從三個方面闡釋《鬼泣-巔峰之戰》如何實現在戰鬥中的酷炫效果。

首先,流暢舒適的動作感受必不可少。為了達到流暢且合理的動作效果,我們將它的速度設為漸變,同時配合我們的 BlendTree 設定,使得動作實現流暢的變化。再輔以其他合理的機制,比如在加速跑到待機的過程加入對應的急停動作,使得《鬼泣-巔峰之戰》的動作感受達到整體的舒適和統一。

此外,我們還對複雜動作進行了動作分層的處理,為了在動作切換的時候不是太過僵硬。比如但丁開槍行為展現的同時,也會接收玩家發出的移動操作,這種複雜動作我們就要採用更復雜的動作分層相關技術。我們將上半身抬手進行射擊的區域,與下半身雙腿移動的區域進行分增與融合,結合玩家輸入型反饋,最終實現一整套複雜的動作行為,透過分層的方式我們對於複雜的動作進行解構。

最後,在動作切換方面我們也下了大功夫。我們把不同的動作進行劃分,可以劃分為基礎動作和行為動作,基礎動作是指人物的待機、移動、轉向,行動動作指釋放技能、受擊、與場景互動。基礎動作來說,任何動作的起點和終點都是有待機行為的,這個是有要求的,不能有特別大的動作,移動是要有速度,前面的 BlendTree 能夠生效,他們整體的規格務必要一致,比如說人物先邁左腳還是右腳的問題就必須要納入考慮了。

而對於行為動作,我們又可以進行二次細分,分為主動行為動作與被動行為動作。顧名思意,主動行為就是由於單位自身上主觀意願進行的動作行為,比如釋放技能,被動行為就是由其他單位導致自身進行的動作行為,比如受擊。下圖為某個切換過程的融合配置:

高雲崢老師還分享了《鬼泣-巔峰之戰》是如何製作角色表情、角色 AI、關卡、後處理最佳化等內容,盡在 B 站完整錄播中。

02 《鬼泣-巔峰之戰》精選問答

本次出鏡答疑的,除了雲暢遊戲CEO 高雲崢,還有《鬼泣-巔峰之戰》的製作人周輝和技術負責人緞君衡。

作為《鬼泣-巔峰之戰》的首款手遊作品,《鬼泣-巔峰之戰》是如何儘量還原主機端遊的觸感的呢?

周輝:

這個問題我們拆開兩塊來講。首先說一下原版的操作方式,原版是可以透過手柄或者是鍵盤來獲得按鍵反饋,這樣玩家很清楚知道我的按鍵是什麼,實現了什麼操作。然後,我們在《鬼泣-巔峰之戰》裡開發了這麼一個鎖定的功能。透過鎖定,透過推搖桿前後配合組合按鍵實現連打的操作。

其實這種手動鎖定的方式在手遊裡用的比較少,我們在最開始製作的時候也是沒有製作這個操作方式的。隨著開發版本的迭代,我們主動加入了鎖定的方式,這也是我們在手機上實現的比較有特色的點。透過不同按鍵的組合方式,去替代原版的手柄操作,鎖定+前後推遙杆的組合按鍵,在這個上面降低玩家的操作複雜度,同時也保留了原版所有的技能和招式,玩家在手遊裡也可以做實時連打等操作。

高雲崢:

我也幫周輝擴充套件一下,因為《鬼泣》是一個傳統的端遊作品,端遊共有12個按鍵,所以我們在手遊研發時做了大量的工作去簡化,這不僅是策劃的工作,技術的同學也參與其中,包括適配手柄等,這樣大家既可以在手機上體驗,也能夠連線手柄獲得更好的體驗。

空中戰鬥動作設計方面,與地面戰鬥有什麼不同的地方?

周輝:

這個問題問得比較專業,對於一個動作遊戲來說,在空中和地面其實在動作上是完全兩種製作方式。如果說傳統我們在地面上打擊和打擊反饋都是人在地下,我雙腳是在地面的,我攻擊都會伴隨一些角色往前走。所以我前面看到無論是揮刀,還是做一些射擊的動作,透過這種方式來體現角色的打擊感,這個對打擊感的幫助也是很大的。包括受擊一方也會給玩家帶來一個比較真實的受擊反饋。

但如果是在空中,首先角色在空中很難前後的移動,因為你更多還是基於上下的位移,同時我們所謂的怪物在空中的受擊也跟地面完全不一樣,最重要的因為是我們要在空中還原一個比較真實物理反饋的感受,當我在空中跟怪物發生攻擊之後,比如說有擊墜,或者有再次擊飛,這塊就需要我們的物理演算法能夠達到一個相對真實的反饋體驗。然後包括很多怪物從空中到地面,再從地面反彈起來的展現的形式也是需要我們去單獨的處理的。

所以,空中的話實際上會比地面在打擊反饋上更難以處理,包括在比如說我使用一個連打型的武器,全套在空中跟怪物追加的連打,一套連招打完之後,我對怪物相當於造成一個最終一擊的狀態的時候,這個時候其實對反饋更加要求難度大,同時要求更加真實。

《鬼泣-巔峰之戰》的研發過程當中,Unity 的引擎解決了哪些研發中遇到了印象最深的問題?

緞君衡:

我認為 Unity 在我們做《鬼泣-巔峰之戰》幫助最大的就是動畫狀態機。我們之前嘗試過自己做一些控制器和編譯器,但是效果都不太好。我們在最後切到 3D 機以後,比較大的優勢就是對我們的工作流非常友好,美術可以在沒有任何邏輯程式碼的情況下,可以直接預覽這些動作切換、動作融合,包括 BlendTree 這些東西都可以直接實時預案,不用運行遊戲。對策劃幫助也比較大,策劃其實可以在這裡做各種拓展。

對程式的好處一個是在這個動作融合的 BlendTree 上,《鬼泣-巔峰之戰》用的 BlendTree 會比較多,牽扯到很多層,整體來講效果比較好,另外一個就是,包括動作狀態的分層,如果我們自己去做我們相對來說有一些難度,效果可能也沒有它的這麼好。覺得這塊給我們的幫助是最大的。

03 SRP 底層渲染流程及原理

大家好,我是來自於 Unity 的高階解決方案工程師李中元,今天給大家帶來的分享是《SRP 底層渲染流程及原理》。

我們先來看一下整個 SRP 的簡單的架構,首先上面的這一層是我們常見的 URP 跟 HDRP,還有自己去擴充套件的一些自定義的渲染管線,一般這些上面的渲染管線會依賴於 SRP Core,SRP core 為我們提供了一些 Common 庫,工具函式,還有 Shader Library,再往下是其實是我們 Unity C++ 層面的一些東西,包括我們給大家提供的 Context,Culling,還有各種 Draw 的函式,當然了還有 SRP 獨有的 SRP Batcher。

這部分對大家來講更多是一個黑盒,今天分享的目的就是為了幫大家把這個黑盒開啟,透過理解 Unity 在底層做了什麼事情,大家可以利用這些資訊去更好的最佳化自己的專案。

我們先來看一下今天分享的內容,今天分享的內容包括三個大的部分,一個是 culling,就是 ScriptableRenderContext。Cull,Culling 就是我們在去調 Cull 函式的時候,Unity 底層會做的一些事情;還有我們的 Draw,Draw 其實包含我們呼叫的 CommandBuffer。Blit、CommandBuffer。DrawMesh 這些常見的用的 API,還包含 DrawRenderers、DrawShadows 這些 API 等等;最後是 Submit。

我們先來看 Culling,Culling 分兩部分,一個是 Culling 的過程,還有我會跟大家引入兩個概念,一個是 RenderNode,還有一個 RenderNode Queue,我會跟大家講清楚,Unity 為什麼會引入這兩個概念。

我們先來看一個小 DEMO,左邊是場景中燈光的設定,我們看到場景裡面有四盞燈光,每個燈光其實都是實時的燈光,然後每一個光都會產生陰影。

我們每一個場景裡面的每一個物體都會產生陰影。

透過我們執行這個 DEMO 檢視 Profiler 的 Timeline 檢視,能看到整個 Culling 的過程是這樣的,看上去比較複雜,我們先一點點看。

首先我們來看這個紅框框起來的部分,這部分是 Shadow 的 Culling。

我們先來嘗試著把場景中四盞燈光中其中三盞燈光的產生 Shadow 的選項改成 NoShadows。

再回來看一下,這個時候我們就發現整個的 Shadow Culling 的 Job 從四個減到了一個。我們可以看出 Unity 底層會為我們每一個產生陰影的燈光建立一個 Shadow 的 Job,去裁減我們整個的 Shadow。

再來看一個小細節,下圖箭頭指向的標籤——CullShadowCastersDirectionalDetnail。

這個跟什麼有關係呢?我們繼續修改我們場景裡面的設定,我們把場景裡面物體的會產生陰影的選項給關掉。

這個時候再執行 DEMO 看一下,這部分執行的開銷也小了。

到這裡我們能夠發現,如果我們想去減少整個陰影部分的裁減開銷,首先要去檢查一下場景裡的燈光是否有必要產生陰影,其次要去檢查場景裡的物體應不應該產生陰影,如果發現不合理的應該把這些選項都關掉,這樣陰影裁減這部分的開銷就能夠降下來。

我們看完了陰影部分的裁減工作,我們再來看一下左邊紅框裡面的部分,這部分是做什麼的?

這部分其實是在裁減場景裡面的動態物體,這幾個 job 會產生一個 IndexList 這麼一個數據結構,在 IndexList 存的是什麼東西呢?Unity 內部維護了一個場景裡面所有 renderer 的列表,IndexList 存的其實是當前可見的 renderer 的陣列下標。我們透過一個例子來看一下這些 job 是怎麼工作的。

在這個例子裡面,上面的部分從 0 到 19,這些其實就是 Unity 內部維護的 endererlist,然後我們產生了四個 Job,分別處理 List 裡面不同的部分。大家能夠看到底下,底下就是我們 IndexList,對於 IndexList 這裡能看到一個特點,就是這個 list 跟我們 Renderer 的 list 是等長的。

我們每一個 Job 處理裁減的時候,比如拿橘色 Job 作為例子,我們能看到在這個裡面第四個是不可見的,這時候 Cull Job 就會把 0、1、2、3 寫到 IndexList 裡面去。我們再來看一下紫色的 Job 是怎麼做的,7 和 8 中間這兩個元素是不可見的,我們應該是把 569 三個 Index 寫到 IndexList 裡面去。但是,它並沒有在 3 後面插入我們的 569,而是在自己的區間,比如說它只會訪問我們的 5 號到 9 號的 IndexList,所以說第一個能訪問的 IndexList 是 5。所以,它把 569 寫到了這個位置,後面也是依此類推的。

這部分為什麼 Unity 這麼去做?

首先每一個 Job 都是在不同的執行緒執行的,如果說每一個 Job 去獲取的資料,他們中間沒有任何的交集,我是不需要引入鎖的,所以他們訪問的所有的資料都是執行緒安全的。

當我們寫入 IndexList 的時候也是同樣的。當我們寫入的時候,橘色 Job 只會往 01234 這四個裡面寫資料,紫色往 56789 這裡面寫資料,我們在寫入的時候也是不用考慮鎖的,每個 Job 讀寫的資料都是獨立的,所以我們的 job 裡面是不需要給任何資料加鎖的,這個就保障了我們 Culling 過程的速度。

但是這會帶來一個小問題,就是我們產生的 IndexList 本身是不連續的,我們能看到 0123 和後面 569 中間會插入一個 0,9 後面有兩個 0,這兩個資料是不可用的。

Unity 後面會怎麼做的?在 Culling Job 完成之後,會有一個 combine 的操作,會把不連續的 IndexList,就是把後面這些資料拷到前面的空格里面來,這樣就會產生一個連續的 IndexList,我們這時候就得到了場景裡面目前可見的 Renderer 陣列的下標,這樣就完成了場景裡面動態物體裁減的過程。

以上只是 SRP 底層渲染流程及原理的部分內容。

Unity SRP 底層渲染的更多“黑箱秘密”,都在 B 站完整版影片中,點選“閱讀原文”即可前往。

華南買量困局|上海人才戰|二次元產品名單

天地劫|二次元抽卡|自研困局|Supercell新遊

天涯明月刀|雙人成行|Q2產品儲備