幀同步遊戲開發小結

本文發表於程式設計師劉宇的個人部落格,轉載請註明來源,https://www.cnblogs.com/xiaohutu/p/12402399.html

這幾年做了一些網路同步項目,總結一下幀同步的一些東西。

1. 幀同步基本特點

  1. 所有的邏輯行為運算都在客戶端進行,客戶端保證彼此之間執行結果的一致性。
  2. 客戶端將自己的所有操作發給伺服器,伺服器轉發。
  3. 伺服器維持一定的邏輯幀率向客戶端發包,每次都帶上一間隔的所有客戶端發來的操作,如果沒有就發空幀,附帶上客戶端需要執行此包的幀數。
  4. 客戶端收到幀數據執行這一邏輯幀的行為,否則等待。

2. 同步性的保證

  • 確保需要同步部分邏輯執行次序的一致性,特別關注各種容器的底層結構以及運行過程中對容器的增刪改。
  • 確保AI、物理引擎等執行結果的一致性,避免因為使用部分遊戲引擎的更新特性,導致一些AI計算的時間次序問題。
  • 確保數學運算在不同cpu上的的一致性,使用定點數或者浮點數截取等方法計算邏輯。
  • 確保隨機結果基於次數的一致性,使用次數一致性的隨機演算法,如梅森旋轉演算法。
  • 確保客戶端數據來源的一致性,存儲的靜態數據以及讀取過程需要特別關注。

3. 邏輯畫面分離

  • 抽離開影像執行邏輯和關鍵幀執行邏輯的循環結構。
  • 邏輯部分可以無畫面執行,正確的輸出結果。
  • 數據關鍵幀(即執行AI的關鍵幀)可以調整,和影像幀的比例也可以調整,影像幀做好動畫、位移、旋轉等行為的平滑。
  • 在一些遊戲中需要實現快速的邏輯與影像脫鉤,即迅速載入畫面,迅速卸載畫面,是否需要畫面由需求決定。

4. 樂觀鎖

  • 傳統情況,每個客戶端必須回報給伺服器收到幀數,伺服器再次發送確認包才執行幀數據,否則所有人等待。
  • 現在情況,基於現在手游的流行程度和若網路環境,手游一般都採取樂觀鎖模式。即收到伺服器推幀後,客戶端立即執行,不等待其他人。這樣卡頓的人自己卡,不影響其他人的遊戲體驗。同時卡頓的人在收到數據後,自行加速補幀,追趕上正確的遊戲速度。

5. 斷線的處理

  • 斷線重連:伺服器存儲好所有的開戰數據和所有的幀數據,用來做斷線重連(見7)。
  • 軟重連:製作一定的遊戲策略,在遊戲畫面卡住的情況下,盡量通過從伺服器拉取增量幀數據來恢復現場。

6. 弱網路處理和優化

  • 應對網路抖動,快取適當的幀在本地,作為抖動窗口,本地適當延後運行,這樣可以應對網路延遲較高的情況。當然快取的越多,客戶端操作操作的延遲就越高,越少則遇到未收到幀的時候有可能會卡頓。
  • 如果對操作實時性要求比較高,可以製作回滾機制(Rollback)(見9),客戶端在預測的基礎上,對玩家的操作進行實時的響應,不等待伺服器數據,實時的進行行為。收到伺服器的推送後將自己的預測與伺服器下發的數據進行行為的一致性驗證,不一致則立刻回滾,執行伺服器的數據。
  • 應對TCP的擁塞機制,使用RUDP一類的帶保障機制的UDP協議,避免協議擁塞導致的卡頓。
  • 應對UDP丟包,首先是客戶端回包帶標籤來補發點機制,其次也可以每次都發冗餘包,比如上5個關鍵幀的所有數據。
  • 協議包不一定採取Protobuf等通用結構,可以採用自定義二進位來減流量,提高弱網路發包的成功率。

7. 追趕和重連的補幀

在卡頓情況和短線重連發生之後,都要在短時間內執行大量的幀數據來追趕上其他人的遊戲進度,這個周期下客戶端有一些特點,分情況看一下:

  • 斷線重連時,因為其他人的當前幀數遠超過自己,此時自己的玩家輸入操作是基於很舊的數據執行,如果立即同步過去,其他人的客戶端會存在非法和不合理的情況,所以一般這個時候會屏蔽或者丟棄掉。大多數網遊都是使用Loading畫面屏蔽掉這個過程,因為玩家自知是斷線,可以理解。
  • 較短時間的補幀,伺服器是可以設計為接受操作的。對於自己來說,這個操作實際時延是當前展示的邏輯幀到收到伺服器下一個最新邏輯幀的時間。因為存在延遲,對於所有客戶端來說,這個操作也有可能在執行時被標記為非法,比如攻擊一個小兵的操作,實際上過一兩幀執行的時候,小兵已經死亡。客戶端對於幀數據的處理必須要處理好異常情況,多個客戶端使用同樣的異常處理則結果仍然是同步的。

8. 戰鬥錄像

這一塊對於幀同步來說天生是有優勢的,採用和斷線重連一樣的機制,按正常速度播放就可以在客戶端重播整個戰鬥過程。一個戰報數據包括:

  • 開始數據(單位屬性,ID,戰場資訊等)
  • 過程數據(總幀數,有操作的幀數據)

9. 回滾(Rollback)和高階錄像

先說說回滾的實現:

  1. 首先遊戲的邏輯、數據、表現拆解的特別完美,可以實現數據變化後畫面盡量不違和的變化。
  2. 將所有數據可以按照幀存儲,可以採用Attribute來採集指定變數,也自己寫好介面實現(這一步會比較複雜,但是可行),這一步我們稱之為快照(Snapshot)。
  3. 在遊戲運行時,存儲一定量的幀本地數據,比如100幀。
  4. 在遊戲邏輯進行預測,與伺服器數據不一致失敗後,進行數據的回滾,同時通知表現層回滾,執行相應的表現層邏輯。

這個時候,我們可以得到錄像的拖動功能:

  1. 錄像播放的時候實時存儲所有快照。
  2. 製作進度條,可以反覆拖動。拖動後在數據層執行上面同樣的第4步操作,回退畫面。

10. 防作弊

  • 首先做完整的戰鬥離線校驗是沒有問題的,類似戰鬥錄像的過程,可以採用跑客戶端同一套程式碼的方式來進行。這對於伺服器來說是一個性能問題,取決於遊戲類型和數據大小。
  • 根據錄像數據驗證合法性的基礎上,還要加入技能CD等確認客戶端輸入合法性的校驗。
  • 對於多個客戶端上報不同結果的情況,需要根據不同情況具體分析,如果是多人對戰項目,傾向於採信結果相同多的一方。無論如何都要進行離線校驗。
  • 同時也可以進行實時在線校驗,採用和客戶端一套程式碼運行在伺服器的形式,來模擬一個客戶端驗證各種數據的結果。
  • 對於開圖等客戶端外掛,需要額外手段防護,無法通過幀同步機制來防止。
  • 對於加速外掛,天生可以防止,不存在問題。

11. 同步性的調試

一個成熟的項目,中期一定要經歷大量的調試來能保證幀同步一致性,這裡有一些我用過的技術手段和方法。

  1. 首先戰鬥校驗服,客戶端的發布全自動化,通過遊戲更新機制里的程式版本號和資源版本號來保證客戶端資源的一致性。
  2. 戰鬥校驗服也自動發布一個客戶端的純邏輯執行版本,用來離線在客戶端調試戰鬥純邏輯過程。
  3. 在戰鬥過程中加入日誌用來查看調試,在戰鬥校驗服、客戶端調試模式、客戶端運行模式下均生成同格式的日誌,用來進行比對。
  4. 不一致的戰鬥數據通過戰鬥校驗服、客戶端調試模式、客戶端運行模式的日誌進行查看比對。
  5. 有了以上機制後,製作全自動校驗驗證的程式,採集一定的戰鬥用例,每次發版本或者長期多機器、多平台自動化24H運行,避免修改導致的一致性問題。

12. 總結

  • 總體來說,幀同步對於伺服器壓力很小,承載較高、流量也小。
  • 對於客戶端來說,實現簡單,調試難度較高,作為一個實現實時戰鬥的放來,性價比較高。
  • 幀同步比較適用於弱聯網情況下的強同步遊戲,有足夠多的方式應對弱聯網環境。
  • 幀同步製作需要中途加入的遊戲比較簡單。
  • 幀同步實現錄像、校驗等難度不高。

寫到這裡差不多把個人的一些經驗與大家分享完畢,有問題歡迎與我聯繫,謝謝大家的時間。