從單機游戲到網絡游戲
單機游戲,這里指即時的動作類游戲,玩家輸入操作,通過終端運算而進行的游戲。加入了多人網絡以后,玩家的輸入不僅僅只是在本地的終端上運算,還會通過網絡同步,使多人可以在同一個虛擬環境中同時游戲。由此,網絡多人快節奏的動作游戲帶來了新的問題:一致性,響應性,帶寬,延遲。網絡游戲的實時PVP就是為了平衡這四點的要素。
幀同步的引入
幀同步應該是引入多人網絡以后,能想到最直接 的同步方式,在理想狀態下,是合適的解決方案。而現實是,幀同步需要發送大量的幀數據來驅動游戲邏輯,需要客戶端能在一段時間保持網絡穩定(低延遲,少量的網絡抖動)。此外,還有狀態同步技術,幀同步是把玩家的動作直接同步到其他玩家的終端上,通過確定性的運算達到同步效果,而狀態同步是所有客戶端的動作發送到服務器,由服務器計算并把最終狀態廣播下發。網上有大量的資料對這兩種技術進行對比,下面只對幀同步的技術做講述。
幀同步的原理
了解幀同步技術,不妨可以參考下DOOM/QUAKE I/II/III 網絡模型的演化,約翰·卡馬克為FPS的網絡同步寫下了原型,后面的版本又在這基礎上做出了眾多改良版本。
幀同步技術最重要的基礎概念:
相同的輸入+相同的時機=相同的顯示
意思是每個客戶端接受的輸入是相同的,執行的邏輯幀也是一樣的,那么結果也是同步一致的。為了跟每個機器執行的快慢無關,每個邏輯幀為固定幀數固定時長,游戲當中的實體都是按照這種設定運算,因此移動、碰撞等都能算出相同的結果。而渲染幀(一般為30到60幀),則是根據邏輯幀(10到20幀)去插值,得到一個“平滑”的展示。邏輯幀實際是是由一個個定時下發的“網絡幀”來驅動,而渲染幀則由本地CPU的update驅動,渲染幀只是邏輯幀無限逼近(插值),如果邏輯幀突然中斷,則游戲就會卡在那一幀狀態,這就是lockstep的由來。
為了保證游戲同步執行的一致性,代碼必須按照lockstep的方式組織運算,不依賴本地客戶端的幀率,時間或者隨機數。理想狀態下,每個“網絡幀”被及時接收,客戶端渲染幀都能滿幀運算,游戲就像播放電影一樣。但在網絡游戲中,各個客戶端的硬件和網絡情況都不一樣,可能會導致客戶端收到“過去時間”里的一堆網絡幀,因此,必須要有處理這些堆積起來的網絡數據的能力。最簡單的做法是加速播放(快進),根據堆積的量計算出加速比率,以此快速地執行邏輯幀,盡快地追上最新的實時幀。同時,在加速的過程中,可以考慮丟棄用戶的操作,因為玩家看到的是“過去”的狀態,此時進行控制打擊是沒有意義的。另外一種處理方式是,直接運算直至追上最新的網絡幀,這樣會直接閃現到“最新”的狀態,玩家可以馬上操作。比較建議采用快進的手段,這種方式可以讓玩家感受到相對自然的游戲畫面。這一基礎特性也用于支持后面的斷線重連。
幀同步的響應性
對于實時PVP的游戲來說,手感流暢的角色控制體驗很重要。玩家的輸入往往在幾十分之一秒內,就開始顯示變化,而在幀同步中,玩家的輸入發送到網絡,下一個網絡幀操作回來時盡快處理并顯示 ,當網絡不穩定時,常常會時快時慢,非常難以預測輸入動作后,角色會在什么時候起反應。要解決這個問題,可以參考下傳輸語音業務的做法,在接收網絡數據時,不立刻處理,而是給所有的操作增加一個固定的延遲,在延遲的時間內盡可能收集更多的網絡包,然后按固定的時間去播放(運算)。這種做法相當于建立了一個網絡緩沖區,平滑了因為網絡抖動而時快時慢的數據包。這里添加的固定延遲可以按照玩家所在的網絡延遲來設置,可以動態地取一個連續平穩(避免抖動)的值,可以使用累計或抽樣的加權平均來獲取延遲。玩家發出的操作只要在固定延遲內接收,游戲就可以流暢運行,網絡的抖動已經被間隔相等的邏輯幀抹平了。
TCP還是UDP,這是一個問題
保證了邏輯運算的一致性以及邏輯幀的平滑加速,基本上可以動手把一個單機游戲改成多人聯機游戲了,在局域網運行勉強還能接受,可惜,我們做的是互聯網上的游戲。接下來了解一下移動網絡的延遲情況。首先是TCP,保證了包序,丟包自動重傳,看起來就是我們想要的拼圖,并且已經有人幫我們實現了,而且還幫老板省了一大筆錢。誠然,可靠的傳輸協議非常誘人,并且也有不少成功的例子,且再權衡下缺點再做決定。TCP是基于重傳來保證可靠性,如果IP包丟包,TCP協議需要等待至少2個往返時延才會重新發送這個數據包,丟包嚴重甚至會斷線,一旦斷線,則觸發斷線重連流程。看看下面一組數據。
這是騰訊一款以忍者格斗為題材的ACT手游給出來的數據,可以看到在各種網絡情形下,UDP的表現(延遲分布)基本上都優于TCP。
那么到UDP,非面向連接的傳輸協議,沒有自動重傳,沒有擁塞控制,不能保證包序,甚至不能保證可到達,只保證了數據報完整的基礎特性。優點是延遲小、數據傳輸效率高、資源開銷小,如果用來作為網絡游戲的傳輸方案,需要在應用層定制更多適用于網游的特性。在UDP基礎上定制一個應用層的協議,難度比較大?;赨DP也有一個通用的解決方案UDT,保證了可靠性和包序,但是跟TCP類似的,UDT也是基于超時重傳的方式保證可靠。下面我想把一些專用定制的方案拿出來討論。
首先,幀同步需要每幀廣播數據,廣播的頻率非常高,這要求每次廣播的數據要足夠小,最好每個網絡幀在一個MTU以下,這樣可以避免在IP層分片,降低延遲,互聯網的MTU標準為576字節,有效載荷長度控制在(576-8-20)548字節以內。為了盡量避免重傳,游戲里面可以用冗余的方式——每個幀數據包實際包含了過去2幀的數據,也就是每次發3幀的數據來對抗丟包。三個包里面只要有一個包沒丟,就不影響游戲。另外,定制的方案還需要有一個請求“下載”丟失幀的特性,以防止連續3個包全丟的情況。對于“下載”特性,則可以考慮使用TCP。這是全冗余的做法,缺點是會導致流量增加2倍。還有一種動態冗余算法,根據客戶端的丟包狀況動態調整冗余倍數,上面介紹的那款ACT游戲就是用了這種方法,本質上還是用流量換速度。
接發包速率對一款PVP競技型的商業游戲來說至關重要,目前還只是學習到皮毛,以后深入了解后再補充。除此之外,后續還需要服務器介入,解決斷線重連和反作弊等問題,先寫到這里。