從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

遊資網發表於2021-05-13
五一節前,我們舉辦了首次「Unity 大咖作客」線上分享會,邀請到 24 Entertainment 資深客戶端開發工程師姚金毅和 Unity 大中華區技術經理高川,以動作動畫為主題展開分享。

從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

應粉絲們許願,首場「Unity 大咖作客」線上分享會的文字版本來啦。本文精選了兩位嘉賓分享的部分內容,如果想要觀看完整版以及現場 Q&A 環節的精彩內容,歡迎前往 B 站。



《永劫無間》的動作與運動系統

大家好,我是來自 24 Entertainment 工作室的姚金毅,今天給大家帶來的是《永劫無間》的動作與運動系統。

首先,我們簡單介紹一下我們遊戲以及我們怎麼利用 Unity 引擎進行開發的。我們《永劫無間》是一款多人動作類的端遊。在整個開發過程中,我們對引擎做了很多自定義的開發,Unity 官方的技術支援部門以及一些駐場工程師也對我們的開發過程給予了非常大的幫助,尤其是幫助解決一些疑難雜症的問題。

今天,我主要分享一下動作和運動系統方面的經驗和心得。

01、動作系統總覽

《永劫無間》的動作系統主要是使用了 Unity 裡面的人型骨架系統,也就是 Humanoid 的動作。我們目前有兩個英雄體型,有少量的非英雄體型的怪物,動作 state 的數量大概有 2000 多個,動作片斷目前有 9000 多個,正式上線可能會突破 1 萬個,因為我們還在不斷地製作新英雄、新武器。

我們的動作既有動捕的,也有美術手工製作的。使用動捕比較多的是一些角色的受擊和角色的死亡動畫表現上面。因為這部分的動作我們希望把它做得儘量地真實、豐富、流暢一點。

我們還較多地使用了分層動畫的系統。就像我們的英雄目前有 10 個 layer,可以說非常多了,包括基礎層、左右手、頭家左右手、上半身、手指層等等的。比較常見的比如下半身跑路,上半身做一個射擊瞄準之類的。

我們在做動畫系統的時候通常會涉及到一個主題是我們如何控制角色位移的。兩種主流的做法,一個是用動作本身來啟動,也就是開啟 RootMotion;另外一個是用程式裡面計算角色位移,去應用它的位移。整體上來說,在我們遊戲裡面絕大多數情況是使用 RootMotion 的。

首先是我們的動作美術具有非常豐富的動作遊戲開發的經驗,他們能把一個角色的位移比如一個簡單的跑步能做得更加有節奏感,表現得更加真實,所以說這部分的位移的主動權都是交給美術的。程式位移也有小部分的一些應用。比如跳躍需要根據角色跳的長短做不同高度的跳躍,包括飛索可能有不同速度、不同方向的飛行,一些受擊的位移需要配置不同的參數列現不同距離的位移,不同時間長短的硬直之類的。這部分的位移是靠程式進行驅動的。

從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

02、Playable API 構建底層動作控制系統

我們知道在 Unity 裡面 Animator 構建一個動作狀態機是非常方便的,你可以直接在 Animator Controller 裡面建立一些動作 state,新增一些transation的動作過渡連線,設定一些引數和過渡條件,給每個 state 配置上具體對應的動畫,基本上一個可以跑起來的動作狀態就構建好了。

但是,這樣的方式在我們這樣一個非常注重動作的遊戲專案裡面應用起來也還是有一些困難的。其中非常典型的問題就是前面我們提到我們的動作 state 有 2000 多個,如果把 2000 多個節點都放到 Animator Controller 裡面在我們看來是幾乎無法維護的。幸好,Unity 還是提供了一個 Playable API 允許我們進自定義地重新寫一套動作控制的系統。我們的專案也是用這個 Playable API 重新構建了一套動作底層的控制系統。我們今天主要聊聊使用這樣的機制有些什麼樣的好處。

首先,它可以控制動畫載入的策略,可以按需載入,也可以非同步載入。第二點是可以更加靈活地控制 Playable Gragh 的資料流,可以比較方便地插入一些自定義的 AnimationJob 來做一些特殊的動作機制,動作表現。第三點是我們可以載入自定義的配置資料,讓動作系統能夠更加方便地和其他的遊戲系統結合。最後,我們可以做一個自由度更高的 Override 機制。我們知道 Animator 本身是有一個 Override Animator 的概念,在我們專案裡面其實做了一個更加高階的 Override 的機制,能讓一些動作更加方便地組合和覆蓋。

這裡有一些參考資料,基本上也是使用 Playable API 重新進行了對動畫控制系統的構建。


[1] SimpleAnimationhttps://github.com/Unity-Technologies/SimpleAnimation
[2] Animancer:https://assetstore.unity.com/packages/tools/animation/animancer-pro-116514

03、ProBulider 工作流

現在來分享一下我們具體是怎麼進行離線標註的,這裡必須要提一個非常棒的 Unity 工具, ProBuilder,它在我們專案裡面發揮了非常大的作用。大概有三個方面:一個是關卡的原型;第二個是構建測試場景;第三個是輔助特殊運動觸發區的設定。

首先講如何使用 ProBuilder 來設計關卡原型呢?在我們專案裡面一個場景的構建過程大概有這樣三步。第一個是關卡策劃來搭建場景白模,第二個是通過迭代不斷地驗證玩法,這個場景白模是能符合我們遊戲玩法的,等整個場景的拓撲結構大概定下來之後,最後我們美術才會進場對場景進行一個美化。

這個工作流程也是現在遊戲開發裡面,尤其是 3D 遊戲開發裡面比較規範、比較科學的一套開發流程。

從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

這裡面場景的白模搭建基本上都是用 ProBuilder 來實現的。

第二個 ProBuilder 的功能就是搭建一些測試的區域。我們所有的運動觸發區,比如像障礙,我們的策劃會搭建不同規格的障礙在測試場景裡面。不管是程式開發還是 QA 測試都會提供非常大的便利性。

從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

第三個是我們前面提到的一個運動觸發區的離線標註系統,這個我們也是通過 ProBuilder 外掛的方式提供了一些輔助。

比如說這個爬樹的觸發區,我們可以選一些樹的面,把這個 trigger 給刷出來。屋簷的觸發區可以選屋簷的線,把 trigger 刷出來。比如說這個天花板,這個索樑,都可以選對應的面把 trigger 刷出來。

整體上我們可以通過 ProBuilder 比較方便地選取模型中的點線面,輔助通過一些運算來直接把這個 trigger 刷出來,這樣就比人肉手動地擺放一些 trigger 對齊場景有更高的效率。

從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

姚金毅老師還分享了《永劫無間》如何在執行時檢測周邊環境,如何做物理資料管理、動作錄製和回放工具、離線動作取樣工具等內容,完整版已上傳至 B 站。

Animator 定製優化方案

剛才感謝《永劫無間》的小夥伴為我們帶來了非常精彩的分享,今天我們們撿一點乾的聊一聊,講一個 Animator 的定製方案。

今天著重要講的是一個熱點問題——Animator 裡面的 Override,首先從原理的角度分析,我們瞭解一下這個熱點是如何形成的。

現在我的 demo 裡有兩個 U 醬。這兩個 U 醬現在都是一個 Tpose 的狀態,分別叫 Unity Normal,Unity Mass。她們是同一個 Avatar,但是我給了她們每一個人不同的 Controller。

Normal 的 Unity 醬加了一個標準的 Unity Controller,有兩層的 state。我們現在一層的 state 裡面加了一個新的狀態叫 test,它對應了我自己做的一個空動畫;另外一層就是 face,這一層在 demo 裡面原來是用來控制小姐姐的動作表情的 state。

我們再看一下 Mass,Mass 的上一層就是我們的 base layer,和剛才的 Normal 是一模一樣的。這個地方我們也加了一個 state test,這個地方我們加了一個 demi。我們在 face 這一層做了茫茫多的 state,其實就是把上面的 state 複製貼上。

從搭建到優化,《永劫無間》如何做遊戲動作與運動系統

能看到在我們的螢幕上有兩個按鈕,一個叫 Override Normal,一個叫 Override Mass,這兩個操作幾乎做的是同一件事,就是我們會對 Controller 裡面 dummy 的節點給它同樣 Override 一個 wait 動畫進去。

當我去 Override Normal 和 Override Mass 的時候,花在 Override 這件事情上的時間,Mass 會更多。

為什麼 Mass 多?

在 Unity 這個 Override 裡面,我們發現會有相當長的一段時間,這段時間都在做什麼?這個是很神奇的事情,因為 Unity 在 Tag 裡面沒有看到更詳細的資訊了。其實,這個地方 Unity 會嘗試把你的 Animator Controller 裡面所有的 state 合併到一個資料結構叫做 Animation set,它會把資料結構都放進去。所有的資料意味著你裡面所有的 Animation Clip,注意,再乘上所有 clip 用的曲線都要經過一系列的運算。

什麼意思?我們可以看一下這個曲線,我們在裡面找一個 Animation Clip。當我們點這個的時候會有一些統計出的曲線數。這些曲線數對於優化來講是有意義的。在這個裡面大家會看到有一個叫 const 的曲線,如果 const 曲線的比值越高,在我們剛才說的做那段計算的時候所需要進行的計算就會相對少一些。

上面還有很多曲線,比如 curvepose,還有尤拉、scale 等等。這些曲線是要經過一系列的合併運算的,也就是說這些曲線在後期你們取樣的時候會真正地參與運算的。const 曲線就是一個數,大家可以認為是一個常數的形式參與的。所以 const 的曲線帶來的效能消耗並不大。像我這種百分之百的還好說。但是,我們在真正實際的專案中會看到有很多的曲線數大概在 20% 到 10%,剩下 Stream 的曲線佔比會非常非常高。

另外,我們要看它這些線的總數。比如說我在這個地方有 10 個 AnimationClip,每個 AnimationClip 大概有 300 條曲線,你可以簡單地認為它要做 3000 次運算,這些運算當然不是一個線性比例,但是大致上是這樣的線性關係。

也就是說在我們的 state 裡面,你基礎的 state 越多,每一個 state 用的曲線越複雜,無論這個節點最終有沒有參與到你執行時的最終表現中,它都會在 Override 的時候產生效能消耗的。

當然,我們在現實工作中如果用 Animator 這套系統,它的整體數量可能比我 demo 裡面的數量還要大,甚至大出幾個數量級。包括在一些主機專案等等更大型的專案裡面如果用 Animator 的話也會面臨同樣的問題。

下面看一下如何優化?

優化也很簡單。第一種方式就是我們儘量地減少整個狀態機的複雜程度,比如說我們可以在裡面儘量多地使用 dummy。比如同樣一個狀態,我們在這個地方要 Override,我們就在基礎狀態機裡面儘量少地使用很複雜的動畫,不要一上來就把它都放在基礎狀態機裡面用。如果你的基礎狀態機非常複雜,比如有上千個,你每次至多是 Override 其中的兩三個。這種相對來講效能比較虧,因為你每次要把這些不動的全要運算一次。

所以說這種情況下我們建議你可以拆成多個 Controller 來控制,或者有一些開發者會把 Animator 拆分成兩個或者是三個 Animator 來控制,也可以的。總而言之,就是要減少你基礎狀態機中 Animation 或者是動畫數量的大小。

另外一個,當我們做一個動畫的時候,我們檢查一個動畫的時候重點要看到這個曲線數,儘量增加它 const 的曲線數。有的同學說我匯入的時候它就不是 const 怎麼辦呢?在 Unity 匯入的時候實際上是有匯入選項的,大家如果做過一些常規優化都知道,在我們 Animation Compression 裡面有一個 Keyframe reduction,或者是 optional,一般來講上面的選項更直接一點。

還有一種情況是怎麼樣優化的?比如說我們這個地方有一個 Keyframe,我們會發現它有一個偏差值,但是這個偏差值不是個絕對值,是個比。比如說 0.5,當我K出來的動畫本身運動幅度非常非常小,比如前一個 Keyframe 是 0.000000……1,下一個幀多一個 0 的 1,那它的比值實際上是比較大的。但實際上在肉眼觀察,這個東西幾乎沒有什麼變化。

所以,大家也會經常看到一些資料上說我們可以嘗試給 FBX 裡面動畫的精度做一個精度的削減。比如我們只保留小數點之後 5 位或者 3 位的動作,從而讓我們的Keyframe reduction 可以壓縮更多的本來就很相近的關鍵幀,從而減少這個曲線的數量。這樣可以減少一部分記憶體,因為你所有曲線 keyframe 的資料最終在 Animationset 的時候起來要填充到一個記憶體塊裡面,這個東西本身是比較費的。另外,它可以產生更多的 const 的動畫曲線,從而減少你整個 Override 的消耗。

當然,還有一種方式是像我們《永劫無間》的小夥伴們使用 timeline 的方式。因為 timeline 和 Animator 最大的區別就是,Animator 在設計上是把整個 Controller 視為一個整體的。什麼意思?在 runtime 看來,我今後所有的操作是一整個的 Controller,而並不是大家在圖裡看到的 A 動畫、B 動畫。從 Unity 編輯器的角度看,做完這些合併的工作之後只有一個 Animator Controller,裡面是一大堆的資料。所以,你任何一次 Override,任何一次對 Controller 的修改都是對整體資料集的修改,這個修改是非常龐大的。

而 Unity 在做 timeline 的時候就避免了這個問題,timeline 實際上是基於你每一個 Clip 修改的。當我 Override 一個 Clip 或者是更換一個 Clip 的時候,實際上要更新的資料就只有 Clip 這一部分。因此,它相對來說整體的效能消耗就會更平緩一些,不會像 Animator 這樣容易出現一個峰值。

最後我還要推薦一下我自己給開發者做過的定製方案,這個優化方案的優化思路是什麼?

既然我們在很多的專案組中要被 Override 的動畫是個 dummy,很多開發組在實際開發過程中經常更換的話,他們的基礎狀態機裡面就會放一個 demi,既然是這樣,不如把它做一個前提約定,約定你要 Override 的動畫在這個狀態機裡面一定是個空動畫,基於這一點,我們就可以玩出很多花樣了。

優化之後的效果如何呢?我之前測的比較穩定的一個數大概是 1.32,也就是說我們基於 Unity 這樣一個優化了之後,從 4 點幾到 1.32。

今天講的是動畫方面的。我最近有一年左右的時間一直在給各個遊戲廠商做類似這樣的定製方案,如果大家有興趣,也可以到我們官方諮詢更多物美價廉的定製方案,花錢不多,但是你可以擁有一個完全屬於你的 Unity 引擎,聽起來還挺酷的。大家可以通過掃這個二維碼,或者和我們這個郵箱聯絡,諮詢相關的優化方案。

來源:Unity官方平臺
原文:https://mp.weixin.qq.com/s/OUdDJRA9YAcQ6CuTyDcahw

相關文章