在上一篇文章中

Jerry:UE4網路同步思考(一)---經典同步方案?

zhuanlan.zhihu.com
圖標

介紹了下UE經典網路同步方案,適用於絕大多數射擊遊戲同步場景,但眼下吃雞玩法正興,Fortnite大世界有多達50000+個同步對象,按照傳統的同步方案會給伺服器的CPU帶來巨大的壓力。

專用伺服器(Dedicated Server,簡稱DS)CPU的消耗位於網路同步的發送端,因此我們先回顧下經典網路同步方案中的發端演算法。

舉個圖例,DS每幀會遍歷遊戲世界裡的所有對象,這裡假定是A1,A2,A3,排除掉不需要進行同步或者不相關的A1,再根據A2和A3的同步屬性劃規到不同的連接通道Connection裡面,這裡A2與所有客戶端連接都相關,而A3僅與Connection2相關。因同步帶寬有限,只能保證高優先順序的Actor最先同步,所以需要進行排序。排序後就是每個Actor內部的同步屬性比較,以及將變化屬性的地址和變化量寫入包中並發送。

注意經典發端演算法存在的幾個問題:

(1)每幀遍歷所有Actor,還要逐個計算每一個Actor與每一個客戶端連接的相關性,即使這些相關性是確定的(如同隊玩家)。

(2)一些場景中固定不動的同步對象,比如拾取物Pickup,房屋門窗(因有破壞性所以需同步)都會在每幀計算相關性,而客戶端玩家在有限時間內不太可能有較大範圍的空間移動。

以上,如能利用起大世界的空間相關性,就可以進一步優化網路同步的發端演算法。

一種想法是將大世界網格化,每一個需要同步的Actor都會落在其中一個格子里,那麼只要規劃好每個格子的大小,就只需要同步玩家周圍的九個格子裡面的Actor即可,如下圖所示橙色區域所示,還要包含玩家藍色太陽自身所在的格子,形成九宮格:

這樣對於靜態物體,比如拾取物pickup,就不再需要每幀計算相關性了,只要有玩家當前所在位置,周圍九宮格內(含自身)的Actor都是需要同步的。

對於動態物體,比如其他玩家,他們的位置每幀都可能在變化,仍可空間化到具體的柵格中來,只需要每幀更新玩家所在的柵格即可。

所以對一個客戶端玩家來言,每幀需要做兩件同步相關的事情:

(1)根據位置拉取它周圍九宮格內的同步對象

(2)自身位置變換時需更新所在柵格,要保證其他客戶端連接能及時地同步自己。

上述演算法已經考慮到了空間相關性,但每個格子劃分多大也是要講究的,劃分太大則同步對象太多,CPU負擔降不下來,劃分太小又可能因為同步不及時帶來體驗上的問題(eg:被一個看不見的敵人殺掉)。

對於Pickup來說,因為大多集中在室內,所以同步距離可以少一些,但對於玩家來說,同步距離則要大到視距,這樣格子劃分的大小很難統一。保守做法就是統一按最大視距來劃分格子,但這樣優化量就很少。另一種做法是分門別類,根據類型劃規不同大小的柵格,然後分開處理不同類型對象的同步,缺點就是存在多套柵格系統,是一種以空間換時間的做法。

UE4的ReplicatonGraph解決了上面的問題,相較於上述將一個對象放置於一個柵格中,轉而設置這個對象的多個影響柵格,具體如下圖示:

每個對象設置了一個同步相關的CullDistance,

將之柵格化:

橙色區域為其影響的柵格:

只要客戶端玩家走進了橙色柵格中的任意一個,都會收到這個太陽對象的同步信息。以下示例:

這個方案的優點是可以根據實際需要設置不同的CullDistance,同時保證了柵格自身大小可以固定不變。而且一般地,相同類型對象的CullDistance是相同的,即只要把CullDistance這個變數配置到類Class身上即可。

這樣Class配置會很多嗎?當父類Class與子類Class的關鍵同步屬性相同時,只會考慮父類Class,所以不必擔心。

同樣分析下靜態/動態物體的空間化同步,對於靜態物體,位置總是固定的,因此不需要實時計算影響柵格,對於動態物體,因為空間相關性,短時間內柵格變化的概率也比較低,即使有變化,也只是少數幾個,不必要每幀都新算一遍影響柵格,大家可以研究下UE4是如何增量計算柵格變化的。

以上是需要空間化的Class,還有一些諸如總是同步的對象,僅與某個客戶端連接相關(或小隊客戶端連接相關)的對象,UE4都是分開處理的,具體大家可以參考官方示例ShooterGame裡面的ReplicationGraph配置。

總結一下,採用ReplicationGraph充分利用了大世界Actor雖然多但卻具有空間相關性的特性,並且考慮不同類型Actor的特性(總是相關的,與特定連接相關的,以及空間化相關的),有效減少了不必要的相關性計算,從而有效節省伺服器CPU的負載。


推薦閱讀:
相关文章