遊戲開發中常用的兩種同步模式:狀態同步和幀同步

一、同步

所謂同步,就是要多個客戶端表現效果是一致的,例如我們玩王者榮耀的時候,需要十個玩家的屏幕顯示的英雄位置完全相同、技能釋放角度、釋放時間完全相同,這個就是同步。就好像很多個人一起跳街舞齊舞,每個人的動作都要保持一致。而對於大多數遊戲,不僅客戶端的表現要一致,而且需要客戶端和服務端的數據是一致的。所以,同步是一個網絡遊戲概念,只有網絡遊戲才需要同步,而單機遊戲是不需要同步的。

 

二、狀態同步和幀同步的區別

最大的區別就是戰鬥核心邏輯寫在哪,狀態同步的戰鬥邏輯在服務端,幀同步的戰鬥邏輯在客戶端。戰鬥邏輯是包括技能邏輯、普攻、屬性、傷害、移動、AI、檢測、碰撞等等的一系列內容,這常常也被視爲遊戲開發過程中最難的部分。由於核心邏輯必須知道一個場景中的所有實體情況,所以MMO遊戲(例如魔獸世界)就必須把戰鬥邏輯寫在服務端,所以MMO遊戲必須是狀態同步的,因爲MMO遊戲的客戶端承載有限,並不能把整張地圖的實體全部展現出來(例如100米以外的NPC和玩家就不顯示了),所以客戶端沒有足夠的信息計算全圖的人的所有行爲。

具體到客戶端和服務端通信上,在狀態同步下,客戶端更像是一個服務端數據的表現層,舉個例子,一個英雄的幾乎所有屬性(例如血量、攻擊、防禦、攻速、魔法值等等)都是服務端傳給客戶端的,而且在屬性發生改變的時候,服務端需要實時告訴客戶端哪些屬性改變了,客戶端並不能改變這些屬性,而是服務端傳來多少屬性就顯示多少屬性(雖然可以改變客戶端數值達到表現上的效果,例如無限血量,但是服務端那邊的血量屬性爲0時,一樣要死)。再舉個例子,一個英雄要釋放一個非指向性技能(例如伊澤瑞爾的Q),具體的過程就是,客戶端通知服務端“我要釋放一個技能”-》服務端通知客戶端“在某地以什麼方向釋放某技能”-》客戶端根據這些信息創建一個特效放在某地,然後以某個方向飛行-》服務端根據碰撞檢測邏輯判斷到某個時刻,這個技能碰到了敵方英雄,通知客戶端-》客戶端根據服務端信息,刪除特效,被打的英雄減血同時播放受擊特效。

而在幀同步下,通信就比較簡單了,服務端只轉發操作,不做任何邏輯處理。以下圖爲例:

現在同一局裏有4個玩家,也就是4個客戶端,這時客戶端A釋放了一個技能x,此時將操作傳遞給服務端,服務端不做任何判斷,直接把A的操作全部分發給ABCD,則ABCD同時讓客戶端A控制的英雄釋放技能x。

 

三、流量

狀態同步比幀同步流量消耗大,例如一個複雜遊戲的英雄屬性可能有100多條,每次改變都要同步一次屬性,這個消耗是巨大的,而幀同步不需要同步屬性;例如釋放一個技能,服務端需要通知客戶端很多條消息(必須是分步的,不然功能做不了),而幀同步就只需要轉發一次操作就行了。

 

四、回放&觀戰

幀同步的回放&觀戰比狀態同步好做得多,因爲只需要保存每局所有人的操作就好了,而狀態同步的回放&觀戰,需要有一個回放&觀戰服務器,當一局戰鬥打響,戰鬥服務器在給客戶端發送消息的同時,還需要把這些消息發給放&觀戰服務器,回放&觀戰服務器做儲存,如果有其他客戶端請求回放或者觀戰,則回放&觀戰服務器把儲存起來的消息按時間發給客戶端。

 

五、安全性

狀態同步的安全性比幀同步高很多,因爲狀態同步的所有邏輯和數值都是在服務端的,如果想作弊,就必須攻擊服務器,而攻擊服務器的難度比更改自己客戶端數據的難度高得多,而且更容易被追蹤,被追蹤到了還會有極高的法律風險。而幀同步因爲所有數據全部在客戶端,所以解析客戶端的數據之後,就可以輕鬆達到自己想要的效果,例如moba類遊戲的全圖掛,喫雞遊戲的透視掛,都是沒辦法防止的,而更改數據達到勝利的作弊方式(例如更改自己的英雄攻擊力)可以通過服務器比對同局其他人的戰鬥結果來預防。

 

六、服務器壓力

狀態同步服務器壓力比較大,因爲要做更多運算。

 

七、開發效率

首先要說,狀態同步的遊戲佔主流,其次就是狀態同步開發起來比較難。而幀同步服務器開發難度低,同一套方案可以給很多不同類型的遊戲使用,反正都是轉發操作;減少了服務端客戶端溝通,老實說,沒有扯皮的時間,開發效率最起碼提高20%,狀態同步的方案下,同一個功能至少需要一個客戶端和服務端共同完成;PVP和PVE基本用的是同一套代碼,做完PVP很容易就可以做單機的PVE。

 

八、使用幀同步的知名遊戲

王者榮耀、魔獸爭霸3、所有格鬥類遊戲

 

九、斷線重連

狀態同步的斷線重連很好做,無非就是把整個場景和人物全部重新生成一遍,各種數值根據服務端提供加到人物身上而已。幀同步的斷線重連就比較麻煩了,例如客戶端在戰場開始的第10秒短線了,第15秒連回來了,就需要服務端把第10秒到第15秒之間5秒內的所有消息一次性發給客戶端,然後客戶端加速整個遊戲的核心邏輯運行速度(例如加速成10倍),直到追上現有進度。

 

 

十、注意點

需要保證每次隨機的數字都相同,所以需要自己實現一套隨機數,不能用unity自帶的那個隨機數接口,而且需要服務端發送相同的隨機種子;因爲非常微小的誤差就有可能產生蝴蝶效應,所以所有float型的參數必須變成int型,保證計算結果一致。

 

 

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