UE4入门学习-网络基础(一)

UE4网络基础总结(一)


一、综述

要构建多人的网络游戏,理解UE4的网络框架和原理是十分必要的。所以在这里
对UE4的网络框架做一个简单总结,一些内容来源于自己的理解,还有一些来源于对官方文档和引用文章的翻译。

UE4的网络框架与许多常见框架一样采用了 C/S的客户端 – 服务器模型架构 ,这就意味着服务器有绝对**权威(authoritative)**决定关键的行为,客户端一般将动作请求发送到服务器由服务器进行校验,然后服务器处理数据变化,再将结果同步回客户端进行展示和模拟。

1. Actor的作用

在实现网络通信和同步上,UE4操作的基本对象就是Actor, 所以参与同步的类不是直接或者间接继承自Actor C++基类AActor, 就是作为某个Actor的组成部分。

在通信机制上,UE4对Actor提供了两种基本机制,一种是同步(Replication), 一种是远程过程调用**RPCs(Remote Procedure Calls)。**同步是UE4自动管理进行的,用于频繁发生变化的属性和数据的同步;RPCs由程序主动调用,类似于函数调用,只不过调用和执行的机器并不是在同一个地方。之后会详细介绍这两种机制。

2. 网络模式Network Modes

UE4定义了几种网络模式, 都有专门的的枚举来表示,表示当前运行程序在网络同步中的角色或者作用,它们分别是:

NM_Standalone : 表示服务器运行在单机状态,不接受任何其他连接客户端的请求。可以看做是没有网络功能的单机程序,一般用于单人游戏或者本地游戏。

NM_DedicatedServer专用服务器(Dedicated Server),类似于传统中心C/S网络结构中的服务器角色,它本身不包含任何客户端。只运行服务器的逻辑,所以省略可以不运行不必要的声音,画面, 用户输入等客户端专有的处理逻辑。

NM_ListenServer :包含了本地Player的服务器模式。这个模式既包含服务器部分的功能又包含客户端部分的功能。但是由于本身也是客户端,所以在此机器上运行的逻辑和决策并不是完全可信的。一般这种模式用于几个人联机点对点的服务,比如以前的魔兽RPG的开房间模式,其中一个客户端作为服务器管理中转其他客户端的信息。

NM_Client : 纯客户端的网络模式,当处于这个模式,客户端是连接到远程的Dedicated Server专用服务器或者Listen Server上,不会运行服务器端的逻辑。

下面会详细列列举UE4网络相关的一些基础知识。

二、 UE4服务器类型

就像之前提过的,服务器运行方式分为2种类型,分别是 Listen ServerDedicated Server

1. Dedicated Server

Dedicated Server是 专用服务器 ,是作为一个 独立运行 的服务器,不需要与任何客户端绑定。

Dedicated Server与客户端分离运行,一般作为一个中心服务器让客户端加入或者离开。

它可以被编译在Windows和Linux平台运行,也可以运行在Virtual Server虚拟服务器,让客户端通过固定的IP地址连接。

Dedicated Server不需要显示部分,所以不需要UI,不需要PlayerController。也没有专属的角色在游戏中。

2. ListenServer

ListenServer是 客户端服务器端结合 的一种服务器类型,所有至少会有一个Client连接到这个服务器,当客户端关闭,服务器也会关闭。

ListenServer需要有UI和PlayerController, 可通过PlayerContoller[0]来获得。

ListenServer的IP地址就是运行客户端的地址,所以可能会产生没有固定的静态IP的问题。这个问题可以由OnlineSubsystem子系统来解决。


三、 服务器交互机制

Actor是网络交互中的 第一类对象 ,所以要进行网络信息的同步传输,首先要找到需要同步的Actor。为了使用网络功能,必须把Actor的bReplicates属性设置为True,这个属性在C++中或者蓝图中都可以使用。

在Actor的网络交互机制上,主要分为2种类型,分别是 Replication同步Remote Procedure Calls(RPCs)远程过程调用

Replication同步 , 主要用于进行属性的自动同步。一般用于一些经常变化的属性,比如角色的HP血量, 移动位置等。

RPCs远程过程调用 ,由调用者主动发起。一般用于一些通知事件的发生,或者发起一些行为和动作。比如使用武器开火射击,或者释放了一个技能

1. Replication同步

Replication属性同步一般是指服务器将数据传递到客户端的动作(反之不行)。进行同步的方式分为两种, Property Replication属性同步Component Replication组件同步

顾名思义, Property Replication属性同步 是以对象属性作为基本同步单位的同步机制;而 Component Replication组件同步 是以Actor组件为基本管理单位的同步机制,相对于属性同步属于更粗粒度的管理。

下面会具体介绍两种机制和使用方式。

1.1 Property Replication属性同步


**Property Replication属性同步** 是以Actor的属性为单位进行网络同步的机制。每个Actor会维护一个包含了所有需要同步的属性列表。当其中有属性发生变化的时候,服务器就会把更新的属性值发送到所有客户端进行同步,客户端收到同步的属性值后,就会更新本地对应的属性为新的值。这种同步机制的方向 **只会从服务器Server发送到客户端Client** ; **客户端永远不会同步属性到服务器** 。 如果强行在客户端修改了这个属性,那么这个值可能会在下次服务器同步的时候被覆盖,或者永远与其他客户端不同步,导致一些错误。

a) 使用Property Replication属性同步

要使用属性同步,首先要保证Actor已经把Replicates标记开启了,这个是所有网络机制启用的基本保证。设置方式既可以通过蓝图勾选,也可以同步代码设置,如下图:

在这里插入图片描述

接下来,要将声明的属性设为Replicated,同样也可以通过C++和蓝图两种方式:

在这里插入图片描述
在这里插入图片描述

接下来,在属性所在的Actor类中,需要声明GetLifetimeReplicatedProps 函数,并调用UE4内置宏添加这个属性同步的调用语句,如下:

在这里插入图片描述
第一个参数是Actor的子类名,第二个是属性名。

b) Replication Condition同步条件

除了默认的同步调用,我们也可以使用 条件同步 调用, 使用的是另一个宏 DOREPLIFETIME_CONDITION ,如图:
在这里插入图片描述
当使用条件同步时,只有满足了条件所对应的情况才会进行属性同步,这个例子中 COND_OwnerOnly 对应其中一种条件值得枚举值,表示属性只会同步给Actor 的拥有者(Onwer)。此外还有以下其他条件枚举值可以选择:

条件说明
COND_InitialOnly属性只会在 第一次 连接时候同步,之后不再进行同步
COND_OwnerOnly属性只会同步给Actor的 拥有者(Owner)
COND_SkipOwner属性会同步给 除了 Actor 拥有者 之外其他的所有连接
COND_SimulatedOnlyActors 属性只同步给**模拟(simulated)**的Actor
COND_AutonomousOnly属性只同步给 Autonomous自治 类型的Actor
COND_SimulatedOrPhysics属性会同步给模拟(Simulated)类型和**物理类型(设置了bRepPhysics标记的)**的Actor
COND_InitialOrOwner属性会在 第一次连接 时候同步 或者 是同步给 Actor的拥有者
COND_Custom自定义同步条件,使用 SetCustomIsActiveOverride 来开关同步

c) RepNotify

有时候可能想在属性 同步之后 得到通知,或者调用一个回调函数,UE4提供了 RepNotify 机制来实现这个功能。这个机制能让用户提供一个回调函数,在当属性值发生更新之后,就会调用这个回调函数,这个机制对 所有同步的实例 都起作用。

在蓝图里,当选择 Replication类型为RepNotify之后,蓝图会自动创建一个回调函数,一般名称为OnRep_***各式, 如下图:
在这里插入图片描述
在这里插入图片描述
如果在C++代码中,可以在 UPROPERTY宏 中添加声明, 并手动添加一个同步通知函数,如下:
在这里插入图片描述
这个例子在Health属性同步声明中,指定了OnRep_Health函数作为其RepNotify的函数,要注意这个函数必须添加 UFUNCTION 的宏声明,即使这个宏里是空的声明。

当Health属性更新后,就会调用 OnRep_Health通知函数。在实现中可以添加特殊的逻辑,比如以下的逻辑判断如果Health小于0,则播放死亡动画。
在这里插入图片描述

1.2 Component Replication组件同步

除了使用同属性同步,UE4也支持以功能**组件(Component)**的方式进行同步。一般情况下,大部分组件是不需要同步的,需要同步的属性一般都在Actor下通过属性同步方式完成。但有一些特殊Component包含了需要网络同步的属性,这种情况下就需要使用组件同步的功能。

组件同步的方式与Actor的方式基本一致,组件也可以使用属性同步,或者调用RPCs来进行网络交互。组件可以看作是Actor的一部分,当Actor发生同步时,就会递归调用到组件的同步。组件同步属性时,也需要调用相应的**::GetLifetimeReplicatedProps (…)**函数像Actor一样添加同步属性的设置。

当使用组件同步时,组件可以分为两个大类,分别是静态组件(Static Component)和动态组件(Dynamic Component)

a) 静态组件Static Component

静态组件是Actor创建时候就会生成的组件,无论是否进行组件同步,都会创建这个组件。服务器也不会通知客户端创建这类组件,客户端创建Actor的时候就会直接生成,一般这种组件是在C++构造函数Constructor或者作为默认的子物体DefaultSubObject在蓝图里创建。一般只有这类组件的属性发生变化,才需要进行服务器和客户端之间的同步。

b) 动态组件Dynamic Component

动态组件是指在运行时动态创建的组件,在服务器创建的动态组件会自动同步到所有客户端实例然后分别进行创建,之后也会在属性变化时候自动同步属性给所有客户端。

客户端虽然也可以创建自己本地的动态组件,但是这种情况下组件并不会同步到服务器,也不会同步给其他客户端,所以在服务器和其他客户端是看不到新创建的组件的。

c) 使用组件同步

使用组件同步的方式基本和Actor一样,都是先开启同步的标记属性,然后在GetLifetimeReplicatedProps函数添加具体要同步属性的规则宏。

使用C++进行组件同步

要在C++开启组件同步,需要调用组件类的**AActorComponent::SetIsReplicated(true)**方法。如果组件是Actor的默认子对象,那么需要在构造函数生成组件之后进行调用开启同步,如下图:
在这里插入图片描述

使用蓝图进行组件同步

在蓝图中的组件属性面板中,有 Component Replicates组件同步 的属性勾选项,只要勾上就可以开启。
在这里插入图片描述
还有一种方式可以通过在蓝图中 调用函数 的方式开启,蓝图函数是 Set Is Replicated , 如图:
在这里插入图片描述



2. RPCs远程过程调用

RPCs 是**(Remote Procedure Cal)远程过程调用**的缩写,表示一个函数在本地进行调用,但是在远端执行。

RPCs既可以是从 服务器调用到客户端 ,也可以是 从客户端调用到服务器 。在使用上,一般用于调用一个事件或者动作,比如服务器告诉客户端某个NPC释放了一个技能,客户端进行特效,声音的播放。或者客户端告诉服务器自己更换了一个武器,服务器处理物品的更换,属性变化,以及同步给其他客户端的一些逻辑等。

2.1 RPCs类型

RPCs分为三种类型,分别是:

  • Server : 这类函数一般由客户端进行调用,会在 服务器端的实例执行
  • Client : 这类函数一般由服务器调用,会在这个 Actor的拥有者Owner的客户端实例执行
  • NetMulticast : 多播类型的RPC,一般用于在服务器调用,然后在所 有客户端对应Actor的实例执行 ,同时 也会在服务器端执行 。虽然函数也可以在客户端进行调用,但是效果和执行一个非RPC的函数是一样的,不会在服务器产生任何效果。

2.2 RPC的一些需求和缺陷

在使用RPCs进行网络通信,必须满足一些特定的需求和注意事项:

  • RPCs必须从Actor进行调用。
  • 进行RPCs的Actor必须是开启了同步属性(Replicated)的。
  • 如果RPCs是从服务器调用到客户端的(Client调用类型),只有拥有这个Actor的客户端(Owner)会执行这个函数
  • 如果RPCs是从客户端调用到服务器的(Server调用类型),这个客户端必须 拥有这个Actor 才可以。
  • NetMulticast RPCs多播类型的RPCs是一个特例:
    • 如果从服务器调用这类RPC,不仅服务器会本地执行这个RPCs的函数,并且 其他所有已经连接的客户单实例 也会执行。
    • 如果从客户端进行调用,则 只会在这个客户端本地执行 ,不会在服务器和其他客户端执行。
    • 目前UE4还有一个机制会限制RPCs的调用:
      • 一个多播类型的RPCs函数在一个 更新周期(Update Period) 只会同步一次,也就是在更新周期内即使频繁调用多次,最终也只会执行一次。根据官网描述,以后UE4可能会改进这个限制。

2.3 不同类型的RPCs调用表

为了更清晰,不同类型RPCs在不同角色的机器上和不同的拥有关系下的执行效果,下边列出了两种情景下的调用效果表。第一种是在服务器调用的效果;第二种是在客户端调用的效果。

表的不同列表示RPCs的不同类型,表的行表示被调用Actor对应不同的拥有关系。

a) 从服务器调用
Actor拥有关系非同步NetMulticastServerClient
客户端拥有在服务器执行在服务器和所有客户端执行只在服务器执行在拥有者的客户端执行
服务器拥有在服务器执行在服务器和所有客户端执行只在服务器执行在服务器执行
未被拥有在服务器执行在服务器和所有客户端执行只在服务器执行在服务器执行

b) 从客户端调用
Actor拥有关系非同步NetMulticastServerClient
调用客户端拥有在调用客户端执行在调用客户端执行在服务器执行在调用客户端执行
其他客户端拥有在调用客户端执行在调用客户端执行丢弃在调用客户端执行
服务器拥有在调用客户端执行在调用客户端执行丢弃在调用客户端执行
未被拥有在调用客户端执行在调用客户端执行丢弃在调用客户端执行

2.4 在C++使用RPCs


a) 声明RPCs函数

声明RPCs很简单,只需要在C++头文件对应的函数增加 UFUNCTION 宏,里边写入函数的RPCs类型关键字,如下:

Client类型RPCs :
在这里插入图片描述

Server类型RPCs:
在这里插入图片描述

NetMulticast多播类型RPCs:
在这里插入图片描述

b) Reliability可靠性

RPC有一个隐含的属性 —— 可靠性Reliability 。 默认情况下, RPCs是不可靠的,也就是无法保证肯定会在目标机器上执行,可能会丢失。但是也可以手动开启关键字 Reliable 的标签,保证函数肯定会执行, 如下:

在这里插入图片描述

也可以添加 Unreliable 的关键字标签,但是因为默认就是Unreliable的,所以一般并不需要。

一般情况下,Reliable用于实现一些关键函数事件,尤其是会对逻辑流程产生依赖影响的函数。而Unreliable的函数适合播放一些无关紧要的效果,声音等,即使偶尔没有执行也不会产生严重影响。Unreliable的RPCs在执行上会有更高的性能效率。

c) RPCs实现函数

要在C++实现RPCs函数,需要实现一个 (函数名)_Implementation的函数体,加后缀_Implementation是UE4默认的规则,如下声明了一个名叫Server_PlaceBomb的函数:
在这里插入图片描述

然后添加了这个函数的实现体
在这里插入图片描述

d) Validation验证

有时候开发者希望在调用RPCs函数之前进行一些条件检查,并不是每次都必然会执行这个函数实现,或者在检测到一些特定的输入时候执行一些特定的操作。UE4提供了这样的机制,就是 Validation验证机制

要对一个RPCs函数使用验证,只需要在声明时候在 UFUNCTION 宏中额外添加 WithValidation 关键字,如下:

在这里插入图片描述
然后需要找一个地方添加验证函数, 函数名默认是 原函数名_Validate ,如下:

在这里插入图片描述

在这个验证函数中,验证RPCs参数 AddHealth 是否大于最大加血量 MAX_ADD_HEALTH , 如果超过了就返回false, 这样会主动断开调用者的连接,认为是作弊者;否则就返回true, 通过验证。


2.5 在蓝图Blueprint中使用RPCs

除了C++, 也可以再蓝图中使用RPCs函数。使用方法是首先在蓝图里生命一个自定义事件Custom Event, 然后选择Replicates对应的RPCs函数类型。因为RPCs无法返回值,所以无法使用一般函数来创建它们。如图:

在这里插入图片描述

在这里插入图片描述

四、 引用

http://cedric-neukirchen.net/2017/02/14/multiplayer-network-compendium/

https://docs.unrealengine.com/en-US/Gameplay/Networking/index.html

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值