蓝图中,RPC是通过创建CustomEvent(自定义事件)来创建的和设置它们成为Replicate属性。
RPC不能有返回值!所以函数不能创建它们。
Replicates复选框可用于标记RPC是否可靠传输。勾选确保执行而不是丢失。
NOTE:不要把所有RPC都标记成可靠的!(消耗资源还慢)
要在C++里面用整个网络,你需要在头文件里#include "UnrealNetwork.h"。
在C++中RPC相对容易创建,只需要在UFUNCTION()宏里面添加特定参数。
-
源文件实现该函数的时候要加上‘_Implementation’。
-
源文件也要实现该函数的校验函数,'_Validate'为后缀。
另外两种类型的RPC这样创建:
客户端RPC,必须标记为‘reliable’或‘unreliable’!
-
多播RPC,也需标记为‘reliable’或‘unreliable’!
-
当然,也能在RPC上加‘reliable’关键字使它可靠。
-
校验函数(C++)
验证的思想是,如果RPC检验函数检测到任何函数是错误的,可以立刻通知系统断开客户端/服务器的连接。
校验函数在每个服务器RPC函数上都要实现。UFUNCTION宏里面的WithValidation关键字就用来干这个。
-
一个校验函数的使用例子:
-
NOTE:从客户端到服务器的RPC需要校验函数来保证安全的服务器RPC函数,并使添加代码来检查每个参数是否针对所有已知输入约束有效变得尽可能容易!
拥有权Ownership
理解所有权比较重要。
前面你已经看过了类似‘客户端拥有的Actor’那个表格(Page 60)。
服务器或者客户端能‘拥有’一个Actor。
PlayerController就是一个例子,它被客户端拥有。
另一个例子是,在场景里面生成/放置一个门。这基本是服务器拥有的。
但为什么会这样呢?
如果你再看一下这个表格,你会注意到,例如,如果客户端对不属于自己的Actor调用服务器RPC,则服务器RPC会丢失。
那么客户端就不能调用服务器上的‘Server_OpenDoor’。
那怎么解决?
我们使用客户端实质拥有的Actor类。这就是PlayerController发挥作用的地方。
因此,我们在PlayerController中创建服务器RPC,让服务器调用门上的接口函数(例如:interaction()),而不是在门上启用输入并在那里调用服务器RPC。
如果门实现了正确的接口,那么它将调用定义的逻辑,并期望正确地复制打开/关闭状态。
NOTE:接口并非特定于多人游戏,如果你不知道接口是什么,我建议你先去了解下!
Actors和他们所拥有的连接
正如类概述中提及过的,PlayerController是Player实质拥有的第一个类,但意味着什么呢?
每个连接都有一个专门为该连接创建的PlayerController。所有这个PlayerController被该连接拥有。
所以我们为确定一个Actor是否被某连接特定拥有,我们就查询它最外层拥有者是不是一个PlayerController,因为拥有PlayerController的连接就拥有该Actor。
很简单吧,不明白我就再举个例子。
Pawn/Character类。被PlayerController拥有,此时PlayerController就获得这个Pawn的所有权。
意味着,拥有PlayerController就拥有这个Pawn。
Un-possessing将导致客户端不再拥有这个Pawn。
为什么这个重要,我们要用它干嘛?
1.RPC需要确定哪个客户端将会执行 运行于客户端上的RPC
2.确定Actor的复制和连接相关的操作
3.涉及所有者Actor属性复制条件
你现在应该清楚从客户端和服务器调用的RPC是不同的,取决于这个RPC被哪个连接所拥有。
你也了解过条件复制了,(C++里)变量只在特定条件下进行复制。
下面描述列表的相关性。
Actor相关性和优先级
相关性
那么什么是相关性?我们需要相关性干什么?
设想一下,如果有款关卡/大地图游戏,那么玩家相互之间可能显得不那么重要。
如果A合B相隔几百米,那么他们相互之间不需要可见。
为了提高带宽,UE4只告诉当前客户端它所能知道的相关集。