再談網遊同步技術

實時動作遊戲在近年來得到迅猛的發展。而遊戲同步問題,成爲大家繼續解決的核心問題之一。早在 2004年,國內遊戲開發還處於慢節奏 RPG滿天飛的情況下,我就開始實時動作遊戲研究,分別在 2005-2006期間寫了一系列相關文章,被好多網站轉載:

幀間同步模式:《幀鎖定同步算法》(2007): http://www.skywind.me/blog/archives/131 

玩法規避模式:《網絡遊戲同步法則》(2005): http://www.skywind.me/blog/archives/112

預測插值模式:《影子跟隨算法》(2007): http://www.skywind.me/blog/archives/1145

如今十年過去,網上越來越多的人開始討論遊戲同步技術了,然而很多文章往往只針對某種特定的遊戲情況,而觀點又經常以偏概全。很多人並沒有真正開發過實時動作遊戲,更別說瞭解同步技術的前世今生了。轉載別人的觀點並加上自己理解的人很多,實際動過手的人很少。避免給更多人造成無謂的誤導,我今天基於先前的實踐和對歐美動作遊戲,戰網遊戲,主機遊戲(PSN,XBox Live等)網絡技術的瞭解,來對這個問題做一個簡單總結:

網速的變化

開發快速動作遊戲,首先要對公網的網絡質量數據有詳細的瞭解。這裏所說到的網速,是指 RTT,數據往返一週的毫秒時間,而非每秒傳送多少 KB/s。我寫這篇文章是基於我 2005-2006年開發的東西來說的,當時國內公網質量比國外差很多:

clip_image003

上圖爲 2005-2006年國內的網絡環境,某三個省級 IDC的情況採樣。當時公網 RTT平均值基本在100ms,120ms左右徘徊。所以我文中引用了很多 100ms。這個情況在2009 年以後已經好了很多(60ms的rtt)。到了2012年以後,公網平均 RTT已經降低到平均 40ms-50ms,省內平均10ms以內了:

image

上圖爲 2015年某省級 IDC的全國延遲情況,如若全國多布點以及區別電信聯通的話,平均延遲能控制在20ms以內,延遲基本接近國外水平(當然帶寬還差很多),比我當年文章中提到的網絡情況好了不少。

幀間同步法

關於幀間同步的“幀鎖定算法”系列的方法有很多類似實現(包括後面提到的幀間無等待改進,包括 LockStep等),但是他們的核心都是一個:保證所有客戶端每幀的輸入都一樣。這樣的方式被格鬥遊戲,RTS和足球(FIFA類)、籃球(NBA)等體育和動作遊戲大量使用,比如我們熟悉的各大戰網平臺遊戲(Xbox Live等),還有很多基於模擬器的街機對戰平臺。以及不少大型多人橫版動作遊戲。以開發便利,同步邏輯直觀而受到大家歡迎。

幀鎖定算法多用在 C/S模型中(或者一人做主多人做從的P2P裏),它和 LockStep(多用於P2P)共同存在的問題就是 “網速慢的玩家會卡到網速快的玩家”,老式遊戲經常一個角色斷網,所有人就在那裏等待。爲此出現了幀鎖定的改良版本 “樂觀幀鎖定”(具體描述見幀鎖定文章的下半部分)經過了不少遊戲的實踐檢驗。先前還有幾款上線的橫版格鬥頁遊(如熟知的街機三國)用 Flash 的 TCP without NODELAY 來每秒20個關鍵幀的模式(特意找該遊戲開發者確認了一下)跑該算法(由於近兩年國內網速提高,Flash的 Tcp without NODELAY也能做很多事情了),效果還不錯。

具體實施時用不着按照文所述每一個步奏都相同,可以有很多變通。比如不一定是有變化的時候才通知服務端,有線上某橫版格鬥頁遊就是也可以每秒 20次向服務端直接發送數據(flash時鐘不準需要自己獨立計時),服務端再每秒 40次更新回所有客戶端,看具體情況而定。

也有使用 UDP的端遊,客戶端每秒鐘上傳50次鍵盤信息到服務端,丟了就丟了,後面持續發送過來的鍵盤數據會覆蓋前面的數據,所以丟了沒關係,更快捷。當然,UDP也不是必須的,近兩年網速提高很快,省內都能做到10ms的 RTT 了,跨省也就 50ms的rtt,不少頁游上用該方法上裸的 TCP 照樣跑的很順暢。

而近兩年國外動作遊戲領域也涌現出其他一些新的改良方法,比如 Time Warp,以客戶端先行+邏輯不一致時回滾的方式,帶來了更好的同步效果,俗稱時間回退法。不果國內暫時沒看到有遊戲這麼嘗試,更多的是國外近兩年的雙人動作遊戲比較多,要求遊戲每幀狀態都可以保存,邏輯上開發會複雜一些。國內大部分是超過兩人出去副本的,在3-4人出去 PK的情況下,引入狀態回退,會讓整個效果大打折扣。不過2人的效果確實有所改進,有興趣的同學可以搜索 Time Warp相關的論文。

2009年,雲遊戲(遊戲遠程渲染)技術得到廣泛應用,客戶端上傳操作,服務端遠程渲染,並以低延遲視頻編碼流的方式傳回給客戶端,用的就是這樣類似的技術。客戶端不需要高額的硬件,也不存在盜版問題,其中 Gaikai和 OnLive兩家公司做的比較好。

2012年,Sony推出 Playstation Now技術,可以在 PSV和 PS3/PS4上玩雲遊戲,玩家不需要購買遊戲就可以免費體驗一定時間。使得 PSV/PS3等低端硬件也可以流暢的跑 PS4遊戲。

image

但是目前國外網絡環境下跑的還比較流暢,國內的網絡環境要低延遲傳送 HD畫質的視頻流還比較困難,視頻都是比較費帶寬的。但是幀鎖定等保證每幀輸入一致的算法,在當今的網絡質量下傳遞一下玩家操作,還是沒有任何問題的。

狀態同步法

對於邏輯不需要精確到幀的遊戲類型而言(RPG/ARPG,FPS,賽車),允許每個客戶端屏幕上顯示的內容不同,只要將他們統一到一個邏輯中即可,這部分見:“網絡遊戲同步法則”(最好給策劃看看這篇,從玩法上規避)。如果是 RPG遊戲,其實更多是使用障眼法從玩法和動畫效果上減少 “一次性的”,“決定性”的事件即可:

RPG 遊戲的移動很簡單,只需要“誰在哪裏朝着哪裏移動”,客戶端再做一些簡單的平滑處理即可,不需要額外的“時間”參數。比如《魔獸世界》移動時,就是差不多每秒發送一次(座標,朝向,速度),別的客戶端收到以後就會矯正一下,如果矯正錯誤,比如 A本來往北走突然拐彎向東,這個數據包傳到B上,B屏幕上的A可能在拐彎前往北跑了更遠,致使拐彎向東時被樹卡住,那麼B就會看到A被樹卡了兩秒無法移動,然後突然瞬間移動到新的座標,繼續朝着東跑。

通常 RPG攻擊分爲“有鎖定攻擊”和“無鎖定攻擊”,有鎖定攻擊意思是,我朝你發射火球,不管你怎麼跑,火球都會追蹤並射擊到你,比如你在我面前橫着跑過,我向你發射火球,可以發現火球並不是直線飛行,而是曲線追蹤着你就過去了,這叫有鎖定攻擊。無鎖定攻擊一般是範圍攻擊,先播放個動畫(比如揮刀),然後將攻擊請求提交服務器,服務器結果回來時,動畫剛好播放完畢,然後大家一起減血。

而 FPS和 賽車類遊戲的同步性要求比 RPG高很多,每秒發包量也會多很多(10-30個),多半採用位置預測及座標差值的“導航推測算法(DR)”,具體實現見我的:“影子跟隨算法”(DR算法的一個改進實現)。

image

這類算法由於位置判定更爲精確,所以計算量大,很多沒法服務端判斷,而是客戶端直接判斷,比如 FPS射擊是否打到別人,客戶端先判斷,除了狙擊這種一槍斃命的射擊外基本都是客戶端判斷的。由於計算更爲複雜,每秒同步發包差不多到 30個以上,這樣的模式下,每局遊戲的人數也不可能很多,一般16人左右。而且很多才用 P2P的方式運行,具體 FPS遊戲的實現,及 DR算法的代碼編寫,見 “影子跟隨算法”這篇文章。

其實狀態同步是一種樂觀的同步方法,認爲大家屏幕上的東西不同沒關係,只要每次操作的結果相同即可,不需要象“幀間同步”那樣保證每幀都一樣,因此,對網速的要求也沒有 “幀間同步”系列算法那麼苛刻,一般100ms-200ms都是能夠接受的(DiabloIII裏面300ms的延遲照樣打),偶爾網絡抖一下,出現1秒的延遲,也能掩蓋過去。然而比起 “幀間同步”,狀態同步方式對玩法有不少要求,諸如 “一次性”,“決定性”的事件要少很多,而且代碼編寫會複雜一些,不果由於能容忍更壞的網絡情況,以及容納更多同時遊戲的人數,在一些玩法確定的遊戲中(RPG,FPS,賽車),被廣泛使用。

image

而狀態同步又分爲“DR同步”和“非DR同步”,前者針對 FPS,賽車或者更激烈點的 ARPG,後者針對 RPG和普通 ARPG。他們對網速的要求和錯誤的容忍度也是不一樣,當然,帶來的遊戲即時感也是不同的。

總得來說,你希望遊戲體驗更爽快,即時感更強,那麼你每秒發包數就越多,每局(副本)支持的人數越少;而你如果追求對網絡的容忍,想降低發包數,並且增加同時遊戲的人數,那麼相應的就需要以降低即時感爲代價,其二者不可得兼。然而聰明的策劃和程序們總能想出很多好主意,利用障眼法和玩法規避,動作掩蓋等方法,在相同的情況下來掩蓋延遲,讓玩家“看起來”更加“即時”和“爽快”,而這個方法具體該怎麼做,並沒有統一的做法,就得大家結合自己的遊戲和玩法,發揮自己的聰明才智了。

 

結果同步法

結果同步往往比較簡單,位置即使全部錯亂或者延遲很久都沒有關係,因爲遊戲過程完全不在乎位置,只在乎最後的結果,比如《夢幻西遊》這樣的“回合制 RPG” 遊戲,屏幕上的人走到哪裏確實無所謂,所有操作都是要點擊或者選擇菜單來下命令,象這樣的遊戲背後其實是文字遊戲,只是加了一個圖形的殼。

遊戲表面上看起來是動作/RTS 遊戲,但是沒有玩家直接協作和對抗,都是單機遊戲,並不需要同步什麼東西,服務端只要監測下結果不離譜即可,延遲檢測都沒關係。基本是 PVE,而且無協作。即使是 PVP也就是打一下別人的離線數據,和無同步回合制遊戲並無本質上的區別。

 

傳輸協議選擇

老話題 TCP還是 UDP,答案是大部分時候,TCP打開 NODELAY即可,現在網絡情況好了很多,沒必要引入新的複雜度。即便是“幀鎖定算法”上線的多人實時格鬥遊戲,也有在用 TCP跑着的。幀間同步如果能夠做到更好的架設機房,那麼延遲基本能控制在 10ms以內,將遊戲玩家按照區域分服務器,讓他們選擇更快的服務器。

即便是帶 DR的狀態同步,很多也都是 TCP的,《魔獸世界》和《暗黑破壞神3》都是基於 TCP來實現的,所以我的建議是,先上 TCP,把你的遊戲發佈出去。

當然,等到你的遊戲發佈出去了,開始掙錢了,你想改進你的遊戲效果,特別是高峯期的卡頓比例(需要收集客戶端統計),那麼你可以使用 UDP來改進,《街霸4》和《英雄聯盟》都是使用 UDP的,比如你可以使用 libenet(英雄聯盟用的)。不過 libenet所採用的傳輸技術,是上世紀的標準 ARQ做法了,《街霸4》所採用的傳輸技術遠遠高過 libenet,如果你想採用更爲現代的傳輸技術,贏得更低延遲的話,可以使用我的“快速傳輸協議-KCP”(http://www.skywind.me/blog/archives/1048),被再若干上線項目和開源項目使用的協議,效果遠遠 PK libenet。

在使用 KCP時,你可以用在你 TCP的基礎上,再登陸時服務端返回 UDP端口和密鑰,客戶端通過 TCP收到以後,向服務端的 UDP端口每隔一秒重複發送包含握手信息,直到服務端返回成功或者失敗。服務端通過 UDP傳上來的密鑰得知該客戶端 sockaddr對應的 TCP連接,這樣就建立 TCP連接到 UDP連接的映射關係。爲了保持連接和 NAT出口映射,客戶端一般需要每 60秒就發送一個 UDP心跳,服務端收到後回覆客戶端,再在這個 UDP連接的基礎上增加調用 KCP的邏輯,實現快速可靠傳輸,這樣一套 TCP/UDP兩用的傳輸系統就建立了。

中國的網絡情況比較特殊,會存在有些網絡 UDP連接不上的情況,因此都是先連接 TCP,然後試圖 UDP,UDP不通的情況下,退回 TCP也能正常遊戲,一旦 TCP斷開,則認爲 UDP也斷開了。

不果歸根結底,還是先上 TCP,再根據自己遊戲的特點和是否出現傳輸問題,選擇 UDP。

 

話題總結

根據遊戲類型,選擇恰當的同步方式和傳輸協議是最基礎的問題,很多講述網絡同步的文章一般就是隻會強調上述那麼多種算法的其中一種方式,好像使用該方式就可以 hold住所有遊戲一樣的,其實並非如此。技術需要多和策劃溝通,別策劃一個需求下來,技術就來一句:無法實現,這樣的遊戲永遠沒有競爭力。就像國內當時都是慢節奏 RPG,偶爾有點 ARPG的時候,大家覺得《DNF》這樣的遊戲無法實現,於是韓國實現了,在市場上取得了先機,國內才慢慢跟進,再一看,哇塞,好多坑呢。

然後開發者開始在網上尋找各種同步算法,東一榔頭西一棒子,明明是一款需要幀間同步的格鬥遊戲,結果卻上了導航推測,最後發現問題永遠解決不了,一堆 BUG這就叫誤導。正因爲我 2004年就開始弄同步相關的問題,期間也指導過不少遊戲設計他們的同步方案,所以這次相當於將以前的觀點做一個總結和點評,根據自己的遊戲類型選擇最適合的同步算法,爲玩家提供更好的體驗纔是關鍵。

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