ECS方式實現幀同步

0x00. 引言

ECS是Entity-Component-System(實體-組件-系統) 的縮寫,是一種很是好用的框架思想,能夠提升代碼複用率,遊戲邏輯開發中使用這種組合因爲繼承的方式能夠很大程度上簡化複雜度,並且在性能上也是有很大提高的。Entiy是一個包含惟一ID的容器對象,在Entity內部能夠綁定不少Component,每一個Component只負責存儲數據,正是由於Component的功能單一性,它能夠很方便的作數據快照。System內部負責操做Component數據,從而最終做用於Entity。
 
幀同步的應用場景不少, (幀同步與狀態同步的區別,感謝知乎做者雲影)從格鬥遊戲,Rts,到如今的幾乎全部Moba類遊戲,他們各自的實現方式細節各不相同,幀同步對不一樣類型的遊戲也有各類變種。列舉一下例子,如格鬥遊戲,因爲參與角色很少,玩家對角色操做反饋及時性要求很高,所以這種幀同步須要有預測能力,在預測對方玩家行爲以後還須要有回退矯正的能力。有預測能力意味着操做反饋會很流暢,也是因爲角色很少,出現預測錯誤的概率也很少回退矯正形成的視覺影響也不會很大,所以格鬥遊戲的幀同步就要採用這種方式定製。再好比魔獸爭霸遊戲,這種早期局域網環境下的對局方式,網絡的環境比較好,各個客戶端採用的是鎖幀方式的幀同步,若是其中一個客戶端出現小延遲狀況,客戶端本身會迅速根據關鍵幀標記跟上服務器的步伐,若是出現大延遲甚至斷線,那麼全部的客戶端須要一塊兒暫停等待,所以這種方式對於多控制單位的遊戲類型很好用也很簡單。
 

0x01.開始結合 

準備將幀同步與ECS合起來工做,幀同步邏輯實現的第一步是要將邏輯和顯示完全的分離,能夠把不少邏輯放到線程裏去,這點也是ECS能夠帶來的便利。
首先須要實現一個模擬器,這部分的代碼功能主要是實現一個stopwatch的機制,在線程中使用stopwatch記錄時間流逝,在設定邏輯幀幀率參數一致下同時啓動模擬器保證在通過相同的一段時間內全部的模擬器執行的幀數同樣。模擬器還須要有添加行爲的功能,各類行爲以互相排列組合的方式共同協助模擬器完成整個模擬過程, 下圖能夠看到一個模擬器中每一幀須要依次執行下列幾個模塊。
#邏輯幀記錄:主要負責記錄當前的邏輯幀編號;
#網絡數據回滾:負責收取服務器廣播的帶幀編號的網絡數據,這部分也是幀同步的核心,收到的邏輯幀數據須要和本地的數據進行回滾並向後繼續演算,而且記錄下操做以便播放戰報;
#實體操做:在這裏會是全部ECS中System的執行入口,如MoveSystem等;
#用戶輸入:完成監聽並記錄用戶操做,用於最後一步的請求同步;
#數據備份:對當前幀的全部數據進行快照保存,以便用戶回滾;
#請求同步:將操做發送給服務器;
整個順序過程會在一個邏輯幀內執行,由於上述全部邏輯都放置與線程中執行,因此在顯示方面和這部分邏輯是沒有調用關係的,所以須要在主線程中對於當前的數據作出響應,這就是一個數據驅動顯示的過程。這樣作的好處有不少,首先就是邏輯顯示分離後服務器也能夠執行邏輯代碼進行驗證,其次線程裏執行的邏輯運算功能能夠分擔主線程壓力。
 
上述的模塊還能夠繼續擴展,好比能夠在用戶輸入模塊後繼續加上AI輸入模塊,在實體操做模塊中添加子系統,子系統以ECS中的System形式出現,負責根據需求改變數據。
 
關於ECS的實現能夠有不少種,具體狀況因地制宜,固然也能夠本身DIY一下。我在這裏把ECS擴展成ECSR,多出來的R表示Render,Render會根據Component數據內容用於主線程畫面的表現。

0x02. 擴展一下

上述完成了對幀同步模擬過程,還有一項幀同步特有的功能就是戰報回放,這是狀態同步開發下不管如何也完成不了的。戰報回放的replay文件能夠以不一樣速率完整的模擬出整個幀同步過程,這依賴於邏輯幀命令的記錄和保存。一樣是用模擬器去完成只是在模擬器中添加不一樣的模塊。
#邏輯幀記錄:這裏和上述的稍做改變,做用類似,也是記錄邏輯幀編號;
#實體操做:同上
#回放輸入:根據回放的replay文件,讀取邏輯幀命令;
 

0x03. GIF效果展現

效果展現爲多用戶移動命令下的幀同步效果。
 

0x04. 後續

關於幀同步的網絡通訊方面的選擇,最好仍是使用UDP方式,採用發送冗餘信息,外加丟幀請求的方式。我這裏爲了簡單實現同步的過程採用的是TCP方式。後續的修改應該是往UDP方式改進。
另外幀同步中的數學運算官方提供了 Unity.Mathematics數學庫 。
相關文章
相關標籤/搜索