網(wǎng)絡(luò)同步在游戲歷史中的發(fā)展變化(五)—— 物理同步
五:物理同步
? 1.概念與理解
?? ? - 什么是物理同步
? 2.問題與解決方案
? ? ?- 物理引擎的確定性問題
? ? ?- 同步誤差如何被物理模擬放大
? ? ? ?? ?-? ?物理同步Demo案例介紹
? ? ? ?? ?- 《看門狗》的載具同步
? ? ? ? ? - 《火箭聯(lián)盟》的物理同步策略
網(wǎng)絡(luò)同步是游戲開發(fā)中比較重要且復(fù)雜的一項(xiàng)技術(shù),但是由于網(wǎng)上資料參差不齊導(dǎo)致很多朋友對部分概念和原理理解有誤。作者耗費(fèi)近一年的時間,參考了大量的論文與資料(不下100篇),最終整理出來“網(wǎng)絡(luò)同步在游戲歷史中的發(fā)展變化”系列文章,希望能夠幫到大家(求關(guān)注與分享)。上一篇文章我們分析了狀態(tài)同步的發(fā)展歷史以及相關(guān)優(yōu)化手段,這篇主要是針對物理同步技術(shù)做分析和總結(jié)。
前段時間,@知乎韋易笑老師?闡述了有關(guān)“幀同步”一詞在國內(nèi)的發(fā)展歷史也解釋清了該名詞被亂用的原因??偟膩碚f,國內(nèi)“幀同步”與國外“Lockstep”的核心思想是一致的,為了方便描述,這篇文章會使用“幀同步”替代“LockStep”。
五、物理同步
1.概念與理解
所謂“物理同步”,字面上講就是“帶有物理狀態(tài)對象的網(wǎng)絡(luò)同步”,嚴(yán)格上來說它并不是一個標(biāo)準(zhǔn)的技術(shù)名詞,而是大家約定俗成的一個概念。按照我的個人理解,可以進(jìn)一步解釋為“在較為復(fù)雜的物理模擬環(huán)境或有物理引擎參與計(jì)算的游戲里,如何對持有物理狀態(tài)信息的對象做網(wǎng)絡(luò)同步”。在英文中,我們可以使用Replicate physics simulated objects 或者Networked physics來表示類似的概念。

??網(wǎng)絡(luò)游戲《火箭聯(lián)盟》
不過,考慮到并不是所有物理現(xiàn)象都交給物理引擎處理,而且有物理引擎參與的網(wǎng)游也并不一定需要對同步做任何處理,所以我們常說的物理同步更多的是指“在網(wǎng)絡(luò)游戲中,如果玩家的位置或者與玩家交互對象的位置需要經(jīng)過物理引擎的模擬處理來得到結(jié)果,那么其中涉及到網(wǎng)絡(luò)同步技術(shù)就可以稱為物理同步”。(這里的物理模擬一般指整個對象完全交給物理引擎去計(jì)算碰撞、位置、約束等,很多情況下可以等價為對Ragdoll的模擬)
備注:物理一詞涉及的范圍非常廣,在游戲里面應(yīng)用的場景也很多,但是并不一定需要進(jìn)行網(wǎng)絡(luò)同步,比如簡單的拋物線運(yùn)動,射線檢測,與玩法無關(guān)的場景破碎等。
早在上世紀(jì)70年代,就誕生了許多圍繞物理特性產(chǎn)生玩法的游戲,不過由于當(dāng)時計(jì)算機(jī)系統(tǒng)算力有限,涉及到的物理計(jì)算都非常簡單(比如乒乓球游戲中小球的移動模擬[1])。隨著計(jì)算機(jī)性能的飛速提升,開發(fā)者們考慮將環(huán)境中的所有對象都交由統(tǒng)一的物理模塊驅(qū)動,由此慢慢的催生出了通用的物理引擎[2]。很快的,各個游戲開發(fā)商逐漸將物理引擎集成進(jìn)來,將更多更復(fù)雜的物理模擬過程應(yīng)用到游戲中,制作出了諸如極品飛車、FIFA、NBA、憤怒的小鳥等圍繞物理特性進(jìn)行玩法設(shè)計(jì)的游戲。另一方面,隨著計(jì)算機(jī)網(wǎng)絡(luò)的發(fā)展,游戲中的網(wǎng)絡(luò)同步技術(shù)愈加成熟,網(wǎng)絡(luò)游戲的品質(zhì)也不斷向單機(jī)游戲靠攏,我們也得以將傳統(tǒng)的單機(jī)游戲拓展成多人游戲。物理模擬作為提升游戲趣味性的一大技術(shù)也自然逐漸被納入其中,物理同步變得重要起來。

??游戲《Pong》
正如所前面解釋的那樣,物理同步并不是一種特殊的同步方式,而是在物理引擎和網(wǎng)絡(luò)同步技術(shù)共同發(fā)展的條件下而誕生的一種綜合行性解決方案,其核心手段還然是我們熟悉的幀同步或者狀態(tài)同步。使用幀同步技術(shù)我們需要每幀把玩家的Input信息發(fā)送出去,然后讓另一端的物理引擎根據(jù)輸入去模擬結(jié)果。如果使用狀態(tài)同步我們則需要本地模擬好數(shù)據(jù)并把物理位置、旋轉(zhuǎn)等關(guān)鍵信息發(fā)送到其他的客戶端,然后其他客戶端可以根據(jù)情況決定是否再執(zhí)行本地的物理模擬(如果是快照同步,由于拿到的就是最終的結(jié)果,那么就不需要本地再進(jìn)行模擬了)。
這樣看來,物理同步好像與常規(guī)的同步也沒什么本質(zhì)上的區(qū)別,那么為什么他卻是一個難題呢?我認(rèn)為原因有以下兩點(diǎn):
物理引擎的不確定性
在物理引擎參與模擬的條件下,網(wǎng)絡(luò)同步的微小誤差很容易被迅速放大
首先,我們談?wù)勎锢硪娴?/span>確定性問題。很不幸,目前所有的物理引擎嚴(yán)格來說都不是確定性的,因?yàn)橄氡WC不同平臺、編譯器、操作系統(tǒng)、編譯版本的指令順序以及浮點(diǎn)數(shù)精度完全一致幾乎是不可能的。關(guān)于物理確定性的討論有很多[3],核心問題大致可以歸類為以下幾點(diǎn):
1.編譯器優(yōu)化后的指令順序
2.約束計(jì)算的順序
3.不同版本、不同平臺浮點(diǎn)數(shù)精度問題[4][5]
(問題1與問題3其實(shí)是密切相關(guān)的)
這里摘選一段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(注:不同平臺、編譯器、優(yōu)化版本都會影響確定性). 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.
如果游戲只是單個平臺上發(fā)行,市面上常見的物理引擎(Havok,PhysX,Bullet)基本上都可以保證結(jié)果的一致性。因?yàn)槲覀兛梢酝ㄟ^使用同一個編譯好的二進(jìn)制文件、在完全相同的操作系統(tǒng)上運(yùn)行來保證指令順序并解決浮點(diǎn)數(shù)精度問題,同時打開引擎的確定性開關(guān)來保證約束的計(jì)算順序(不過會影響性能),這也是很多測試者在使用Unity等商業(yè)引擎時發(fā)現(xiàn)物理同步可以完美進(jìn)行的原因。當(dāng)然,這并不是說我們就完全放棄了跨平臺確定性的目標(biāo),比如Unity新推出的DOTS架構(gòu)[7][8]正在嘗試解決這個問題(雖然注釋里面仍然鮮明的寫著“Reserved for future”)。

??常見物理引擎
考慮到物理引擎的確定性問題,我們可以得出一個初步的結(jié)論——完全使用幀同步做物理同步是不合適的(或者說做跨平臺游戲是行不通的)。而對于狀態(tài)同步,我們可以定時地去糾正位置信息來避免誤差被放大。如果一定要使用幀同步去做跨平臺同步,那么只能選擇放棄物理引擎自己模擬或者用定點(diǎn)數(shù)來改造物理引擎,這可能是得不償失的。
下面不妨先排除掉一致性的問題,來看看如何實(shí)現(xiàn)所謂的“物理同步”。實(shí)際上,無論是優(yōu)化手段還是實(shí)現(xiàn)方式與前兩篇提到的方案是幾乎一致的,幀同步、快照同步、狀態(tài)同步都可以采用,增量壓縮、Inputbuffer等優(yōu)化手段也一樣可以用于物理同步的開發(fā)中。Network Next的創(chuàng)始人Glenn Fiedler在2014年撰寫了一系列的物理同步相關(guān)的文章[9],使用一個同步的Demo非常詳細(xì)地闡述了同步技術(shù)是如何應(yīng)用以及優(yōu)化的。涉及到的技術(shù)點(diǎn)大致如下,涵蓋了網(wǎng)絡(luò)同步的大部分的知識細(xì)節(jié):
如何確保物理引擎的確定性
如何實(shí)現(xiàn)物理幀同步
Inputbuffer如何改善幀同步
為什么用UDP替代TCP
如何實(shí)現(xiàn)快照同步
怎樣用插值解決網(wǎng)絡(luò)抖動
如何通過快照壓縮減少網(wǎng)絡(luò)流量
如何實(shí)現(xiàn)增量壓縮
如何實(shí)現(xiàn)狀態(tài)同步
另外,在2018年的GDC上,Glenn也對物理同步進(jìn)行一次演講分享[10],具體的細(xì)節(jié)建議大家移步到Glenn Fiedler的網(wǎng)站以及GitHub[11]去看。

??Glenn Fiedler的物理同步Demo
接下來,我們再來談?wù)劦诙€難點(diǎn),即網(wǎng)絡(luò)同步的誤差是如何被物理模擬迅速放大的(尤其在多人交互的游戲中)。我們在前面的章節(jié)里也談過,為了保證本地客戶端的快速響應(yīng),通常會采取預(yù)測回滾的機(jī)制(Client prediction,即本地客戶端立刻相應(yīng)玩家操作,服務(wù)器后續(xù)校驗(yàn)決定是否合法)。這樣我們就犧牲了事件順序的嚴(yán)格一致來換取主控端玩家及時響應(yīng)的體驗(yàn),在一般角色的非物理移動同步時,預(yù)測以及回滾都是相對容易的,延遲比較小的情況位置的誤差也可以幾乎忽略。然而在物理模擬參與的時候,情況就會變得復(fù)雜起來。
主控(Autonomous/Master)以及模擬(Simulate/Replica)都是針對某個對象而言的。
假如有兩個客戶端,玩家A控制小車1,玩家B控制小車2。小車1在玩家A的客戶端上就是主控的,小車2在玩家A的客戶端上就是模擬的。同理,小車2在B客戶端上就是主控的,小車1在B客戶端上就是模擬的。
假如在一個游戲中(帶有預(yù)測,也就是你本地的對象一定快于遠(yuǎn)端)你和其他玩家分別控制一個物理模擬的小車朝向?qū)Ψ經(jīng)_去,他們相互之間可能發(fā)生碰撞而彼此影響運(yùn)動狀態(tài),就會面臨下面的問題。
1.由于網(wǎng)絡(luò)同步的誤差無法避免,那么你客戶端上的發(fā)生碰撞的位置一定與其他客戶端的不同。

??本地客戶端的模擬小車(對手小車)一定落后其控制端
2.其次,對于本地上的其他模擬小車,要考慮是否在碰撞時完全開啟物理模擬(Ragdoll)。如果不開啟物理,那么模擬小車就會完全按照其主控端同步的位置進(jìn)行移動,即使已經(jīng)在本地發(fā)生了碰撞他可能還是會向前移動。如果開啟碰撞,兩個客戶端的發(fā)生碰撞的位置會完全不同。無論是哪種情況,網(wǎng)絡(luò)同步的誤差都會在物理引擎的“加持”下迅速被放大進(jìn)而導(dǎo)致兩端的結(jié)果相差甚遠(yuǎn)。

??不開啟物理模擬條件下,模擬端不會在碰撞時立刻停下
其實(shí)對于一般角色的非物理移動同步,二者只要相撞就會迅速停止移動,即使發(fā)生穿透只要做簡單的位置“回滾”即可。然而在物理模擬參與的時候,直接作位置回滾的效果會顯得非常突兀并出現(xiàn)很強(qiáng)的拉扯感,因?yàn)槲覀儙缀鯖]辦法在本地準(zhǔn)確的預(yù)測一個對象的物理模擬路徑。如果你仔細(xì)閱讀了前面Glenn Fiedler的文章(或者上面總結(jié)的技術(shù)點(diǎn)),你會發(fā)現(xiàn)里面并沒有提到常見的預(yù)測回滾技術(shù),因?yàn)樗挥幸粋€主控端和一個用于觀察結(jié)果的模擬端,并不需要回滾。
在2017年的GDC上,來自育碧的技術(shù)負(fù)責(zé)人Matt Delbosc就《看門狗2》中的載具同步進(jìn)行了演講[12],詳細(xì)的分析了多個主控端控制不同對象發(fā)生碰撞時應(yīng)該如何處理。
《看門狗2》的網(wǎng)絡(luò)模型是基于狀態(tài)同步的P2P,主控角色預(yù)測先行而模擬對象會根據(jù)快照(snapshot,即模擬對象在其主控端的真實(shí)位置)使用Projective Velocity Blengding做內(nèi)插值,他們在制作時也面臨和上面描述一樣的問題。假如兩個客戶端各控制一個小車撞向?qū)Ψ?,由于延遲問題,敵人在本地的位置一定是落后其主控端的。那么就可能發(fā)生你開車去撞他時,你本地撞到了他的車尾,而他的客戶端什么都沒有發(fā)生。

??收到快照再進(jìn)行內(nèi)插值,會有延遲造成兩邊不一致
所以,首先要做的就是盡量減少不同客戶端由于延遲造成的位置偏差,Matt Delbosc引入了一個TimeOffset的概念,根據(jù)當(dāng)前時間與TimeOffset的差值來決定對模擬對象做內(nèi)插值還是外插值,有了合適的外插值后本地的模擬對象就可以做到盡量靠近敵方的真實(shí)位置。

??使用外插值減少延遲
而關(guān)于碰撞后位置的誤差問題,他們采用了Physics Simulation Blending技術(shù),即發(fā)生碰撞前開啟模擬對象的RigidBody并設(shè)置位置權(quán)重為1(快照位置的權(quán)重為0),然后在碰撞發(fā)生后的一小段時間內(nèi),不斷減小物理模擬的權(quán)重增大快照位置的權(quán)重使模擬對象的運(yùn)動狀態(tài)逐漸趨于與其主控端,最終消除不一致性,騰訊的吃雞手游就采用了相似的解決方案[13]。

??根據(jù)碰撞時間調(diào)整權(quán)重
不過實(shí)際上, Matt團(tuán)隊(duì)遇到的問題遠(yuǎn)不止這些,還有諸如如何用插值解決旋轉(zhuǎn)抖動問題,人物與載具相撞時不同步怎么辦等等,知乎上有一篇譯文可以參考[14]。
可能有些朋友會問,如果我不使用預(yù)測回滾技術(shù)是不是就沒有這個問題呢?答案依然是否定的,假如你在運(yùn)行一個車輛的中間突然變向,而這個操作被丟包或延遲,只要服務(wù)器不暫停整個游戲來等待你的消息,那么你本地的結(jié)果依然與其他客戶端不同進(jìn)而產(chǎn)生誤差。也就是說除非你使用最最原始的“完全幀同步”(即客戶端每次行動都要等到其他客戶端的消息全部就緒才行),否則由于網(wǎng)絡(luò)同步的延遲無法避免,誤差也必定會被物理模擬所放大。
同樣在2017年,另一款風(fēng)靡全球的競技游戲——《火箭聯(lián)盟》悄然上線,可謂是將物理玩法發(fā)揮到了極致。次年,《火箭聯(lián)盟》的開發(fā)者Jared Cone也來到了GDC,分享了他們團(tuán)隊(duì)是如何解決物理同步問題的[14]。
《火箭聯(lián)盟》的核心玩法是“用車踢球”,每個玩家控制一個汽車,通過撞擊足球來將其“踢”進(jìn)敵方的球門。由于是多人競技游戲,所以一定要有一個權(quán)威服務(wù)器來避免作弊,最終的結(jié)果必須由服務(wù)器來決定。相比于《看門狗》,他們遇到的情況明顯更復(fù)雜,除了不同玩家控制不同的小車,還有一個完全由服務(wù)器操控的小球。按照常規(guī)的同步方式,本地的主控玩家預(yù)測先行,其他角色的數(shù)據(jù)由服務(wù)器同步下發(fā)做插值模擬。但是在這樣一個延遲敏感且?guī)в形锢砟M的競技游戲中,玩家的Input信息的丟失、本地對象與服務(wù)器的位置不統(tǒng)一都會頻繁的帶來表現(xiàn)不一致的問題,而且FPS中常見的延遲補(bǔ)償策略并不適合當(dāng)前的游戲類型(簡單來說就是延遲大的玩家會影響其他玩家的體驗(yàn),具體原因我們在上一篇延遲補(bǔ)償?shù)恼鹿?jié)也有討論)。

??客戶端有延遲,導(dǎo)致服務(wù)器實(shí)際位置不同
為了解決這些問題,Jared Cone團(tuán)隊(duì)采用了“InputBuffer”以及“客戶端全預(yù)測”兩個核心方案。InputBuffer,即服務(wù)器緩存客戶端的Input信息,然后定時的去buffer里面獲?。╞uffer大小可以動態(tài)調(diào)整),這樣可以減少網(wǎng)絡(luò)延遲和抖動帶來的卡頓問題。

??Inputbuffer的作用
客戶端全預(yù)測,即客戶端上所有可能產(chǎn)生移動的對象(不僅僅是主控對象)全部會在本地預(yù)測先行,這樣本地在預(yù)測成功時所有對象的位置都是準(zhǔn)確的,客戶端與服務(wù)器的表現(xiàn)也會高度一致,當(dāng)然預(yù)測失敗的時候自然會也要處理位置回滾。

??采用全預(yù)測后的效果
仔細(xì)分析這兩款游戲,你會發(fā)現(xiàn)他們采用都是“狀態(tài)同步+插值+預(yù)測回滾”的基本框架,這也是目前業(yè)內(nèi)上比較合適的物理同步方案。
除了同步問題,物理引擎本身對系統(tǒng)資源(CPU/GPU)的消耗也很大。比如在UE4引擎里面,玩家每一幀的移動都會觸發(fā)物理引擎的射線檢測來判斷位置是否合法,一旦場景內(nèi)的角色數(shù)量增多,物理引擎的計(jì)算量也會隨之增大,進(jìn)而改變Tick的步長,幀率降低。而幀率降低除了導(dǎo)致卡頓問題外,還會進(jìn)一步影響到物理模擬,造成更嚴(yán)重的結(jié)果不一致、模型穿透等問題,所以我們需要盡量減少不必要的物理模擬并適當(dāng)簡化我們的計(jì)算模型。
- 未完待續(xù) -
- 原創(chuàng)不易,希望大家能點(diǎn)贊、分享支持一下 -
參考資料:[1] WIKI, "Pong", WIKI, 2020.Available:https://en.wikipedia.org/wiki/Pong[Accessed:2020-12-12][2] Tony Wang, "游戲物理模擬簡史", 知乎, 2020.Available:https://zhuanlan.zhihu.com/p/106977617[Accessed:2020-12-12][3] Theraot, "How can I perform a deterministic physics simulation?",Gamedev Stackexchange, 2019.https://gamedev.stackexchange.com/questions/174320/how-can-i-perform-a-deterministic-physics-simulation[Accessed:2020-12-12][4] Yossi Kreinin, "Consistency: how to defeat the purpose of IEEE floating point", Personal Blog , 2008. Available:http://yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html[Accessed:2020-12-12][5] Glenn Fiedler, "Floating Point Determinism", Personal Blog , 2010. Available:https://gafferongames.com/post/floating_point_determinism/[Accessed:2020-12-12][6] NVIDIA, "NVIDIA PhysX SDK 3.4.0 Documentation Determinism", NVIDIA , 2020. Available:https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/BestPractices.html[Accessed:2020-12-12][7]MelvMay, "How much deterministic is Physics from Unity3d In 2019?",Unity Forum,2020.Available:https://forum.unity.com/threads/how-much-deterministic-is-physics-from-unity3d-in-2019.711311/[Accessed:2020-12-12][8]Unity, "Burst User Guide",Unity Manual, 2020.Available:https://docs.unity3d.com/Packages/[email protected]/manual/index.html?_ga=2.60059693.1096806956.1607653832-2097754989.1600740353[Accessed:2020-12-12][9] Glenn Fiedler, "Introduction to Networked Physics", Personal Blog,2014. Available: https://gafferongames.com/post/introduction_to_networked_physics/[Accessed:2020-12-12][10]Glenn Fiedler,"Physics for Game Programmers : Networking for Physics Programmers", 2018.Available:https://www.gdcvault.com/play/1022195/Physics-for-Game-Programmers-Networking[Accessed:2020-12-12][11]Glenn Fiedler,"UnityDemo: Networked Physics in Virtual Reality: Networking a stack of cubes with Unity and PhysX" , 2018. Available:https://github.com/fbsamples/oculus-networked-physics-sample/[Accessed:2020-12-12][12] Matt Delbosc, "Replicating Chaos Vehicle Replication in Watch Dogs 2", GDC, 2017.? Available:https://www.bilibili.com/video/BV1KA41187jk[Accessed:2020-12-12][13]Ned,"手游中載具物理同步的實(shí)現(xiàn)方案", 騰訊游戲?qū)W院, 2018.? Available:https://gameinstitute.qq.com/knowledge/100044[Accessed:2020-12-12][14]Funny David, "看門狗2的載具同步(翻譯)", 知乎, 2019. Available:https://zhuanlan.zhihu.com/p/95560180[Accessed:2020-12-12][15]Jared Cone, "It IS Rocket Science! The Physics of 'Rocket League' Detailed", GDC, 2018. Available:https://www.bilibili.com/video/av44416219[Accessed:2020-12-12]
?往期文章推薦?

虛幻引擎技術(shù)系列【使用虛幻引擎4年,我想再談?wù)勊木W(wǎng)絡(luò)架構(gòu)】

游戲科普系列【盤點(diǎn)游戲中那些“欺騙玩家眼睛的開發(fā)技巧”】

C++面試系列【史上最全的C++/游戲開發(fā)面試經(jīng)驗(yàn)總結(jié)】

校招求職系列

游戲開發(fā)技術(shù)系列【想做游戲開發(fā),我應(yīng)該會點(diǎn)啥?】
游戲開發(fā)那些事
我是Jerish,網(wǎng)易游戲客戶端高級開發(fā)工程師,
這里會定期輸出技術(shù)干貨和游戲科普的文章
回復(fù)"gamebook",獲取游戲開發(fā)書籍
回復(fù)"C++面試",獲取C++/游戲面試經(jīng)驗(yàn)
回復(fù)"操作系統(tǒng)",獲取操作系統(tǒng)經(jīng)典書籍
游戲開發(fā)交流群(875867499)
