天天看點

UE4網絡同步什麼是網絡同步中介轉發幀同步與狀态同步使用UDP還是TCPReliable,可靠性如何同步重要狀态變量Game ModesAGameModeBasePawnPlayerController(玩家控制器)RPC伺服器與用戶端幾種同步方式

什麼是網絡同步

多人遊戲裡面需要把某個玩家操作的結果通知給其玩家,這個通知的過程就是同步,再放到區域網路或者廣域網中進行,就是網絡同步了。

中介轉發

中介轉發就是用戶端/伺服器模式(C/S),添加一個伺服器作為中介節點,每台用戶端隻與伺服器建立連接配接,用戶端與用戶端彼此獨立,伺服器負責轉發消息。這種架構的複雜度僅為O(N),維護起來也友善。

幀同步與狀态同步

幀同步是伺服器将某個玩家的輸入直接轉發給其他玩家,自己不做處理。理論上所有用戶端都以相同的初始狀态開始,隻要收到的輸入相同,那麼每時每刻的狀态都會是相同的。

狀态同步是伺服器隻同步影響遊戲功能的某些重要狀态變量,并且這些重要變量是在伺服器運算出來的或者至少校驗過的,用戶端拿到這些狀态變量後自行做本地的表現。

一般來說幀同步在實時性、節省流量方面比較好,狀态同步則在安全性角度來說更勝一籌。具體選用哪種方案由具體遊戲類型來決定,UE是在射擊遊戲基礎上發展而來的,它預設的網絡同步方案是狀态同步,把決策權放在伺服器上做,可以有效減少外挂,對于中途加入/斷線重連也能天然支援。

使用UDP還是TCP

在UDP的基礎上融合了TCP的優點,例如加入了亂序處理,以及對reliable的包丢失重傳。可謂是各取所優,既保證了連接配接速度,也保證了可靠性。

Reliable,可靠性

  1. Reliable,不會丢失,立刻發出,适合重要的事件
  2. Unreliable,可能會丢失,适合表現相關的和不重要的事件
  3. 全部的遠端調用都使用Reliable,可能會造成網絡擁堵
  4. 盡量避免在循環裡面進行遠端調用和勾選Reliable選項
  • 丢失重傳:reliable的包在發端會儲存一個備份,隻有收到收端傳回的Ack包确認後才會清掉,若收到的Ack包跳序則會觸發重傳。
  • Packet亂序整理:因網絡鍊路複雜性,到達包順序可能與發端不一緻,這時會進行整理,對于分包導緻的不完整的部分(Partial)包也會等待重組。

如何同步重要狀态變量

需要同步的重要狀态變量都存在于Actor這個容器裡。

Game Modes

Game Modes 的任務是定義和實作規則。

AGameModeBase

AGameModeBase

 包含大量可覆寫的基礎功能

函數/事件 目的
PreLogin 接受或拒絕嘗試加入伺服器的玩家。如它将 

ErrorMessage

 設為一個非空字元串,會導緻 

Login

 函數失敗。

PreLogin

 在 

Login

 前調用,Login 調用前可能需要大量時間,加入的玩家需要下載下傳遊戲内容時尤其如此。
PostLogin 成功登入後調用。這是首個在 

PlayerController

 上安全調用複制函數之處。

OnPostLogin

 可在藍圖中實作,以添加額外的邏輯。

Pawn

Pawn類是一個代表你或者代表電腦的人工智能的遊戲對象,它是可以在螢幕上控制的遊戲對象。Pawn 是玩家或 AI 實體在遊戲場景中的具化展現。如果它是被玩家控制的,我們通常稱之為controller(控制器);如果它是被人工智能腳本控制的,我們通常稱之為AI(Artificial Intelligence,人工智能),那些NPC(Non-player Characters,非玩家角色)就通常具有AI行為。

預設情況下,控制器(Controllers)和 Pawn 之間是一對一的關系;也就是說,每個控制器在某個時間點隻能控制一個 Pawn。此外,在遊戲期間生成的 Pawn 不會被控制器自動控制。

PlayerController(玩家控制器)

PlayerController(玩家控制器) 是Pawn和控制它的人類玩家間的接口。PlayerController本質上代表了人類玩家的意願。

當設定PlayerController時,需要考慮的一個事情就是想在PlayerController中包含哪些功能及内容。可以在 Pawn 中處理所有輸入, 尤其是不太複雜的情況下。但是,如果需求非常複雜,比如在一個遊戲用戶端上的多玩家、或實時地動态修改角色的功能,那麼最好 PlayerController中處理輸入。在這種情況中,PlayerController決定要幹什麼,然後将指令(比如"開始蹲伏"、"跳躍")釋出給Pawn。

同時,某些情況下,則必須把輸入處理或其他功能放到PlayerController中。PlayerController在整個遊戲在過程中都是一直存在的,但是Pawn可能是臨時存在的。 比如,在死亡競技模式的遊戲中,您可能死了又重生,是以您将獲得一個新的Pawn,但是您的PlayerController都是一樣的。在這個示例中,如果您将分數儲存到您的Pawn上, 那麼分數将會重置,但是如果您将分數儲存到PlayerController上,它将不會重置。

RPC

RPC (遠端過程調用)是在本地調用但在其他機器(不同于執行調用的機器)上遠端執行的函數。

RPC 函數非常有用,可允許用戶端或伺服器通過網絡連接配接互相發送消息。

這些功能的主要作用是執行那些不可靠的暫時性/修飾性遊戲事件。這其中包括播放聲音、生成粒子或産生其他臨時效果 之類的事件,它們對于 Actor 的正常運作并不重要。在此之前,這些類型的事件往往要通過 Actor 屬性進行複制。

伺服器與用戶端

UE4有兩種伺服器:

  • Listen Server:友善進行區域網路本地遊戲,在本地機器上搭建伺服器,此時本地機器既是伺服器又是用戶端。其接受遠端用戶端中的連接配接,且直接在伺服器上擁有本地玩家。此模式通常用于臨時合作和競技多人遊戲。
  • Dedicated Server:獨立伺服器,在獨立伺服器上則不執行渲染任務,隻承擔伺服器的相關職責。其接受遠端用戶端中的連接配接,但無本地玩家,是以為了高效運作,其将廢棄圖形、音效、輸入和其他面向玩家的功能。此模式常用于需要更固定、安全和大型多人功能的遊戲。此類遊戲包括MMO、競技MOBA,或快節奏網絡射擊遊戲。

伺服器是多人遊戲實際發生的地方。用戶端會遠端控制其在伺服器上各自擁有的 Pawn,發送過程調用以使其執行遊戲操作。但伺服器不會将視覺效果直接流送至用戶端顯示器。伺服器會将遊戲狀态資訊複制到各用戶端,告知應存在的Actor、此類Actor的行為,以及不同變量應擁有的值。然後各用戶端使用此資訊,對伺服器上正在發生的情況進行高度模拟。

伺服器需要選擇性地向各用戶端發送資訊,遊戲程式設計時須指定要複制的資訊和接收副本的機器。主要的難點在于選擇應複制的資訊及方式,以向所有玩家提供一緻的遊戲體驗,同時需最小化資訊複制量,盡可能減少網絡帶寬占用率。

遊戲狀态和流程一般是通過 GameMode 這一 actor 來驅動。隻有伺服器才包含此 actor 的有效複本(用戶端不包含複本)。要向用戶端傳達該狀态,可以使用 GameState actor 顯示 GameMode actor 的重要狀态。這個 GameState actor 被标記為複制到每個用戶端。用戶端将包含此 GameState actor 的一個近似複本,而且能使用這個 actor 作為引用,用于了解遊戲的一般狀态。

伺服器和用戶端的連接配接過程如下

  1. 用戶端發送連接配接請求。
  2. 如果伺服器接受連接配接,則發送目前地圖。
  3. 伺服器等待用戶端加載此地圖。
  4. 加載之後,伺服器将在本地調用 AGameModeBase::PreLogin。這樣可以使 GameMode 有機會拒絕連接配接
  5. 如果接受連接配接,伺服器将調用 AGameModeBase::Login。該函數的作用是建立一個 PlayerController,可用于在今後複制到新連接配接的用戶端。成功接收後,這個 PlayerController 将替代用戶端的臨時 PlayerController (之前被用作連接配接過程中的占位符)。此時将調用 APlayerController::BeginPlay。應當注意的是,在此 actor 上調用 RPC 函數尚存在安全風險。您應當等待 AGameModeBase::PostLogin 被調用完成
  6. 如果一切順利,AGameModeBase::PostLogin 将被調用。這時,可以放心的讓伺服器在此 PlayerController 上開始調用 RPC 函數。

越好的網絡環境和網絡模型,用戶端的遊戲狀态會越接近伺服器。

用戶端之間是沒有直接連接配接的,必須通過伺服器來進行用戶端之間的互動,即:如果沒有伺服器告知,用戶端之間是不知道互相之間的存在的。

遊戲資訊隻準從伺服器向用戶端同步,用戶端不能向伺服器同步,就算用戶端發資訊給伺服器,伺服器也當成垃圾丢掉。

用戶端向伺服器發資訊的方式隻有調用RPC中的Server函數一種形式。

幾種同步方式

複制是指在網絡會話中的不同機器間複制遊戲狀态資訊。若正确設定複制,将可同步不同機器的遊戲執行個體。

1. Actor Replication

大多數 actor 複制操作都發生在 

UNetDriver::ServerReplicateActors

 内。在這裡,伺服器将收集所有與各個用戶端相關的 actor,并對(已連接配接的)用戶端發送自上次更新後出現變化的所有屬性。

預設情況下,UE4不知道是否該對一個Actor執行複制操作,我們需要将Actor::bReplicates變量設定為true。

繼續閱讀