什麼是網絡同步
多人遊戲裡面需要把某個玩家操作的結果通知給其玩家,這個通知的過程就是同步,再放到區域網路或者廣域網中進行,就是網絡同步了。
中介轉發
中介轉發就是用戶端/伺服器模式(C/S),添加一個伺服器作為中介節點,每台用戶端隻與伺服器建立連接配接,用戶端與用戶端彼此獨立,伺服器負責轉發消息。這種架構的複雜度僅為O(N),維護起來也友善。
幀同步與狀态同步
幀同步是伺服器将某個玩家的輸入直接轉發給其他玩家,自己不做處理。理論上所有用戶端都以相同的初始狀态開始,隻要收到的輸入相同,那麼每時每刻的狀态都會是相同的。
狀态同步是伺服器隻同步影響遊戲功能的某些重要狀态變量,并且這些重要變量是在伺服器運算出來的或者至少校驗過的,用戶端拿到這些狀态變量後自行做本地的表現。
一般來說幀同步在實時性、節省流量方面比較好,狀态同步則在安全性角度來說更勝一籌。具體選用哪種方案由具體遊戲類型來決定,UE是在射擊遊戲基礎上發展而來的,它預設的網絡同步方案是狀态同步,把決策權放在伺服器上做,可以有效減少外挂,對于中途加入/斷線重連也能天然支援。
使用UDP還是TCP
在UDP的基礎上融合了TCP的優點,例如加入了亂序處理,以及對reliable的包丢失重傳。可謂是各取所優,既保證了連接配接速度,也保證了可靠性。
Reliable,可靠性
- Reliable,不會丢失,立刻發出,适合重要的事件
- Unreliable,可能會丢失,适合表現相關的和不重要的事件
- 全部的遠端調用都使用Reliable,可能會造成網絡擁堵
- 盡量避免在循環裡面進行遠端調用和勾選Reliable選項
- 丢失重傳:reliable的包在發端會儲存一個備份,隻有收到收端傳回的Ack包确認後才會清掉,若收到的Ack包跳序則會觸發重傳。
- Packet亂序整理:因網絡鍊路複雜性,到達包順序可能與發端不一緻,這時會進行整理,對于分包導緻的不完整的部分(Partial)包也會等待重組。
如何同步重要狀态變量
需要同步的重要狀态變量都存在于Actor這個容器裡。
Game Modes
Game Modes 的任務是定義和實作規則。
AGameModeBase
AGameModeBase
包含大量可覆寫的基礎功能
函數/事件 | 目的 |
PreLogin | 接受或拒絕嘗試加入伺服器的玩家。如它将 設為一個非空字元串,會導緻 函數失敗。 在 前調用,Login 調用前可能需要大量時間,加入的玩家需要下載下傳遊戲内容時尤其如此。 |
PostLogin | 成功登入後調用。這是首個在 上安全調用複制函數之處。 可在藍圖中實作,以添加額外的邏輯。 |
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 作為引用,用于了解遊戲的一般狀态。
伺服器和用戶端的連接配接過程如下
- 用戶端發送連接配接請求。
- 如果伺服器接受連接配接,則發送目前地圖。
- 伺服器等待用戶端加載此地圖。
- 加載之後,伺服器将在本地調用 AGameModeBase::PreLogin。這樣可以使 GameMode 有機會拒絕連接配接
- 如果接受連接配接,伺服器将調用 AGameModeBase::Login。該函數的作用是建立一個 PlayerController,可用于在今後複制到新連接配接的用戶端。成功接收後,這個 PlayerController 将替代用戶端的臨時 PlayerController (之前被用作連接配接過程中的占位符)。此時将調用 APlayerController::BeginPlay。應當注意的是,在此 actor 上調用 RPC 函數尚存在安全風險。您應當等待 AGameModeBase::PostLogin 被調用完成
- 如果一切順利,AGameModeBase::PostLogin 将被調用。這時,可以放心的讓伺服器在此 PlayerController 上開始調用 RPC 函數。
越好的網絡環境和網絡模型,用戶端的遊戲狀态會越接近伺服器。
用戶端之間是沒有直接連接配接的,必須通過伺服器來進行用戶端之間的互動,即:如果沒有伺服器告知,用戶端之間是不知道互相之間的存在的。
遊戲資訊隻準從伺服器向用戶端同步,用戶端不能向伺服器同步,就算用戶端發資訊給伺服器,伺服器也當成垃圾丢掉。
用戶端向伺服器發資訊的方式隻有調用RPC中的Server函數一種形式。
幾種同步方式
複制是指在網絡會話中的不同機器間複制遊戲狀态資訊。若正确設定複制,将可同步不同機器的遊戲執行個體。
1. Actor Replication
大多數 actor 複制操作都發生在
UNetDriver::ServerReplicateActors
内。在這裡,伺服器将收集所有與各個用戶端相關的 actor,并對(已連接配接的)用戶端發送自上次更新後出現變化的所有屬性。
預設情況下,UE4不知道是否該對一個Actor執行複制操作,我們需要将Actor::bReplicates變量設定為true。