16年年末的時候我從當時的公司離職,來到了目前任職的一家更專一於遊戲開發的公司。接手的是一個platform遊戲項目,基本狀況是以前的團隊完成了第一個版本,即單人模式的基礎玩法,可是以後對該項目的定位又變成了一個本地局域網的聯機手遊(2-4個玩家)。所以,重寫項目底層外加肯定網絡同步方案就成了第一件須要去認真考慮的事情了。那麼本文就來聊聊網絡同步這件事吧。html
開發網絡多人遊戲是一件十分有趣的事情,可是和單機遊戲相比無疑增長了更多的挑戰。算法
例如,咱們以前開發的單機版本並不須要多麼擔憂做弊的問題。這是由於購買咱們遊戲的玩家(假如咱們的單機遊戲難免費發佈的話)即使做弊,影響的也僅僅是他本身的遊戲體驗,不會影響到別人。
可是開發多人遊戲就不是這樣了,爲了保證讓每一個人都有好的遊戲體驗,防止做弊老是須要去考慮的。編程
除此以外,在開發多人遊戲時咱們還須要考慮如何「欺騙」玩家的眼睛,讓他們認爲他們在同一個世界中。服務器
當2個或4個玩家一塊兒在手機上玩遊戲時,看上去他們確實像是在共享同一個虛擬世界,在同一個世界中游玩。但事實倒是,玩家本身的手機只是對「同一個」虛擬世界的近似模擬。換言之,他們的遊戲世界每個都是獨一無二的,只不過從外觀上看起來像。網絡
所以,爲了達到這種看上去近似的效果,咱們須要確認哪些狀態是須要同步的,只要同步了這些狀態,這個遊戲世界就看上去同樣了。而哪些狀態是無需同步的,即這些對象的狀態是否同步對整個遊戲是否看上去同樣並無特別大的影響。運維
在咱們的遊戲中,玩家的各類屬性、在世界中的座標、遊戲世界中的敵人各類屬性、道具獲取以及各類觸發器的觸發等等都有可能會對遊戲的表現產生影響,所以須要考慮同步;可是像例如海底的水泡粒子效果、道具獲取後的碎裂效果,甚至是背景音樂則不會對遊戲的表現產生特別的影響,所以並無必要去同步這些內容。
性能
既然明確了不存在兩個徹底同樣的遊戲世界,每一個遊戲世界無非都是近似的模擬。那麼接下來咱們就要來選擇一個適合的網絡同步方案以知足這種需求了。最基本的遊戲網絡同步模型大概能夠分爲如下4種(畫圖水平通常,見諒):3d
client-server:專用服務器
client-server:玩家之一做爲服務器orm
peer-to-peer
peer-to-peer:幀同步server
上面的兩種client-server模型的相同點都在於有一臺機器負責整個遊戲世界的模擬,而這臺負責整個遊戲世界模擬的機器是誰則是這二者最大的區別。在咱們的項目中,咱們藉助其中一個玩家的手機做爲服務器,咱們叫它Master主機,而通常的玩家設備則被稱爲Client。固然,更常見的一種情景是遊戲開發商或發行商管理的計算機做爲服務器,這也每每須要更多的計算機和運維人員。
一般,基於這種同步模型的遊戲中客戶端不能作出真正的決定。一個情景就是當客戶端的玩家按下一個按鍵,客戶端並不會真正的執行影響遊戲狀態的操做,相反操做會被髮往服務器,並在服務器執行它,以後服務器將執行完這個操做以後的結果(一般是遊戲世界的狀態變化)返回給客戶端。
因爲你們都知道的網絡延遲,所以服務器和客戶端並不是時刻保持一致的,爲了使遊戲玩家的狀態變化天然(主要是指玩家的位置、角度等狀態),咱們使用的是一種基於插值的同步算法(固然,這種方式也經常被稱爲影子跟隨算法):
固然,將全部的邏輯放到服務器並通過服務器的模擬以後再將結果返回給客戶端的過程會帶來一些滯後感,當玩家對操做的敏感度要求較高時,這顯然不是一個很好的解決方案。所以,客戶端的輸入預測和服務端的延遲補償開始獲得應用。經過在客戶端側的輸入預測,可讓玩家的輸入獲得馬上的反饋。而延時補償則保證告終果的正確性。這個過程能夠基本歸納爲如下幾個階段:
雖然這樣作可以更好的保證玩家的手感,可是咱們發現不管是客戶端仍是服務器,一旦收到消息包以後都須要回滾。而這種回滾機制相對來講較爲複雜,而且也不容易在已有的遊戲中加入這種機制。
綜上,咱們能夠看到在這兩種同步模型中,服務器獲取客戶端的操做指令並在服務器內模擬整個遊戲世界,以後服務器是將服務器所維護的遊戲世界內的狀態同步給各個客戶端,所以這裏主要是作狀態同步。
Peer to Peer點對點同步模型是一種很經典的網絡遊戲網絡同步模型。帶有幀同步模型的Peer to Peer在不少RTS遊戲中獲得了大量應用,不過在討論幀同步模型以前,咱們先來聊聊通常的Peer to Peer。
相對於C/S模型擁有一個計算機負責整個遊戲世界的模擬,Peer to Peer模式並無單一的計算機來負責模擬遊戲世界。相反它將對遊戲世界的模擬分配給了全部玩家,於是每一個玩家的客戶端都在模擬着本身的遊戲世界。這樣作的一大好處在於玩家的輸入老是馬上響應的,我按下一個按鈕,按鈕形成的結果便發生了,同時我須要作的是將個人操做發送給和我相連的客戶端,讓他們也去根據我發送的操做模擬遊戲世界。可是這樣作的一大弊端在於不能保證客戶端看到的遊戲畫面是同樣的。
例如上圖上方的怪物射出的子彈能夠經過畫線來阻擋,可是因爲client1和client2都是在模擬本身的遊戲世界,所以延遲或是不一樣移動設備自己的性能問題就有可能會形成client1的畫線操做同步到client2上時產生不一樣的結果。因此咱們發現只是簡單的讓每一個客戶端模擬本身遊戲世界(就像單機那樣),同時簡單的將操做同步給別的客戶端,至少在同步這個問題上是不靠譜的。
所以,遊戲行業大多會採用幀同步模型來保證同步的可靠性。不少早期的RTS遊戲都採用了幀同步來做爲網絡同步的方案。至於爲何不少人在介紹幀同步的時候,都喜歡把早期的RTS遊戲搬出來做爲一個例子呢?我想各位看一眼RTS遊戲的遊戲截圖就能猜到個大概了。
RTS遊戲中經常伴隨着數十上百甚至上千個邏輯實體單位,若是採用狀態同步的話數據量相對要大不少。可是若是隻同步玩家的操做呢?若是每一個客戶端在相同的狀況下開始遊戲,而且運行徹底相同的步驟,那麼客戶端就能夠不經過接收狀態同步信息就能保證遊戲的同步了。
這也是這種模型的一大優點,咱們除了發送玩家的操做以外幾乎不須要再發送任何數據。這種同步輸入的方式能夠說很是適合RTS遊戲,由於它們有那麼多的單位,同步全部單位的狀態是不容易的。
所以,採用這種模型就能夠把遊戲的過程分爲一個一個的回合。遊戲的每一步都須要經過網絡來收集全部玩家的操做輸入,而後再往下執行。固然,一提到「回合」這個詞,你們想到每每是所謂的回合制遊戲,但事實上只要回合的頻率足夠快,仍然是能夠作出即時遊戲的感受。
固然,因爲沒有同步遊戲的狀態,而是同步玩家在遊戲內的輸入操做,所以實現徹底同步仍是有一些事情須要注意的。由於一旦一個小小的不一樣步發生,就會產生蝴蝶效應,從而引發很明顯的不一樣步。一個典型的例子即是我之前在開發一個戰鬥回放系統時,發現因爲一個士兵在尋路的時候稍微走到有點不同的地方,就致使了一場戰鬥的結果大不相同。
雖然咱們目前的項目並無採用幀同步的方案,可是仍是想和你們分享一點教訓。例如不要使用浮點型數據,這是因爲舍入會形成偏差,因此建議各位使用整形數據。一樣,另外一個又被重視又被忽略的是隨機數的問題。你們都知道幀同步要保證隨機數也徹底一致。所以,你們都會去同步隨機數生成器的種子和它們的使用方式。可是一個潛在的可能性是某一方的非遊戲邏輯對象使用了隨機數生成器,從而形成不一樣步。例如某一方的移動設備性能更好,也所以屏幕上有一些額外粒子特效,這些粒子特效是有可能會使用隨機數發生器的,若是這些遊戲邏輯以外的對象使用了隨機數發生器就會形成不一樣步的發生。
哦,對了,最後須要說明的一點是幀同步還能夠和C/S模型組合使用,咱們能夠經過服務器來轉發客戶端的操做數據,而沒必要讓各個客戶端直接通信。公司內有項目組採用的就是這種方案。
固然,以上只是一些基本的同步模型。在這裏只是結合咱們的項目經驗和你們作一個簡單的分享,我想基於這些基本的模型還會衍生出一些別的方案。也歡迎你們來一塊兒交流。
歡迎你們關注個人公衆號慕容的遊戲編程:chenjd01
最後打個廣告,歡迎支持個人書《Unity 3D腳本編程》~