UE5网络(一)—架构篇
🤯

UE5网络(一)—架构篇

Tags
UE5
C++
网络
Published
August 10, 2023
Author
Think-01

前言

其实官方文档对用网络这块写的已经比较明白,但是我还是逼逼叨几句,用于加深印象。

架构

虚幻引擎使用标准的服务器-客户端架构。这意味着服务器是权威的,网络中的一台计算机作为 服务器 主持多人游戏会话,而所有其他玩家的计算机作为 客户端 连接到该服务器。然后,服务器与连接的客户端分享游戏状态信息,并提供一种客户端之间通信的方法。 每一个客户端会远程控制服务器的Pawn,发送过程调用以使其执行游戏操作(比如移动,攻击等)。但是服务器不会将游戏画面等视觉效果直接流送到客户端,服务器会通过复制,将场景上存在的各个Actor的状态同步到客户端,包括各个Actor的行为(理解会为类的函数调用),以及不同的值(理解为类的属于值)。然后客户端使用这些信息,对服务器上正在发生的情况进行高度模拟。
举例来说:
  1. 当你在吃鸡(Steam游戏:绝地求生)按下WSAD移动操作时,您不会自己移动角色,而是告诉服务器您想要移动它。然后,服务器会为其他人(包括你自身)更新角色的变换(移动,缩放,旋转)。
  1. 当你按下互动键F打开一个宝箱的时候,服务器会将宝箱的开关状态复制到客户端,客户端在对这个状态进行模拟。
还有一点需要强调的是,不同于其他服务器-客户端架构的代码,这里服务器和客户端的代码是写在一起的,没有单独的服务器代码,一次开发,对同样的代码分别进行服务器和客户端的打包,就能得到服务器和客户端的可执行程序。

服务器-客户端

虚幻中能进行通信的GamePlay类大部分都是继承自Actor的,可以这么说,所有的通信都是建立在Actor上的。UObject也是可以复制,你将他放在Actor里面,把Actor当一个容器即可。各种组件就是最好的例子。
根据Gameplay中关于虚幻引擎常用Actor类信息,将它们分为四类:
  • Server Only -只有服务器有的Actor
  • Server & Clients - 服务器和所有的客户端都有的Actor
  • Server & Owning Client - 有的Actor仅存在于服务器和Owning Client的客户端
  • Owning Client Only - 有的Actor仅存在于Owning Client的客户端
下面两张图展示了一些常见的类以及它们存在于哪个类别中。
notion image
下图演示了具有两个客户端和一个专用服务器各自的Actor。
notion image

理解Server、Client与Owning Client的关系与区别

在UE(Unreal Engine)网络中,Server(服务器)、Client(客户端)以及Owning Client(拥有客户端)是三个至关重要的概念。理解它们之间的关系和区别,对于深入了解游戏架构至关重要。本文将通过一个简单的示例,帮助大家轻松理解这三者之间的关系。

场景设定

假设我们有三台电脑:
  • 电脑1: 玩家1P操作
  • 电脑2: 玩家 2P操作
  • 电脑3: 专用服务器 DS
每台电脑只运行一个程序,且DS上运行的是游戏服务端程序。

GameMode

GameMode是游戏模式的核心Actor,它只存在于服务器(DS)上。这意味着在上述场景中,只有DS上有一个GameMode实例。

客户端独有

客户端(Client)独有的元素主要包括UI,例如AHUD、UserWidget以及LocalPlayer。在我们的例子中,存在两个客户端,因此有两个LocalPlayer两个AHUD以及若干UserWidget实例(取决于客户端创建的数量)。

三端共享的Actor

有些Actor在服务器和客户端上都存在,例如GameState、PlayerState和Pawn。
  • GameState: 每个电脑都拥有一个GameState实例,因此总共有三个GameState实例。
  • Pawn: 由于有两个玩家连接到服务器,服务器上会存在两个Pawn实例。1P的电脑上除了1P自身控制的Pawn外,还有2P的Pawn实例,因此1P电脑上有两个Pawn实例。同理,2P电脑上也有两个Pawn实例。所以,三台电脑总共有六个Pawn实例。

Owning Client

在1P电脑上的两个Pawn实例中,虽然都属于Client,但只有属于1P的角色才能被1P直接控制。Owning Client的概念就是用来解释哪个Pawn实例可以被玩家直接操作

PlayerController

在UE(Unreal Engine)的网络架构中,PlayerController负责控制Pawn和PlayerState。PlayerController在客户端中只属于Owning Client,每个Owning Client拥有一个PlayerController实例。同时,服务器也会为每个连接的玩家创建一个PlayerController代理。因此,在我们的例子中,服务器上有两个PlayerController代理,1P和2P的电脑上各有一个PlayerController实例
需要特别强调的是,在1P的电脑上,虽然存在2P的Pawn实例,但是1P无法通过2P的PlayerController去操作2P的角色。因为PlayerController只属于Owning Client,每个玩家只能通过自己的PlayerController来控制自己的角色。
 

void AController::Possess(APawn* InPawn) { ... OnPossess(InPawn); ... } void AController::OnPossess(APawn* InPawn) { ... SetPawn(InPawn); ... }

理解Server、Client与Owning Client的好处

  • RPC 需要确定哪个客户端将执行运行于客户端的 RPC
  • Actor 复制与连接相关性
  • 在涉及所有者时的 Actor 属性复制条件
在Pawn中,可使用C++中的 IsLocallyControlled 函数,或蓝图中的 Is Locally Controlled 节点,以判断Pawn是否在其拥有客户端上。

服务器类型

为啥服务器还有类型?如果你玩过联机游戏你就晓得,比如有的游戏可以在自己的电脑上开服,你的小伙伴直接链接你自己电脑上的服务器就可以一起玩,比如经典生存游戏—-饥荒.这种在虚幻这边叫做监听服务器.而我们常玩的大部分游戏都是专用服务器,你可以在任何时候上线,因为专用服务器基本不会宕机.而如果你玩的是饥荒这种游戏,你断网了你的小伙伴就会连接不上,所有肝的游戏进度全没了.

专用服务器

专用服务器与游戏客户端分开运行,主要用于运行服务器,玩家可以随时加入/离开,而服务器不会随之关闭。
专用服务器可以针对 Windows 和 Linux 进行编译,并且可以在玩家可以通过固定 IP 地址连接到的云服务器上运行。
专用服务器没有视觉部分,因此它们不需要 UI,也没有 PlayerController。他们在游戏中也没有代表他们的角色或类似角色。

监听服务器

侦听服务器是服务器,也是客户端。
由于也是客户端,Listen-Server 需要 UI 并有一个 PlayerController,它代表客户端部分。在监听服务器上获取“PlayerController(0)”将返回该客户端的 PlayerController。
由于Listen-Server运行在客户端本身,所以其他人需要连接的IP就是客户端之一。与专用服务器相比,这通常会带来玩家没有静态 IP 的问题。
然而,使用OnlineSubsystem(后面章节解释),可以解决IP变化的问题。

Actor的网络角色和授权

在服务器-客户端的架构下,每个Actor除了根据其在服务器和客户端的存在情况,还有其他的分类方式,那就是是根据其扮演的角色和拥有的权限(也就是是授权)来区分。
Actor的 角色 将决定网络游戏期间控制Actor的机器。授权 Actor被认为可控制Actor的状态,并可将信息复制到网络多人游戏会话中的其他机器上。授权Actor就是服务器生成的,由服务器主导和控制的Actor。
远程代理 是该Actor在远程机器上的副本,其将接收授权Actor中的复制信息。这里的远程是以服务器为视角的,指客户端上的代理Actor。远程代理分为两种,一种是自主代理,这种一般是玩家自己的Pawn,也就是Owning Client的Actor。一种是模拟代理,这种指的是其他玩家控制的Pawn,以及服务器复制过来的所有不是Owning Client的Actor。
其由 Local Role 和 Remote Role 变量进行追踪,可取以下值:
网络角色
说明
Actor在网络游戏中无角色,不会复制。只存在于本地机器中
授权
Actor为授权状态,会将其信息复制到其他机器上的远程代理。
模拟代理
Actor为远程代理,由另一台机器上的授权Actor完全控制。网络游戏中如拾取物、发射物或交互对象等多数Actor将在远程客户端上显示为模拟代理。
自主代理
Actor为远程代理,能够本地执行部分功能,但会接收授权Actor中的矫正。自主代理通常为玩家直接控制的Actor所保留,如Pawn。
总结,只存在于客户端的Actor,比如UI
同时存在于服务器和客户端的Actor。在服务器上角色为授权(Local Role),在客户端为代理(Remote Role)。在客户端上有Owning Client的为自主代理,没有Owning Client的为模拟代理。
网络角色枚举:
/** The network role of an actor on a local/remote network context */ UENUM() enum ENetRole { /** No role at all. */ ROLE_None, /** Locally simulated proxy of this actor. */ ROLE_SimulatedProxy, /** Locally autonomous proxy of this actor. */ ROLE_AutonomousProxy, /** Authoritative control over the actor. */ ROLE_Authority, ROLE_MAX, };

UE网络编程的方向和策略

对于UE网络的架构来说,服务器是真正运行游戏的地方,而玩家游玩的和见到的是客户端场景。所以如何根据服务器复制同步过来的信息模拟好客户端的场景,就是UE网络编程中最为重要的事。