Havok物理引擎與Unity3D的結合

背景

  在重度手遊的研發過程當中,遊戲中的車輛模擬,場景互動,特效展示等功能很多時候需要物理引擎的介入,以提供豐富的交互體驗。目前3D手遊的開發主要工具是使用Unity3D引擎,於是,如何在Unity3D的開發過程中結合入物理功能變一個需要仔細考慮的問題。

  我們考察的2種物理效果實現方案:Unity3D物理引擎和Havok物理引擎。

  Unity3d物理引擎介紹

  Unity3d在內部集成了PhysX物理引擎,爲其提供了物理模擬能力。

  Physx是目前使用最爲廣泛的物理引擎,PhysX目前由Nvidia公司開發並維護,特點是免費且帶N卡的GPU物理計算加速功能(1)。

  PhysX被很多遊戲大作所採用,使用PhysX製作的遊戲:


                                  

4.X版本的Unity3D集成的是2.8.3的PhysX,該版本較爲老舊。在Unity5中將集成PhysX3.3,較2.8.3版本在功能和性能上有較大幅的提升,但是目前unity5並不是非常穩定。

  Havok物理引擎介紹

  Havok物理引擎是由Havok公司開發的老牌物理引擎,與PhysX不同,Havok專注於CPU端+多線程模擬方案,並且與PhysX的強大市場推廣以及免費策略不同,Havok授權很嚴格,而且基本不提供試用版本下載(2)。

  使用Havok引擎的遊戲大作在數量上與使用PhysX的不相上下,而且很多令人印象深刻:

  Unity3D物理與Havok物理的功能對比

  Unity3D集成的PhysX物理功能:

  Unity3D通過其提供的各種Component訪問PhysX的物理功能,打開菜單欄中的Component->Physics便可以看到各種組件:


  其中,

  Rigidbody提供了剛體的訪問接口。

  各種XXXCollider提供了3種碰撞包圍體方案(Primitive,Mesh,Terrain)。

  WheelCollider組件提供了車輪模擬方案。

  XXXCloth組件提供了布料模擬方案。

  XXXJoint組件提供了關節與連接點模擬方案。

  PhysicsMaterial資源類型提供了表面物理材質描述功能。

  在Unity3D中每一種物理組件都有對應的編輯界面,且即拖即用,非常方便。



  PhysX引擎目前已經涵蓋各個平臺,且跨平臺特性已經融入Unity3D的跨平臺機制中,用戶無需再關注跨平臺開發。

  由於PhysX與Unity3D的深度結合以及Unity3D的閉源特性,修改PhysX的底層模擬機制基本不可能。

  Havok物理引擎功能介紹:

  Havok物理引擎以C++庫的形式,通過組件式的方式,提供了豐富的物理功能。包括了:

  • Rigidbody剛體模擬。
  • 5種碰撞包圍體模擬方案(Primitive,Convex,Mesh,Compount,Terrain)
  • 完整的VehicleKit車輛模擬方案。
  • HavokCloth不了模擬方案。
  • Constraint關節與連接點模擬方案。
  • HKX物理資源數據描述格式以及對應序列化與反序列化接口。
  • HavokDestruction破碎模擬方案。


  Havok並沒有開發官方的Unity3D結合插件,市面上也沒有第三方的結合插件可以使用,需要自行開發,有一定的開發成本。

  Havok具備稱述的跨平臺能力,但是由於沒有結合入Unity3D的跨平臺機制,在發佈到多平臺上需要一點額外的工作量。

  Havok在獲得授權之後,用戶可以修改與定製各個層級的物理功能。

  Unity和Havok主要物理功能對比



  結論:

  遊戲中簡單的物理效果展示可以使用生的PhysX引擎,但是如果需要擴展功能或者訂製細膩的物理效果,可以考慮其它物理方案。

  Unity與Havok物理引擎的結合

  既然有結合其它物理方案的需求,那麼接下來我們便討論一下結合的方案。

  結合基本原理

  要實現Havok與Unity3D相結合,那麼兩個系統之間的交互通信機制便是實現的關鍵。Unity3D提供的二次開發平臺是Mono,並沒有提供NativeCode級別的接口,而Havok是完全以C++編寫的Native實現。所幸,Mono爲IL提供了跨平臺的NativeCode交互機制。

  在Win和Linux(Android)平臺上,Mono提供了以dll和so爲基礎的動態鏈接庫交互C++代碼形式,而在IOS平臺上則是以AOT爲基礎的靜態鏈接庫交互形式,而這種混合編程的方法,便是讓Unity3D與Havok交互的基礎。
                             


  如圖:結合的主要思路就是,Havok作爲一個子系統,以插件的形式和Unity3D結合,並通過Mono進行交互。

  結合的具體過程:兩個系統的更新流程

  由於Unity3D依然負責整個遊戲的主更新流程,那麼便需要將Havok的更新整合入Unity3D的流程中。一個比較合理的更新流程應該是邏輯對象先更新,然後將物理參數傳入物理引擎,邏輯對象更新完成之後,物理引擎開始進行物理模擬,物理引擎返回模擬結果,Unity3D使用模擬結果進行渲染。



  要實現正確的物理效果,必須保證Havok的正確更新時序,但是多個MonoBehaviour本身更新時序就是混亂的,給結合帶來了一定的難度。解決方案是,建議將整個遊戲所有的邏輯對象更新收歸於一個MonoBehaviour中,場景中GameObject上掛接的其餘MonoBehaviour僅用於配置參數,不做任何的邏輯更新。

  結合的具體過程:兩個系統關鍵對象的對應關係

  更新流程的結合可以保證兩個系統能運轉起來,但是要實現具體功能,則必須明確這兩個系統之間的功能對象以及他們的對應關係。

  Unity3D的遊戲世界是以Scene爲單位的,而在Havok中對應的概念是HavokWorld,同一個HavokWorld中的對象纔會相互碰撞。Unity3D中的遊戲對象是以GameObject附帶各種組件來表現,而在Havok中,剛體以HavokRigidbody表示,車輛以HavokVehicle表示,布料以HavokCloth表示,並沒有一個直接對應關係,因此,需要在概念上加以封裝,然後再對應。

  如果我們以CGameScene封裝了UnityScene,CGameEntity封裝了遊戲邏輯對象,CHavokWorld封裝了物理世界,CHavokEntity封裝了物理功能對象,那麼,以一個剛體碰撞對象爲例,他們之間的對應關係應該是:



  對象關係明確之後,對象之間的更新時序也可以相應明確:



  簡單描述上圖中,一個剛體對象的整個功能流程是:CGameWorld中的CGameEntity在一系列邏輯更新之後,準備好了一系列的物理更新參數(可能是需要改變的額外力大小,或者是需要改變的質量大小);然後CGameEntity將這些參數設置給物理對象CHavokEntity,CHavokEntity將參數分解,交給具體的實現對象(hkpRigidbody剛體實現對象);當所有的CGameEntity都傳遞好參數之後,CGameWorld通知CHavokWorld,開始模擬;等模擬結束之後,所有的CGameEntity再紛紛從CHavokEntity中獲取模擬的結果(由hkpRigidbody維護的位置,朝向等最終模擬結果),而這些模擬結果,最終會被設置給GameObject的Transform,拿去做下一步的邏輯處理或者直接渲染。

  結合的具體過程:物理資源製作流程的結合

  Havok擁有自己的資源描述格式:HKX。HKX可以存放從包圍盒到剛體對象(hkpRigidbody實例)等很多信息,HKX擁有文本和二進制兩種模式,但是任何一種模式都不被Unity3D直接支持。被結合入Unity3D的Havok物理引擎要想訪問到HKX格式的資源有兩種方法:

  一.將HKX文件直接放到Unity3D的StreammingAssets目錄下,在Havok的C++代碼層面通過不同平臺的IO API去讀文件。

  這樣的不便之處在於需要自己維護StreammingAssets目錄下的大量文件,也不能使用Unity3D的異步IO機制。



  二.利用Unity3D的ScripableObject機制,將HKX文件內容序列化到UnityAsset中,讓Unity3D在IO之後以byteStream的形式傳給Havok。

  好處在於,可是使用Unity3D編輯器強大的文件管理機制,以及異步IO系統。弊端在於byteStream傳遞過程中可能帶來內存的額外開銷(包括GC以及內存峯值)。



  結合的具體過程:物理場景的編輯流程結合

  Unity3D以靈活好用的編輯器著稱,所以應該將場景中物理對象的編輯結合入Unity3D編輯器。提供一個可行的方案:

  以Unity3D原有的GameObject場景編輯模式爲基礎,在需要物理表現的GameObject上,綁定物理描述腳本,腳本中描述了物理對象的包圍體資源和初始化物理信息。物理對象的初始位置可以使用GameObject的初始位置,這樣當PlayScene的時候,Unity3D便可以通知Havok根據該物體的初始位置以及描述腳本中的信息去創建物理對象,進行模擬。



  如圖,物理描述腳本EntityRoadBlockParam中包含了路障對象的包圍體,質量,碰撞組等物理信息。這種形式的結合方案的不足之處在於,每一個Scene都必須包含Havok引擎完整的更新流程控制代碼,無法做到像Unity3D原生物理引擎那樣直接拖拽組件就能看到效果。

  小技巧與經驗

  物理對象調試工具

  Havok提供了一個可視化的工具——Havok VisualDebugger,將物理世界的信息實時顯示出來,方便調試。該工具使用Socket連接的方式與HavokRuntime實時通信,當HavokRuntime結合入Unity3D之後,依然能夠正常工作,省去了我們寫可視化Debugger的工作量。



  如果受限於網絡環境不能使用VisualDebugger,那麼手動繪製物理對象信息也是一個可選的方案,Havok提供了API可以將包圍體幾何信息輸出(以VertexList和IndexList的形式),可以直接填入Unity3D的Mesh組件,在Gizmos或者Renderer中實時繪製,用於觀察物理對象是否模擬正常。

  穩定物理模擬的幀率

  Havok物理引擎在模擬過程中需要一個穩定的更新間隔時間,如果這個間隔時而長時而短,會導致模擬不穩定,發生穿透,位置跳變等現象。解決的辦法是,一旦發現間隔時間過長,則在過長的間隔時間之內,以固定的頻率多模擬幾次,我們稱爲穩定模擬模式。



  該方法可以保證物理模擬的穩定性與精確性,但是弊端是可能會進一步降低遊戲的幀率。在遊戲更新過程中還是應該儘量保證幀率的穩定,避免物理引擎進入穩定模擬模式。

  異步物理模擬以提升效率

  Havok默認是多線程模擬的,因此,可以對於遊戲過程中僅作爲表現的物理對象做異步更新,異步更新流程如圖:



  異步更新的好處是可以用邏輯更新和渲染更新的時間做多線程模擬(T1 + T2時間段),降低表現物理層的模擬時間,這個方案會導致渲染層晚一幀得到物理模擬結果。但是,如果物理對象的模擬數據需要拿去做邏輯更新,那麼還是需要使用同步模擬機制,否則會增大系統的複雜性。

  性能分析

  Havok結合到Unity3D之後,性能是一個需要關注的問題。我們做了一個Profile,對比Havok和Unity3D原生的Physx物理性能,用例如下:
                                              


  在場景中堆置168個Block,以一定的間隔時間給每一個Block施加一個隨機力,用來擾動這些Block,使他們相互碰撞,觀察模擬耗時,在Sansung Note4(3) 手機上,分析結果如下:

  PhysX_Discrete離散模式



  Havok_Discrete離散模式

  Physx_Continue連續模式



  Havok_Continue連續模式
                                            

  從分析結果上可以看出,在離散模式下,Havok和PhysX的模擬消耗差不多;但是在連續模式(4)下,PhysX的數據有點離譜,PhysX的連續模擬模式在手機上完全無法使用,而Havok則有高出離散模式一倍的開銷。

  (1)大多數使用PhysX的遊戲並不會使用到GPU加速功能,原因有兩點:1,大多數次世代3D遊戲GPU負擔都非常重,並沒有餘力做太重度的物理模擬;2.與顯卡進行數據傳輸也是有開銷的,輕度物理模擬若算上這一部分開銷,在GPU端做並沒有佔到很大便宜。

  (2)Havok的授權很嚴格,試用需要聯繫其商務代表。

  (3)Samsung Note4 Spec :

  PLATFORMOSAndroid OS, v4.4.4 (KitKat), v5.0.1 (Lollipop), upgradable to v5.1.1 (Lollipop)

  ChipsetQualcomm Snapdragon 805 Exynos 5433

  CPUQuad-core 2.7 GHz Krait 450 (Snapdragon 805)

  GPUAdreno 420 (Snapdragon 805)

  Mali-T760 (Exynos 5433)

  MEMORYCard slotmicroSD, up to 128 GB

  Internal32 GB, 3 GB RAM

  (4)連續模擬模式(Continuous Simulation),用於解決子彈穿紙類的問題,需要在高速小物體位置更新過程中,使用迭代計算或者射線檢測等手段求的高精度碰撞檢測與處理結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章