網路同步在遊戲歷史中的發展變化(五)—— 物理同步

Jerish發表於2020-12-22
五:物理同步

1.概念與理解

- 什麼是物理同步

2.問題與解決方案

- 物理引擎的確定性問題
- 同步誤差如何被物理模擬放大
-   物理同步Demo案例介紹
- 《看門狗》的載具同步
- 《火箭聯盟》的物理同步策略

網路同步是遊戲開發中比較重要且複雜的一項技術,但是由於網上資料參差不齊導致很多朋友對部分概念和原理理解有誤。作者耗費近一年的時間,參考了大量的論文與資料(不下100篇),最終整理出來“網路同步在遊戲歷史中的發展變化”系列文章,希望能夠幫到大家(求關注與分享)。上一篇文章我們分析了狀態同步的發展歷史以及相關優化手段,這篇主要是針對物理同步技術做分析和總結。

前段時間,@知乎韋易笑老師 闡述了有關“幀同步”一詞在國內的發展歷史也解釋清了該名詞被亂用的原因。總的來說,國內“幀同步”與國外“Lockstep”的核心思想是一致的,為了方便描述,這篇文章會使用“幀同步”替代“LockStep”。

五、物理同步

1.概念與理解

所謂“物理同步”,字面上講就是“帶有物理狀態物件的網路同步”,嚴格上來說它並不是一個標準的技術名詞,而是大家約定俗成的一個概念。按照我的個人理解,可以進一步解釋為“在較為複雜的物理模擬環境或有物理引擎參與計算的遊戲裡,如何對持有物理狀態資訊的物件做網路同步”。在英文中,我們可以使用Replicate physics simulated objects 或者Networked physics來表示類似的概念。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
網路遊戲《火箭聯盟》

不過,考慮到並不是所有物理現象都交給物理引擎處理,而且有物理引擎參與的網遊也並不一定需要對同步做任何處理,所以我們常說的物理同步更多的是指“在網路遊戲中,如果玩家的位置或者與玩家互動物件的位置需要經過物理引擎的模擬處理來得到結果,那麼其中涉及到網路同步技術就可以稱為物理同步”。(這裡的物理模擬一般指整個物件完全交給物理引擎去計算碰撞、位置、約束等,很多情況下可以等價為對Ragdoll的模擬)

備註:物理一詞涉及的範圍非常廣,在遊戲裡面應用的場景也很多,但是並不一定需要進行網路同步,比如簡單的拋物線運動,射線檢測,與玩法無關的場景破碎等。

早在上世紀70年代,就誕生了許多圍繞物理特性產生玩法的遊戲,不過由於當時計算機系統算力有限,涉及到的物理計算都非常簡單(比如乒乓球遊戲中小球的移動模擬[1])。隨著計算機效能的飛速提升,開發者們考慮將環境中的所有物件都交由統一的物理模組驅動,由此慢慢的催生出了通用的物理引擎[2]。很快的,各個遊戲開發商逐漸將物理引擎整合進來,將更多更復雜的物理模擬過程應用到遊戲中,製作出了諸如極品飛車、FIFA、NBA、憤怒的小鳥等圍繞物理特性進行玩法設計的遊戲。另一方面,隨著計算機網路的發展,遊戲中的網路同步技術愈加成熟,網路遊戲的品質也不斷向單機遊戲靠攏,我們也得以將傳統的單機遊戲擴充成多人遊戲。物理模擬作為提升遊戲趣味性的一大技術也自然逐漸被納入其中,物理同步變得重要起來。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
遊戲《Pong》

2.面臨的問題與解決方案

正如所前面解釋的那樣,物理同步並不是一種特殊的同步方式,而是在物理引擎和網路同步技術共同發展的條件下而誕生的一種綜合行性解決方案,其核心手段還然是我們熟悉的幀同步或者狀態同步。使用幀同步技術我們需要每幀把玩家的Input資訊傳送出去,然後讓另一端的物理引擎根據輸入去模擬結果。如果使用狀態同步我們則需要本地模擬好資料並把物理位置、旋轉等關鍵資訊傳送到其他的客戶端,然後其他客戶端可以根據情況決定是否再執行本地的物理模擬(如果是快照同步,由於拿到的就是最終的結果,那麼就不需要本地再進行模擬了)。

這樣看來,物理同步好像與常規的同步也沒什麼本質上的區別,那麼為什麼他卻是一個難題呢?我認為原因有以下兩點:

  • 物理引擎的不確定性
  • 在物理引擎參與模擬的條件下,網路同步的微小誤差很容易被迅速放大

首先,我們談談物理引擎的確定性問題。很不幸,目前所有的物理引擎嚴格來說都不是確定性的,因為想保證不同平臺、編譯器、作業系統、編譯版本的指令順序以及浮點數精度完全一致幾乎是不可能的。關於物理確定性的討論有很多[3],核心問題大致可以歸類為以下幾點:

  • 1.編譯器優化後的指令順序
  • 2.約束計算的順序
  • 3.不同版本、不同平臺浮點數精度問題[4][5]

(問題1與問題3其實是密切相關的)

這裡摘選一段PhysX物理引擎的描述[6]:

The PhysX SDK can be described as offering limited determinism(注:提供了有限程度的確定性). Results can vary between platforms due to differences in hardware maths precision and differences in how the compiler reoders instructions during optimization. This means that behavior can be different between different platforms, different compilers operating on the same platform or between optimized and unoptimized builds using the same compiler on the same platform(注:不同平臺、編譯器、優化版本都會影響確定性). However, on a given platform, given the exact same sequence of events operating on the exact scene using a consistent time-stepping scheme, PhysX is expected to produce deterministic results. In order to achieve this determinism, the application must recreate the scene in the exact same order each time and insert the actors into a newly-created PxScene. There are several other factors that can affect determinism so if an inconsistent (e.g. variable) time-stepping scheme is used or if the application does not perform the same sequence of API calls on the same frames, the PhysX simulation can diverge.

如果遊戲只是單個平臺上發行,市面上常見的物理引擎(Havok,PhysX,Bullet)基本上都可以保證結果的一致性。因為我們可以通過使用同一個編譯好的二進位制檔案、在完全相同的作業系統上執行來保證指令順序並解決浮點數精度問題,同時開啟引擎的確定性開關來保證約束的計算順序(不過會影響效能),這也是很多測試者在使用Unity等商業引擎時發現物理同步可以完美進行的原因。當然,這並不是說我們就完全放棄了跨平臺確定性的目標,比如Unity新推出的DOTS架構[7][8]正在嘗試解決這個問題(雖然註釋裡面仍然鮮明的寫著“Reserved for future”)。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
常見物理引擎

考慮到物理引擎的確定性問題,我們可以得出一個初步的結論——完全使用幀同步做物理同步是不合適的(或者說做跨平臺遊戲是行不通的)。而對於狀態同步,我們可以定時地去糾正位置資訊來避免誤差被放大。如果一定要使用幀同步去做跨平臺同步,那麼只能選擇放棄物理引擎自己模擬或者用定點數來改造物理引擎,這可能是得不償失的。

下面不妨先排除掉一致性的問題,來看看如何實現所謂的“物理同步”。實際上,無論是優化手段還是實現方式與前兩篇提到的方案是幾乎一致的,幀同步、快照同步、狀態同步都可以採用,增量壓縮、Inputbuffer等優化手段也一樣可以用於物理同步的開發中。Network Next的創始人Glenn Fiedler在2014年撰寫了一系列的物理同步相關的文章[9],使用一個同步的Demo非常詳細地闡述了同步技術是如何應用以及優化的。涉及到的技術點大致如下,涵蓋了網路同步的大部分的知識細節:

  • 如何確保物理引擎的確定性
  • 如何實現物理幀同步
  • Inputbuffer如何改善幀同步
  • 為什麼用UDP替代TCP
  • 如何實現快照同步
  • 怎樣用插值解決網路抖動
  • 如何通過快照壓縮減少網路流量
  • 如何實現增量壓縮
  • 如何實現狀態同步

另外,在2018年的GDC上,Glenn也對物理同步進行一次演講分享[10],具體的細節建議大家移步到Glenn Fiedler的網站以及GitHub[11]去看。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
Glenn Fiedler的物理同步Demo

接下來,我們再來談談第二個難點,即網路同步的誤差是如何被物理模擬迅速放大的(尤其在多人互動的遊戲中)。我們在前面的章節裡也談過,為了保證本地客戶端的快速響應,通常會採取預測回滾的機制(Client prediction,即本地客戶端立刻相應玩家操作,伺服器後續校驗決定是否合法)。這樣我們就犧牲了事件順序的嚴格一致來換取主控端玩家及時響應的體驗,在一般角色的非物理移動同步時,預測以及回滾都是相對容易的,延遲比較小的情況位置的誤差也可以幾乎忽略。然而在物理模擬參與的時候,情況就會變得複雜起來。

主控(Autonomous/Master)以及模擬(Simulate/Replica)都是針對某個物件而言的。

假如有兩個客戶端,玩家A控制小車1,玩家B控制小車2。小車1在玩家A的客戶端上就是主控的,小車2在玩家A的客戶端上就是模擬的。同理,小車2在B客戶端上就是主控的,小車1在B客戶端上就是模擬的。

假如在一個遊戲中(帶有預測,也就是你本地的物件一定快於遠端)你和其他玩家分別控制一個物理模擬的小車朝向對方衝去,他們相互之間可能發生碰撞而彼此影響運動狀態,就會面臨下面的問題。

1.由於網路同步的誤差無法避免,那麼你客戶端上的發生碰撞的位置一定與其他客戶端的不同。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
本地客戶端的模擬小車(對手小車)一定落後其控制端

2.其次,對於本地上的其他模擬小車,要考慮是否在碰撞時完全開啟物理模擬(Ragdoll)。如果不開啟物理,那麼模擬小車就會完全按照其主控端同步的位置進行移動,即使已經在本地發生了碰撞他可能還是會向前移動。如果開啟碰撞,兩個客戶端的發生碰撞的位置會完全不同。無論是哪種情況,網路同步的誤差都會在物理引擎的“加持”下迅速被放大進而導致兩端的結果相差甚遠。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
不開啟物理模擬條件下,模擬端不會在碰撞時立刻停下

其實對於一般角色的非物理移動同步,二者只要相撞就會迅速停止移動,即使發生穿透只要做簡單的位置“回滾”即可。然而在物理模擬參與的時候,直接作位置回滾的效果會顯得非常突兀並出現很強的拉扯感,因為我們幾乎沒辦法在本地準確的預測一個物件的物理模擬路徑。如果你仔細閱讀了前面Glenn Fiedler的文章(或者上面總結的技術點),你會發現裡面並沒有提到常見的預測回滾技術,因為他只有一個主控端和一個用於觀察結果的模擬端,並不需要回滾。

在2017年的GDC上,來自育碧的技術負責人Matt Delbosc就《看門狗2》中的載具同步進行了演講[12],詳細的分析了多個主控端控制不同物件發生碰撞時應該如何處理。

《看門狗2》的網路模型是基於狀態同步的P2P,主控角色預測先行而模擬物件會根據快照(snapshot,即模擬物件在其主控端的真實位置)使用Projective Velocity Blengding做內插值,他們在製作時也面臨和上面描述一樣的問題。假如兩個客戶端各控制一個小車撞向對方,由於延遲問題,敵人在本地的位置一定是落後其主控端的。那麼就可能發生你開車去撞他時,你本地撞到了他的車尾,而他的客戶端什麼都沒有發生。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
收到快照再進行內插值,會有延遲造成兩邊不一致

所以,首先要做的就是儘量減少不同客戶端由於延遲造成的位置偏差,Matt Delbosc引入了一個TimeOffset的概念,根據當前時間與TimeOffset的差值來決定對模擬物件做內插值還是外插值,有了合適的外插值後本地的模擬物件就可以做到儘量靠近敵方的真實位置。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
使用外插值減少延遲

而關於碰撞後位置的誤差問題,他們採用了Physics Simulation Blending技術,即發生碰撞前開啟模擬物件的RigidBody並設定位置權重為1(快照位置的權重為0),然後在碰撞發生後的一小段時間內,不斷減小物理模擬的權重增大快照位置的權重使模擬物件的運動狀態逐漸趨於與其主控端,最終消除不一致性,騰訊的吃雞手遊就採用了相似的解決方案[13]。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
根據碰撞時間調整權重

不過實際上, Matt團隊遇到的問題遠不止這些,還有諸如如何用插值解決旋轉抖動問題,人物與載具相撞時不同步怎麼辦等等,知乎上有一篇譯文可以參考[14]。

可能有些朋友會問,如果我不使用預測回滾技術是不是就沒有這個問題呢?答案依然是否定的,假如你在執行一個車輛的中間突然變向,而這個操作被丟包或延遲,只要伺服器不暫停整個遊戲來等待你的訊息,那麼你本地的結果依然與其他客戶端不同進而產生誤差。也就是說除非你使用最最原始的“完全幀同步”(即客戶端每次行動都要等到其他客戶端的訊息全部就緒才行),否則由於網路同步的延遲無法避免,誤差也必定會被物理模擬所放大。

同樣在2017年,另一款風靡全球的競技遊戲——《火箭聯盟》悄然上線,可謂是將物理玩法發揮到了極致。次年,《火箭聯盟》的開發者Jared Cone也來到了GDC,分享了他們團隊是如何解決物理同步問題的[14]。

《火箭聯盟》的核心玩法是“用車踢球”,每個玩家控制一個汽車,通過撞擊足球來將其“踢”進敵方的球門。由於是多人競技遊戲,所以一定要有一個權威伺服器來避免作弊,最終的結果必須由伺服器來決定。相比於《看門狗》,他們遇到的情況明顯更復雜,除了不同玩家控制不同的小車,還有一個完全由伺服器操控的小球。按照常規的同步方式,本地的主控玩家預測先行,其他角色的資料由伺服器同步下發做插值模擬。但是在這樣一個延遲敏感且帶有物理模擬的競技遊戲中,玩家的Input資訊的丟失、本地物件與伺服器的位置不統一都會頻繁的帶來表現不一致的問題,而且FPS中常見的延遲補償策略並不適合當前的遊戲型別(簡單來說就是延遲大的玩家會影響其他玩家的體驗,具體原因我們在上一篇延遲補償的章節也有討論)。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
客戶端有延遲,導致伺服器實際位置不同

為了解決這些問題,Jared Cone團隊採用了“InputBuffer”以及“客戶端全預測”兩個核心方案。InputBuffer,即伺服器快取客戶端的Input資訊,然後定時的去buffer裡面獲取(buffer大小可以動態調整),這樣可以減少網路延遲和抖動帶來的卡頓問題。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
Inputbuffer的作用

客戶端全預測,即客戶端上所有可能產生移動的物件(不僅僅是主控物件)全部會在本地預測先行,這樣本地在預測成功時所有物件的位置都是準確的,客戶端與伺服器的表現也會高度一致,當然預測失敗的時候自然會也要處理位置回滾。

網路同步在遊戲歷史中的發展變化(五)—— 物理同步
採用全預測後的效果

仔細分析這兩款遊戲,你會發現他們採用都是“狀態同步+插值+預測回滾”的基本框架,這也是目前業內上比較合適的物理同步方案。

除了同步問題,物理引擎本身對系統資源(CPU/GPU)的消耗也很大。比如在UE4引擎裡面,玩家每一幀的移動都會觸發物理引擎的射線檢測來判斷位置是否合法,一旦場景內的角色數量增多,物理引擎的計算量也會隨之增大,進而改變Tick的步長,幀率降低。而幀率降低除了導致卡頓問題外,還會進一步影響到物理模擬,造成更嚴重的結果不一致、模型穿透等問題,所以我們需要儘量減少不必要的物理模擬並適當簡化我們的計算模型。


來源:遊戲開發那些事
原文:https://mp.weixin.qq.com/s/XQwIcOEJUGBuNFDWWd_-aA

相關文章