网络概述

设置多人游戏的联网游戏。

Choose your operating system:

Windows

macOS

Linux

在多人游戏会话中,游戏状态信息将通过互联网连接在多台机器之间通信,而非单独驻留于一台计算机上。玩家之间的信息共享十分微妙,并会增加部分额外步骤,因此此操作导致多人游戏编程比单人游戏编辑复杂。 虚幻引擎 提供的网络框架非常强大,支持部分世界上最流行的网络游戏,可简化此流程。本页对驱动多人游戏编程的概念和可使用的网络游戏构建工具进行了概述。

尽早规划多人游戏

若项目可能需要多人游戏功能,则从项目开始阶段起,构建所有gameplay时都应将多人游戏功能考虑在内。若开发团队通常会在创建多人游戏时实施额外步骤,相较于单人游戏,构建gameplay的流程并不会耗时过久。长远来看,项目将便于整个团队进行调试和维护。同时,虚幻引擎中编写的多人游戏gameplay仍可在单人游戏中使用。

但是,重构无网络情况下编译的基本代码需要梳理整个项目,几乎所有gameplay都需要重新编写。届时,开发团队成员需重新学习可能早已熟悉的编程实操。同时,网速和稳定的相关技术瓶颈也会让你措手不及。

相较于初期规划,在项目后期引入网络功能会占用大量资源,且极为复杂。因此,除非确定项目无需多人游戏功能,否则应 始终 按多人游戏方向进行编程。

客户端-服务器模型

在单人游戏或本地多人游戏中,游戏在 独立 游戏上本地运行。玩家将输入连接到一台计算机,直接控制其上所有内容,而包括Actor、场景和各玩家的用户界面在内的所有游戏项目均存在于这台本地机器上。

本地运行范例

单人游戏和本地多人游戏都仅在一台机器上执行。

在网络多人游戏中,虚幻引擎使用 客户端-服务器 模型。网络中的一台计算机作为 服务器 主持多人游戏会话,而所有其他玩家的计算机作为 客户端 连接到该服务器。然后,服务器与连接的客户端分享游戏状态信息,并提供一种客户端之间通信的方法。

网络运行范例

在网络多人游戏中,游戏将在服务器(1)与多个与之连接的客户端(2)之间进行。服务器处理gameplay,客户端向用户显示游戏。

服务器作为游戏主机,保留一个真实 授权 的游戏状态。换句话说,服务器是多人游戏实际发生的地方。客户端会远程控制其在服务器上各自拥有的 Pawn ,发送过程调用以使其执行游戏操作。但服务器不会将视觉效果直接流送至客户端显示器。服务器会将游戏状态信息 复制 到各客户端,告知应存在的Actor、此类Actor的行为,以及不同变量应拥有的值。然后各客户端使用此信息,对服务器上正在发生的情况进行高度模拟。

欲了解客户端与服务器之间的连接过程的相关额外技术信息,参见 客户端-服务器模型 上的指南。

客户端-服务器游戏范例

我们将在多人游戏中以两个玩家为例,说明此过程对gameplay编程实践的改变方式。我们称他们为 玩家1 玩家2 ,并分析两人互射发射物的过程。

本地游戏

网络游戏

本地运行范例

网络运行范例2

玩家1按下输入以发射武器。

  • 玩家1的Pawn将发射其当前武器以响应此操作。

  • 玩家1的武器生成发射物,并播放附带音效和视觉效果。

玩家1在本地机器上按下输入以发射武器。

  • 玩家1的本地Pawn将武器发射命令传送给服务器上对应的Pawn。

  • 玩家1在服务器上的武器生成发射物。

  • 服务器告知所有连接的客户端各自生成玩家1发射物的副本。

  • 玩家1在服务器上的武器告知所有客户端播放武器发射音效和视觉效果。

玩家1的发射物从武器中射出并前移。

玩家1的发射物从在服务器上的武器中射出并前移。

  • 此时,服务器告知所有客户端复制玩家1发射物发生的移动,因此各客户端上的玩家1发射物便相应移动。

玩家1的发射物撞击玩家2的Pawn。

  • 碰撞将触发摧毁玩家1发射物的函数,对玩家2的Pawn造成伤害,并播放附带音效和视觉效果。

  • 玩家2播放画面效果,作为对伤害的响应。

玩家1在服务器上的发射物撞击玩家2的Pawn。

  • 碰撞触发摧毁服务器上玩家1发射物的函数。

  • 服务器自动告知所有客户端各自摧毁玩家1发射物副本。

  • 碰撞触发告知所有客户端播放附带碰撞音效和视觉效果的函数。

  • 玩家2在服务器上的Pawn承受发射物碰撞造成的伤害。

  • 玩家2在服务器上的Pawn告知玩家2客户端播放画面效果,作为对伤害的响应。

在独立游戏中,此类所有交互都在同一台机器上的同一 场景 中发生,因此理解和编程均较为简单。例如,生成对象时,你可以认为所有玩家均能看到此对象。

在网络游戏中,此类交互发生在多个不同场景:服务器上的场景、玩家1客户端的场景、玩家2客户端的场景,以及参与会话的其他所有客户端的额外场景。每台不同机器上的各场景均有各自的Pawn、武器及发射物的副本。服务器是游戏真正运行的地方,但我们要让客户端的场景看似发生了相同事件。因此,需要选择性地向各客户端发送信息,以在服务器上创建场景的视觉代表。

这一过程将在基础游戏交互(碰撞、移动、伤害)、美化效果(视觉效果和音效)和私人玩家信息(HUD更新)间进行划分。这三者各自与网络中的特定机器或机组关联。但是,此信息的复制过程并非完全自动,游戏编程时须指定要复制的信息和接收副本的机器。主要的难点在于选择应复制的信息及方式,以向所有玩家提供一致的游戏体验,同时需最小化信息复制量,尽可能减少网络带宽占用率。

基本网络概念

以下章节将详细介绍虚幻引擎内驱动网络gameplay的概念。其中包括多种工具的概述和快速参考,以帮助协助构建多人游戏。

网络模式和服务器类型

网络模式 描述了计算机与网络多人游戏会话的关系。游戏实例可采用以下任意网络模式:

网络模式

说明

独立

游戏作为服务器运行,不接受远程客户端连接。参与游戏的玩家必须为本地玩家。此模式用于单人游戏和本地多人游戏。其将运行本地玩家适用的服务器逻辑和客户端逻辑。

客户端

游戏作为网络多人游戏会话中与服务器连接的客户端运行。其不会运行服务器逻辑。

聆听服务器

游戏作为主持网络多人游戏会话的服务器运行。其接受远程客户端中的连接,且直接在服务器上拥有本地玩家。此模式通常用于临时合作和竞技多人游戏。

专属服务器

游戏作为主持网络多人游戏会话的服务器运行。其接受远程客户端中的连接,但无本地玩家,因此为了高效运行,其将废弃图形、音效、输入和其他面向玩家的功能。此模式常用于需要更固定、安全和大型多人功能的游戏。

拥有游戏副本的用户均可启动聆听服务器并在同一计算机上运行,因此聆听服务器对于用户而言较易自发设置。支持聆听服务器的游戏通常拥有游戏UI,用于启动服务器或搜索要加入的服务器。但由于主持聆听服务器的玩家会直接在服务器上游戏,因此比其他必须使用网络连接方可游戏的玩家更具优势,于是便导致了公平和作弊问题。同时,要作为服务器运行,还需支持如图形和音效等玩家相关系统,从而引发额外处理负载。此类因素导致聆听服务器不适用于激烈的竞技或网络负载极高的游戏,但对于小型玩家群体间进行临时合作和竞技多人游戏而言,却十分好用。

专属服务器成本更高,更难以配置,需要独立于所有参与玩家的计算机,并需要完成自身网络连接。但所有加入专属服务器的玩家均使用相同类型的连接进行游戏,从而保证了公平性。由于专属服务器不会渲染图形或执行仅与本地玩家相关的其它逻辑,因此还可高效处理gameplay和网络。因此,出于安全、公平或可靠方面的原因,专属服务器更适用于需要大量玩家或需要高效执行、可信服务器的游戏。此类游戏包括MMO、竞技MOBA,或快节奏网络射击游戏。

因为独立游戏服务器可同时作为服务器和客户端,为多人游戏创建的逻辑可在无需额外工作的情况下,在单人游戏中运行。

Actor复制

复制是指在网络会话中的不同机器间复制游戏状态信息。若正确设置复制,将可同步不同机器的游戏实例。多数Actor默认不会启用复制,且将本地执行所有功能。在C++ Actor类中设置 bReplicates 变量,或将Actor蓝图的 复制(Replicates) 设置设为 true ,可启用给定类的Actor复制。

以下为创建网络游戏时的常见复制功能:

复制功能

说明

创建和销毁

服务器上生成复制Actor的授权版本时,其会在所有连接客户端上自动生成远程代理。其之后会将信息复制到这些远程代理。若销毁授权Actor,则将自动销毁所有连接客户端上的远程代理。

移动复制

若授权Actor启用了 复制移动 ,或将C++中的 bReplicateMovement 设为 true ,其将自动复制位置、旋转和速度。

变量复制

在指定为复制变量的值变更时,其将自动从授权Actor复制到其远程代理。

组件复制

Actor组件复制为其所属Actor的一部分。组件内指定为复制变量将复制,而组件内调用的RPC将与Actor类中调用的RPC保持一致。

远程过程调用(RPC)

RPC是传输到网络游戏中特定机器的特殊函数。无论初始调用RPC的是哪台机器,其的实现仅在目标机器上运行。此类RPC可指定为服务器(仅在服务器上运行)、客户端(仅在Actor的拥有客户端上运行)或NetMulticast(在连接会话的所有机器上运行,包括服务器)。

虽然创建、销毁和移动等常见使用可自动处理,但即使启用复制,其他所有gameplay功能也不会默认自动复制。必须根据游戏的需求明确指定要复制的变量和函数。欲了解上述所有复制功能的详情,参见 Actor复制 指南。

Actor、Pawn和角色的部分常用功能不会复制:

  • 骨架网格体 静态网格体 组件

  • 材质

  • 动画蓝图

  • 粒子系统

  • 音效发射器

  • 物理对象

此类项目均在所有客户端上单独运行。但是,若复制驱动此类视觉元素的变量,则可确保所有客户端都具有相同信息,从而以大致相同的方式进行模拟。

网络角色和授权

Actor的 网络角色 将决定网络游戏期间控制Actor的机器。 授权 Actor被认为可控制Actor的状态,并可将信息复制到网络多人游戏会话中的其他机器上。 远程代理 是该Actor在远程机器上的副本,其将接收授权Actor中的复制信息。其由 Local Role Remote Role 变量进行追踪,可取以下值:

网络角色

说明

Actor在网络游戏中无角色,不会复制。

授权

Actor为授权状态,会将其信息复制到其他机器上的远程代理。

模拟代理

Actor为远程代理,由另一台机器上的授权Actor完全控制。网络游戏中如拾取物、发射物或交互对象等多数Actor将在远程客户端上显示为模拟代理。

自主代理

Actor为远程代理,能够本地执行部分功能,但会接收授权Actor中的矫正。自主代理通常为玩家直接控制的actor所保留,如pawn。

虚幻引擎使用的默认模型是 服务器授权 ,意味着服务器对游戏状态固定具有权限,而信息固定从服务器复制到客户端。服务器上的Actor应具有授权的本地角色,而其在远程客户端上的对应Actor应具有模拟或自主代理的本地角色。

欲了解Actor网络角色,参见 Actor角色和远程角色 指南。

客户端拥有权

特定客户端机器上的 PlayerController 拥有网络游戏中的pawn。Pawn调用纯客户端函数时,其将无视调用函数的机器,而仅指向拥有玩家的机器。若将Actor的 Owner 变量设为特定Pawn,则通关关联,该Actor属于该Pawn的拥有客户端,并将纯客户端函数指向其拥有者的机器。可使用C++中的 IsLocallyControlled 函数,或蓝图中的 Is Locally Controlled 节点,以决定Pawn是否在其拥有客户端上。

由于构造期间Pawn可能未指定控制器,因此避免在自定义Pawn类的构造函数中使用 IsLocallyControlled

有关拥有权的详情,参见 Actor及其拥有连接 上的指南。

相关性和优先级

相关性 用于决定是否需要在多人游戏期间复制Actor。复制期间将剔除被认为不相关的actor。此操作可节约带宽,以便相关Actor可更加高效地复制。若Actor未被玩家拥有,且不在玩家附近,将其被视为不相关,而不会进行复制。不相关Actor会存在于服务器上,且会影响授权游戏状态,但在玩家靠近前不会向客户端发送信息。覆盖 IsNetRelevantFor 函数以手动控制相关性,并可使用 NetCullDistanceSquared 属性决定成为相关Actor所需距离。

有时在游戏单帧内,没有足够带宽供复制所有相关Actor。因此,Actor拥有 优先级(Priority) 值,用于决定优先复制的Actor。Pawn和PlayerController的 NetPriority 默认为 3.0 ,从而使其成为游戏中最高优先级的Actor,而基础Actor的 NetPriority 1.0 。Actor在被复制前经历的时间越久,每次成功通过时所处的优先级便越高。

欲了解Actor相关性和优先级的详情,参见 网络优先级 上的指南。

变量复制

在C++中使用对应 UPROPERTY 宏内的 Replicated ReplicateUsing 说明符,或在蓝图的细节面板中将它们指定为已复制,可将复制添加到变量和对象引用。授权Actor上复制变量的值变更时,其信息将自动从授权Actor发送到连接会话的远程代理。

RepNotify

可指定在Actor成功接收特定变量的复制信息时要调用的 RepNotify 函数。RepNotify仅在变量更新时本地触发。触发gameplay逻辑响应授权Actor上的变量更改时,使用RepNotify可减少开销。在C++中使用变量的 UPROPERTY 宏的 ReplicatedUsing 说明符可访问此功能,或修改蓝图中变量的复制设置以使用RepNotify。

由于RepNotify可添加到需复制的变量中,而无需考虑其他gameplay功能,创建额外网络调用时刻节约大量带宽,因此RepNotify比RPC或复制函数更加好用。

远程过程调用(RPC)

远程过程调用也称为复制函数。可在任何机器上进行调用,但会指示其的实现在与网络会话连接的特定机器上发生。有三种RPC:

RPC类型

说明

Server

仅在主持游戏的服务器上调用。

Client

仅在拥有该函数所属Actor的客户端上调用。若Actor无拥有连接,将不会执行此逻辑。

NetMulticast

在与服务器连接的所有客户端及服务器本身上调用。

细节面板(Details Panel) 中的 复制(Replicates) 下拉菜单设为三种可用类型之一,可为蓝图中的事件和函数指定相同的命名。

蓝图中的复制事件

提供对应 UFUNCTION 宏中的 Server Client NetMulticast 说明符,可在将C++函数指定为RPC。其代码将在代码实现中使用后缀 _Implementation

ExampleClass.h

//服务器RPC MyFunction的声明。
UFUNCTION(Server, Reliable, WithValidation)
void MyFunction(int myInt);

ExampleClass.cpp

//服务器RPC MyFunction的实现。
void AExampleClass::MyFunction_Implementation(int myInt)
{
    //游戏代码在此。
}

将函数指定为RPC后,即可向其给定游戏逻辑,并以调用其他函数的方法进行调用。欲了解RPC的更多相关详情,参见 远程过程调用 指南。

可靠性

必须将RPC指定为 可靠 不可靠 。在蓝图中,函数和事件默认为不可靠。要将函数指定为可靠,将细节面板(Details Panel)中的 可靠(Reliable) 设置设为 true 。在C++中,必须将 Reliable Unreliable 说明符作为 Server Client NetMulticast 函数,添加到RPC的 UFUNCTION 宏及其状态。

不可靠RPC无法保证必会到达预定目的地,但其发送速度和频率高于可靠的RPC。其最适用于对gameplay而言不重要或经常调用的函数。例如,由于Actor移动每帧都可能变换,因此使用不可靠RPC复制该Actor移动。

可靠的RPC保证到达预定目的地,并在成功接收之前一直保留在队列中。其最适合用于对gameplay很关键或者不经常调用的函数。相关例子包括碰撞事件、武器发射的开始或结束,或生成Actor。

滥用可靠函数可能导致其队列溢出,此操作将强制断开连接。若逐帧调用复制函数,应将其设为不可靠。若拥有与玩家输入绑定的可靠函数,应限制玩家调用该函数的频率。

验证

WithValidation 说明符表明除函数的实现外,还有可验证传入函数调用的数据的函数。此验证函数与其负责的函数使用同一签名,但其将返回布尔而非原本返回值。若返回 true ,则其允许执行RPC的 Implementation ;若返回 false ,则防止执行。

ExampleClass.cpp

//服务器RPC MyFunction的验证
bool AExampleClass::MyFunction_Validation(int myInt)
{
    /* 
        若myInt的值为负,建议不允许运行MyFunction_Implementation。 
        因此仅在myInt大于零时返回true。
    */
    return myInt >= 0;      
}

提示和深入阅读

在游戏中实现高效、稳定多人游戏系统的基本指南如下。

基本复制Actor清单

按照以下步骤,可创建复制Actor:

  • 将Actor的复制设置设为True。

  • 若复制Actor需要移动,将复制移动(Replicates Movement)设为True。

  • 生成或销毁复制Actor时,确保在服务器上执行该操作。

  • 设置必须在机器间共享的变量,以便进行复制。这通常适用于以gameplay为基础的变量。

  • 尽量使用虚幻引擎的预制移动组件,其已针对复制进行构建。

  • 若使用服务器授权模型,需确保玩家可执行的新操作均由服务器函数触发。

网络提示

  • 尽可能少用RPC或复制蓝图函数。在合适情况下改用RepNotify。

  • 组播函数会导致会话中各连接客户端的额外网络流量,需尤其少用。

  • 若能保证非复制函数仅在服务器上执行,则服务器RPC中无需包含纯服务器逻辑。

  • 将可靠RPC绑定到玩家输入时需谨慎。玩家可能会快速反复点击按钮,导致可靠RPC队列溢出。应采取措施限制玩家激活此项的频率。

  • 若游戏频繁调用RPC或复制函数,如tick时,则应将其设为不可靠。

  • 部分函数可重复使用。调用其响应游戏逻辑,然后调用其响应RepNotify,确保客户端和服务器拥有并列执行即可。

  • 检查Actor的网络角色可查看其是否为 ROLE_Authority 。此方法适用于过滤函数中的执行,该函数同时在服务器和客户端上激活。

  • 使用C++中的 IsLocallyControlled 函数或蓝图中的Is Locally Controlled函数,可检查Pawn是否受本地控制。基于执行是否与拥有客户端相关来过滤函数时,此方法十分拥有。

  • 构造期间Pawn可能未被指定控制器,因此避免在构造函数脚本中使用 IsLocallyControlled

教程

欲全面了解使用上述原则实现简单多人游戏的方法,参见 多人游戏快速入门指南 。更多教程请参见以下链接:

欢迎帮助改进虚幻引擎文档!请告诉我们该如何更好地为您服务。
填写问卷调查
取消