實時游戲發展迅猛,同步技術也逐漸成為解決方案的核心之一。 本文簡單討論了幀同步和狀態同步。
幀同步
什么是幀同步:幀同步常被RTS(即時戰略)游戲常采用。在游戲中同步的是玩家的操作指令,操作指令包含當前的幀索引。一般的流程是客戶端上傳操作到服務器, 服務器收到后並不計算游戲行為, 而是轉發到所有客戶端。這里最重要的概念就是 相同的輸入 + 相同的時機 = 相同的輸出。
實現幀同步的流程一般是:
-
同步隨機數種子。(一般游戲中都設計隨機數的使用, 通過同步隨機數種子,可以保持隨機數一致性)
-
客戶端上傳操作指令。(指令包括游戲操作和當前幀索引)
-
服務器廣播所有客戶端的操作。(如果沒有操作, 也要廣播空指令來驅動游戲幀前進)。
因為幀同步的特性, 我們可以很方便的做出戰斗回放:服務器記錄所有操作, 客戶端請求到操作文件再執行一次即可。
幀同步的特性導致客戶端的邏輯實現和表現實現必須完全分離。Unity中的一些方法接口(如 Invoke, Update、動畫系統等)是不可靠的,所有要自己實現一套物理引擎、數學庫,做到邏輯和表現分離。 這樣即使Unity的渲染是不同步的,但是邏輯跑出來是同步的。
我曾經參與過一個飛機類彈幕游戲的項目,它的同步方案就是幀同步, 可以完美的播放回放, 並實現服務器上加速驗算。
狀態同步
什么是狀態同步:同步的是游戲中的各種狀態。一般的流程是客戶端上傳操作到服務器,服務器收到后計算游戲行為的結果,然后以廣播的方式下發游戲中各種狀態,客戶端收到狀態后再根據狀態顯示內容。狀態同步最廣泛的應用應該是在回合制游戲中。
狀態同步其實是一種不嚴謹的同步。它的思想中,不同玩家屏幕上的表現的一致性並不是重要指標, 只要每次操作的結果相同即可。所以狀態同步對網絡延遲的要求並不高。像玩RPG游戲,200-300ms的延遲也可以接受。 但是在RTS游戲中,50ms的延遲也會很受傷。
舉個移動的例子,在狀態同步中, 客戶端甲上操作要求從A點移動到B點,但在客戶端乙上, 甲對象從A移動到C,然后從C點移動到了B。這是因為, 客戶端乙收到A的移動狀態時, 已經經過了一個延遲。這個過程中,需要客戶端乙本地做一些平滑的處理,最終達到移動到B點的結果。
所以國產RPG游戲中,動畫的特效一般做的比較絢麗(大), 攻擊的時候給人感覺是擊中了。放技能之前一般也有一個動畫前搖,同時將攻擊請求提交給服務器。等服務器結果返回時,動畫也播放完畢了,之后就是統一的傷害效果和結算。
狀態同步和幀同步的比較和選擇:
比較:
狀態同步 | 幀同步 | |
---|---|---|
流量 | 相對高 | 相對低 |
回放 | 記錄文件大 | 記錄文件小 |
安全性 | 服務器實現邏輯,安全性高 | 邏輯在客戶端,反外掛壓力大、無法避免開圖掛 |
服務器壓力 | 大 | 小 |
戰斗校驗 | 協議加密,內存混餚,誤差校驗,無法徹底解決。 | 服務器可以重啟跑一遍戰斗。 |
網絡卡頓的表現 | 瞬移,回位,莫名掉血 | 戰斗卡頓 |
實現 | 調優狀態同步方式,客戶端需要做插值處理。 | 客戶端按照單機方式開發,保證邏輯層和表現層分離。邏輯層不要用到浮點數,不要用不確定順序的邏輯結構。對於物理引擎和浮點數計算要不能使用Unity的。 |
選擇:對於單位比較多的即時策略游戲,幀同步是很好的選擇。反過來,如果玩家比較多,狀態同步更合適,安全性更高。