天天看點

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

轉自:http://www.gameres.com/478430.html

本帖最後由 小籬 于 2015-12-22 16:08 編輯

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

  GameRes遊資網授權釋出 文 /  韋易笑

  實時動作遊戲在近年來得到迅猛的發展。而遊戲同步問題,成為大家繼續解決的核心問題之一。早在 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年開發的東西來說的,當時國内公網品質比國外差很多:

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

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

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

  上圖為 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遊戲。

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

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

   狀态同步法

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

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

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

  而 FPS和 賽車類遊戲的同步性要求比 RPG高很多,每秒發包量也會多很多(10-30個),多半采用位置預測及坐标內插補點的“導航推測算法(DR)”,具體實作見我的:“ 影子跟随算法 ”(DR算法的一個改進實作)。

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

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

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

再談網遊同步技術:實時動作遊戲同步方式和傳輸協定選擇

  而狀态同步又分為“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年就開始弄同步相關的問題,期間也指導過不少遊戲設計他們的同步方案,是以這次相當于将以前的觀點做一個總結和點評,根據自己的遊戲類型選擇最适合的同步算法,為玩家提供更好的體驗才是關鍵。

   相關閱讀:

   手機格鬥網遊該如何避免延遲?

   影子跟随算法:FPS遊戲中遊戲同步性的實作