MOBA游戲的網絡同步技術


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

在5月13日Unite 2017 案例分享專場上,藍港互動《鬧鬧天宮》項目組的主程序陳實分享了MOBA游戲的網絡同步技術經驗,以下為詳細內容:

  大家早上好,現在自我介紹一下,我叫陳實,來自藍港互動,現在是《鬧鬧天宮》項目組的主程序。我今天的主題是MOBA游戲的網絡同步技術。這個主題昨天王者榮耀跟今天《球球大作戰》都涉及到一些,我講的內容可能會更底層一些,更接觸我們實踐細節的部分。我不是在這里教會大家怎么做這個網絡同步的技術,而是想把我們在使用這些技術的時候,遇到的坑,還有我們認為的重點,就是跟大家分享一下,然后可以讓大家在使用同樣的技術方案的時候,可以有一些坑大家就繞過了,如果節約大家開發的時間或者調試的時間,我覺得這次分享的價值應該就體現出來了。

  我們講這個MOBA游戲里面的網絡同步技術,首先需要來了解一下,MOBA游戲這個同步的一個特點。我把它歸納為三點,首先要求動如脫兔,就是實時性要求比較高,然后要順滑如絲,就是流暢性要求比較高。還有一個就是疏而不漏,就是公平性也要有一定要求。但是允許你在一些顯示的部分,我們可以說兩邊有一些差別,這是可以接受的。但是涉及到核心的游戲玩法的部分必須保證是一樣的。

  接下來我會分三個方面跟大家闡述一下這個主題,首先會講一下我們在做這個網絡同步的時候,常用的兩種方案,狀態同步和幀同步,然后我會結合實際的案例來分析一下,哪些游戲用哪種方案,大家用對應的方案,可以分析一下對應的做的比較好的游戲,然后還會看一下,就是在網絡延遲的情況下兩種方案表現有哪些不一樣。最后會着重介紹一下我們項目在使用幀同步這個方案的時候,遇到的一些問題,然后里面的一些重點和難點。

  那么講一下狀態同步和幀同步。先說一下狀態同步,狀態同步顧名思義,就是同步玩家的狀態信息,比如位置,他的屬性,還有其他跟玩法相關的數據。通常使用這種方案主邏輯基本上都在服務器運行,都在服務器進行計算,客戶端只是作為一個顯示。還有一個特點,就是我們通訊的網絡流量大小是依賴於你的游戲里面,你的玩法里面,需要同步的一個實體數量。

  我們知道以前玩的比較多一些RTS游戲,像星際爭霸,就不太會使用這種狀態同步的方案,他們可以操縱兵的個體的數量是非常多,可能有上百個單位,如果這樣用狀態同步的話,流量就會非常大。我們看一下這個方案的結構圖,大家可以看到,客戶端會把一些,把他們的輸入發給服務器,然后服務器會做一個處理,把狀態信息發到客戶端做一個顯示。

  我們再介紹一下幀同步,幀同步不太一樣的地方就是,只同步玩家的指令,不同步狀態。通常游戲的邏輯都是在客戶端進行各自計算的。這肯定帶來一個問題,就是說你要在各客戶端各自計算,你怎么保證在各客戶端算出來的結果是一樣,這是幀同步的技術重點也是難點。然后就是網絡流量是不依賴於同步實體的個數,只依賴於指令,這么一個大小,還有一個同步的頻率。像星際爭霸這種游戲,即使需要同步的單位從一百個加到兩百個,加到三百個,對同步的網絡流量也不會有特別明顯的增加。

  我們再看一下這個幀同步的結構圖,大家看到,客戶端會把它們的消息跟它們的輸出發送給服務器,服務器會以一個統一的頻率把各個客戶端的輸入轉發給其他的客戶端,然后其他各個客戶端收到大家的輸入之后,統一地進行運算。

  下面我就比較一下,我們認為這幾個重點的方面,使用這兩種解決方案可能有哪些不一樣。我們來比較一下開發和調試難度,我們認為幀同步的難度是比狀態同步的開發調試難度會高一些。因為狀態同步的話,可能只需要有一份邏輯在服務器運行,只要寫好服務器的邏輯,保證正確就行了。幀同步因為各個客戶端去運算,很難說去保證一個一致性,很容易就會出現我們所謂的不同步的現象,就各個客戶端整個游戲的流程就分開了,兩個客戶端看到完全不一樣的游戲狀態。

  下面是一個安全性,我們認為狀態同步的安全性會高一些,因為所有邏輯都在服務器,因為幀同步邏輯是在客戶端運算,就存在一些hack,后面會降到一些外掛的問題,我們如果用這個幀同步方案的話,我們怎么避免它。

  還有一個是離線戰斗,我們知道在手機上的MOBA游戲,手機上的網絡,或者使用場景,網絡是不穩定的,可能我們希望為了體驗好一點,比如單排的時候,或者剛進來的時候第一場新手戰斗,我們希望跟AI在戰斗。跟AI打的時候,不需要去跟服務器進行通訊,保證剛開始的時候,體驗是非常好的。這個離線戰斗也是一個很強的需求,如果你使用的是狀態同步的這種方案,可能需要額外去開發,因為通常我們的Server和Client使用的開發語言是不一樣的,幀同步本身在客戶端算,只需要簡單轉發一下網絡的消息,就可以實現說這么一個離線戰斗的邏輯。

  然后消耗流量的大小,通常來講狀態同步比較高,原因也說過了,因為可能需要同步很多很多的屬性,然后還依賴於需要同步的實體的個數,還有一個就是網絡卡頓的時候的一個表現,狀態同步通常的表現是閃現,幀同步就是動作和輸入會出現一些延時。為什么會出現閃現,因為狀態同步通常使用的策略,會在本地做一個預算,你的預算很明顯很可能跟服務器算出來的最終結果不一樣,這樣他就會把客戶端重置到一個正確的狀態,就出現一個閃現的情況。這種是原生就帶來這個問題,但是額外做一些處理的話,還是可以盡量去避免這種問題。

  還有一個就是我們說的錄像文件的大小,錄像文件的大小,這也是比較重要的。因為現在手機游戲大家都希望去分享你的戰斗,分享你的精彩的時刻。如果你使用狀態同步的話,這個錄像文件比較大,幀同步因為只存指令是比較小的,如果錄像文件比較大,你的儲存、傳輸都是很大的問題。

  下面我再結合具體案例再講一下,我們來看一下,如果想使用狀態同步的話,你可以去研究一下《Dota2》,他使用狀態同步的一個原因,因為Dota2的廠商,有一個Source引擎,這個引擎基本上就是為FPS類游戲開發的,而FPS通常都是使用狀態同步,因為他們使用這個引擎,選擇狀態同步這么一種方案,很多代碼就可以復用。所以可以看到狀態同步也可以在MOBA游戲上得到一個很好的表現。還有就是大家比較熟悉的《守望先鋒》,他就是一個典型的FPS游戲。我們看一下使用幀同步的游戲,幀同步就是《Dota》,他是基於魔獸爭霸3,還有就是《風暴英雄》,《風暴英雄》使用星際爭霸2的引擎,所以說就沿用了幀同步這么一個結構。

  還有現在最火的《王者榮耀》,如果大家使用對應的方案可以研究一下這些游戲,他們的表現。下面截了一張,大家可以看一下,這是騰訊的全民超神,大家可以看到使用狀態同步方案,這個在400毫秒延遲的情況下,大家看一下下面的小兵和英雄,就會出現閃現的情況,還有一個你看最后英雄被擊殺的時候,沒有人打你,你卻莫名其妙的死了,這是一個典型的狀態同步在弱網絡環境下的表現。再對比看一下《王者榮耀》,王者榮耀同樣是400毫秒延遲的情況下,看一下王者榮耀的表現,你明顯可以看到左邊的搖桿往下之后英雄並沒有立即跟着往下,而是過了一會兒才往下。但是你是怎么死的,或者你的運行軌跡是怎么樣的,還是看的非常清楚的。我覺得可能右邊這種表現是更好的一種表現。

  上面就粗略地介紹了一下,我們通用的兩種方案,我認為沒有最好,只有最合適的,大家根據自己游戲的一些需求和你策划的玩法來選擇,使用哪一種同步方案。

幀同步的重難點

  我會從三個方面來講,一個是怎么樣保證確定性,然后怎么樣說能保證是流暢的。最后再來討論一下外掛的問題,外掛的處理。

  首先講一下確定性,要保證確定性,這是幀同步方案最難的地方。那么你要保證這個確定性,就是說各個客戶端根據同樣的輸入,算出同樣的結果,雖然這句話很簡單,但是想要做到算出同樣的結果是非常困難的。我們可以注意哪些問題呢,首先一定要把各客戶端的核心邏輯的同步頻率保持一致。如果你的頻率不一樣,比如用Unity的函數,每一幀運行時間間隔不一樣,如果這個不一樣的話會帶來非常多的問題,基本上是不可能算出一樣的結果,首先第一步就把這個頻率調成一樣的。

  然后是隨機數,我們很多游戲的話,肯定會使用到一些隨機的東西,隨機數在使用的時候,我們幾乎不能使用Unity自帶的隨機數,因為我們知道我們通常使用的是偽隨機數,我們為了保證在各客戶端的隨機數是一樣的,需要做哪些工作。首先需要把初始化的種子要同步,它們是一樣。然后還要保證在各個客戶端上,隨機數調用的次數是一樣。

  為什么不能使用Unity提供的隨機數,因為這個隨機數可能會被一些粒子系統,一些別的內部系統去調用,我們是沒有辦法控制這些調用的次數的,所以,我們需要實現一個自己的隨機數,來保證他的調用次數。然后浮點數,這也是討論比較多的主題了,到底我們是應該使用浮點數還是定點數來計算這個物理。據我所知,除了一些在主機上發布的,因為他們只在PS4或者xbox上運行他們的游戲,因為PS4和Xbox硬件架構是一樣,可以得出結果。但是硬件架構不一樣,或者用不同的編譯器編譯我們的代碼,能不能保證浮點數是一樣的結果,應該是可以,但是基本沒有看到哪家廠商有這樣實現過,為了做到這一點需要做非常多額外的工作,可能需要針對不同的CPU,然后不同的編譯器來優化我們的代碼,還要做一些額外的工作,才能保證算出來的結果是一樣。我們通常還是用定點數來算。

  下面這點,為什么說幀同步開發難度比較高。如果不是很了解這個架構的人去寫這個游戲邏輯的話,就很可能很自然使用一些本地的數據,就是一些客戶端獨有的數據,這個人能不能看到,渲染物體的位置等,可能這個位置在各客戶端上都是不一樣,如果拿這個來做判斷的話,就很容易顯示出來不同步,因為條件不一樣,那么算出來結果自然就不一樣了。這個可能就得根據你們游戲的一個設計,還有你的編碼來去判斷了,就大家可能需要注意這個問題。

  如果我們在使用一些第三方庫的話,比如使用一些box2D這種庫,使用自己開發的庫來做物理運算的時候,還是注意這個問題,可能一些變量沒有初始化,那在不同平台上,或者不同編譯器編譯出來的代碼,初始的值是不確定,如果用這個變量去運行一個邏輯的話,很可能就不同步了,而且還很難意識到這個問題。如果腦子里面有這個概念,變量是必須初始化,就能避免這個問題,同時發生問題的時候,也可以往這個方面想一下。

  然后就是一個遍歷的順序也會導致不同步,我們的一個經研究說,我們經常用Dictionary這個結構,你用這個結構是有問題,你可能天真地認為,往Dictionary順序插入一二三四個元素,他是不是按一二三四的順序去遍歷,官方是不能保證的,如果用這個結構,大家可以嘗試使用另外一種SortedDictionary這個容器,這個容器有一些開銷,如果不頻繁插入的話是沒有什么問題。

  然后就是Raycast返回的結果,在使用Unity的函數,可能會反饋給你一個結果,你碰到哪些東西。這個結果的東西也是不能保證,這個怎么辦,我們解決方法給每個實體會有一個唯一的ID,這是我們自己邏輯里面的唯一ID通過這個ID做一個排序,最后排完序以后再使用Raycast的結果。

  我們游戲一些上層游戲邏輯是用Lua來編寫的,Lua里面有一個Table,使用這個結構也是注意這個問題,把他當成hash表來遍歷的時候,這個順序也很難保證,我們一定要使用這個ipairs來遍歷,就是當成一個數組來遍歷。

  下面是流暢性,講流暢性之前,先了解一下,你不流暢的時候,到底是一個什么樣的表現。通常玩家反饋給你,只會反饋給你,一個是卡,或者說一卡一卡,或者卡卡的。玩家不會告訴你說,我是看到這個東西網絡是不是延遲了,或者說怎么樣?不會這樣,他會給你的是一些很感性的詞。當玩家在說卡的時候,他在說什么?其實他在說輸入和反饋之間存在延遲。輸入和反饋之間存在延遲,這很可能就是我們的網絡,可能存在說你丟包要重發,就是網絡延遲。

  還有一個是畫面卡頓,畫面卡頓的話,游戲存在性能問題,剛才我們《球球大作戰》跟我們分享了優化的對大家也是挺有幫助的。然后還有一個就是游戲物件在來回抖動,可能不是因為你的網絡延遲,也不是因為性能問題,可能單純只是因為你的這個物體,由於你邏輯上有問題,導致它看起來在抖動,我們就遇到過這個問題。

  下面講一下我們怎么來保證這個流暢性,TCP還是UDP,這個問題討論的非常多了。通常來說,我們認為對實時性要求比較高的MOBA游戲,我們還是選擇UDP,有些游戲也是選擇TCP,其實也是做的挺好,也可以根據自己的游戲需求,如果你覺得TCP還OK,那就選擇TCP沒有問題,很成熟,也很可靠。不需要做額外的工作。如果你覺得你的游戲對你的實時性要求比較強,還是推薦你用UDP,使用UDP帶來什么問題,首先UDP是不可靠的傳輸協議,可能需要自己去處理一下丟包,還有順序。通常常用的解決方案就是增加冗余數據,如果一次發包,可以一次帶兩幀的數據,如果丟包之后,丟了一個包,但是仍然可能還是有額外的一幀供你使用,繼續去渲染。

  使用UDP,還有一個問題是說,中國網絡國情,有些網絡是連不上UDP的,在這種時候還是需要處理一下,就是在UDP連不上的時候,是不是還要回去使用TCP。如果大家想要進一步了解TCP和UDP,我推薦大家一本書TCP/IP詳解,這個里面講的是非常清楚的。

  第二個是Tran位置的更新順序,這是流暢性提到第三點問題,其實並不是因為網絡延遲,也不是因為你的性能有問題,而是因為你的,就是更新有問題,而導致玩家看到抖動。然后會常用的一個方案就是說,使用這個運動補間,這就沒什么好說的。這個還有一個問題,大家還會有疑問,剛才說到了,我們剛才說的,要所有客戶端算出一樣的結果,如果用運動補間,因為這個運動補間是基於渲染幀,這個幀率也不能保證是固定間隔去調用的,這個就牽出下面一個問題,就是說你的顯示和邏輯一定要分離。你看到的這個人物的真實位置並不是邏輯位置,就是客戶端已經拿到真實位置,但是並不把真實位置顯示到屏幕上,如果把真實位置顯示到屏幕上人物就是抖動,這個玩家體驗是不好,但是底層邏輯是抖動,但是顯示的物體是流暢的在移動的,我覺得這個玩家是可以接受的。

  下面再來討論一下外掛,外掛這個問題的話,通常外掛就是修改屬性,修改屬性的話,如果是使用幀同步的話,修改屬性基本上沒好說的,雖然是在客戶端各自算的,但是由於你的數據並沒有說去同步,如果只是簡單地把本地的數據給改了,比如把攻擊力從10調到一萬,去秒殺你,但這沒什么用,因為別的客戶端攻擊力還沒有提高,別的客戶端看到還是正常,這種時候我們可能需要一個檢測,是否本地有一些修改。可能你會會把一個玩家所有的屬性做一個Hash,然后去服務器上做一個校驗,如果你服務器沒有運行這個邏輯,不知道這個值算的對不對,可能需要把別的客戶端也發出來做一個仲裁,如果這個人算出來跟其他幾個人算出來不一樣,那更傾向於認為這個客戶端肯定是有問題的。

  修改屬性除了剛才說的方法,可以通過一些加密變量的方案,讓外掛找不到真實的值在哪里。通常這個效果不是特別好,如果想改的話也是能改,通常還是會去使用一個校驗。

  第二個是透視外掛,以前老玩家玩的Dota比較多的,這個外掛還是比較多的,因為在Dota里面視野是非常重要的,由於所有數據都在本地,你的控制這個玩家顯不顯示,可能只是一個變量的問題,改了這個東西,就可以看到這個東西。但是對於一些其他的游戲可能,就相對來說沒有那么重要,還是說看你們的游戲,這個重不重要。如果重要的話,我們會怎么辦?可能需要,一些關鍵數據。雖然幀同步只同步指令,但是應對外掛問題上,關鍵的數據還是選擇去同步。比如你的透視只是一個變量的問題,可能就是一個字節的問題,對你網絡的帶寬就傳輸的東西影響是非常小的,但是就可以完全避免這個問題。

  還有自動操作外掛,對我們這種類DOTA的MOBA游戲影響不大,能模擬一個玩家的操作表現很困難。但是對《守望先鋒》,這種操作外掛就影響非常大,比熟練玩家瞄的准多了,這個沒有特別好的方法,如果所做的是一種類DOTA的MOBA游戲,就不需要去關注這種外掛,一因為也不太可能搞出這種外掛,如果做FPS這種游戲還需要關注一下,目前業界沒有特別好的解決辦法,還是會靠一些發動一些玩家去檢測來,用真人來檢查,來解決這個問題。

  最后還有皮膚美化的外掛,如果很嚴重可能需要處理一下,去做一些檢查。如果不是特別嚴重,也不需要做一些額外的工作。以上就是我的所有的內容了,希望對大家有所幫助,謝謝大家。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM