文章來自http://www.gameres.com/478430.html
實時動作游戲在近年來得到迅猛的發展。而游戲同步問題,成為大家繼續解決的核心問題之一。早在 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年開發的東西來說的,當時國內公網質量比國外差很多:
上圖為 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年就開始弄同步相關的問題,期間也指導過不少游戲設計他們的同步方案,所以這次相當于將以前的觀點做一個總結和點評,根據自己的游戲類型選擇最適合的同步算法,為玩家提供更好的體驗才是關鍵。