ue4 蓝图网络同步、RPC总结

对于ue4蓝图网络同步看了两篇博客,感觉挺简单的,没想到做项目的时候各种bug。

一、首先需要知道的基础概念

1、对于继承了Actor的类,在类默认值属性中勾选Replicates,服务器始终拥有该 Actor 的权威版本,而一个客户端(或多个客户端)可能拥有近似该 Actor 的复制版本,如果该actor不勾选,只会在服务器上存在,客户端没有该Actor,上面的Net Load On Client勾选意思是地图加载时,Actor 将自动加载并在客户端机器上显示

2、对于Switch Has Authority,选择Authority只会在服务端执行,其作用是防止作弊,把重要数据和函数在服务端上修改和生成,选择remote也即只在客户端上执行操作,服务端上没有,作用是执行某种特效仅在客户端,服务端不执行,减缓服务端压力

3、对于勾选了Replicates的actor类,类中的变量要想获取到从服务器发来的复制信息,就要勾选Replicated

4、对于勾选了Replicates的actor类,类中的变量不仅要获取到从服务器发来的复制信息,还要在复制信息发生变化时产生不同的逻辑,需要把变量的Replication改成,比如,客户端获取到服务端的红绿灯颜色值,如果该变量是绿色,客户端和服务端同时执行OnRep_变量名这个函数,这个函数把actor材质改成绿色,如果从服务端发来的变量变成红色,客户端和服务端又自动同时执行该函数,把actor材质改成红色

                                

5、对于事件有三种:Multicast (多路广播), Server (服务器), 以及 Client (客户端)。勾选 多路广播,该事件应在服务器上进行调用,在服务器上执行然后自动转发到客户端。 勾选 Server,该事件应在客户端调用,随后仅在服务器上执行。 勾选 Client,该事件应在服务器调用,随后仅在其所拥有的客户端上执行。

6、对于可靠函数,尤其是在其所属的客户端上运行的时候容易丢包,所以勾上保持数据可靠传输不丢失

7、PRC

Function Replication(远程过程调用或 RPC)是在本地调用但在其他机器(不同于执行调用的机器)上远程执行的函数。RPC 函数非常有用,可允许客户端或服务器通过网络连接相互发送消息。 远程调用函数可设置为 Reliable 或 Unreliable,其中 Reliable 调用必定会发生,而 Unreliable 调用可能会在网络繁忙时被丢弃。大多数处理装饰视觉效果的远程调用函数应设置为 Unreliable,以避免过多占用网络。

远程调用函数主要包括 3 种类型:Multicast 广播Run on Server 在服务端执行 和 Run on owning Client 在客户端执行

8、蓝图中的事件调用说明

对于下面的普通事件

如果该事件是在客户端调用,就会在本地客户端输出Hello,如下图

如果该事件在服务端调用,该事件就会在服务端输出Hello,如下图

在服务端执行服务端的事件有必要吗?答,如果Mytest1中逻辑较多,可以再写个在服务端的事件,把较多的逻辑放到另一个事件中

怎么判断当前Mytest3是在服务端调用还是在客户端调用?

答:看最前面的红色事件是什么就是什么端调用,因此是在客户端调用Mytest3

让服务端和所有客户端都打印:

如果该actor可以同步并且可以获取,先在客户端调用服务端事件(意思是服务端事件之前是客户端调用的),再在服务端事件中调用多路传送事件,可以让服务端和所有客户端都打印,比如,在PlayerCharacter中这样可以达到效果

这里注意的是多路传送事件要写在服务端事件的里面才行

如果其他不变,playercharacter的服务端调用服务端事件,在调用多路传送,只会在服务端打印,因为第二个服务端事件是由服务端调用的,没有客户端。

9、区分哪些类有客户端,哪些类没有客户端

在Gamemode中只有一份,所以都是在服务端运行的,GameState中客户端和服务端都有一份,PlayerCharacter客户端和服务端都有一份,PlayerController不同于PlayerCharacter,它有同步但是其他客户端获取不到,为什么?PlayerController相当于人的思维,PlayerCharacter相当于人的行为,人的行为需要同步,因为服务端拿到你的行为,同步到我的客户端,这样我能看到你的行为,你也能看到我的行为,玩家状态PlayerState服务端一份,客户端一份,但是有必要同步PlayerController吗?没必要,对面玩家没必要看到你的思维,所以其他客户端获取不到别人的controller

因此在PlayerController中的客户端调用服务端事件在调用多路传送,只会打印本地客户端(Client1)和服务端(Server),其他客户端(Server2)不会打印,如下图:

              

二、事例演示

1、需求一

对于中途加入连接的客户端,获取之前的游戏数据(比如游戏开始后5秒有人加入,此时加入的客户端需要获取当前时间游戏已有多少人加入,都叫什么名字),并重新绘制到当前客户端的Widget中

在GameMode中有个OnPostLogin事件,有客户端连接到服务器上时就会触发该事件,我们用多个客户端进行测试,3个玩家,勾选运行专用服务器意思是弹出的三个框全是Client,不勾选那么弹出的是一个Server和2个Client

如果同时运行2个客户端,数据可以同步,比如2个Client中游戏开始倒计时和已加入玩家数据是同步的

如果中途有客户端加进来,输入服务端的ip,端口号默认17777点击回车,发现该客户端丢失了游戏开始倒计时数据和已加入的玩家信息

解决方法:

在GameSate中保存所有已注册的玩家信息和倒计时时间,在新玩家中途加入的时候自动调用OnPostLogin事件,由于在GameMode中在服务端和客户端只存在一份,所以是在服务端执行这些逻辑的(可以调用多路传送和在其所属的客户端上运行这两种事件),我们要获取该客户端本地的Widget,并获取GameState的倒计时和玩家信息数据更新到本地的Widget,因此创建一个倒计时中途加入事件(设为在其所属的客户端上运行)

注意的是OnPostLogin事件是在GameMode执行的,GameMode的逻辑都是在服务端执行的,所以DaoJiShiZhongTuJiaRu事件要写成拥有的客户端事件

在Player Controller中拿到该客户端下的Widget

然后拿到GameState的已加入玩家数据,自动更新到该客户端下的Widget,

这里注意的是由于Player Controller是在客户端执行的,刷新Widget的事件是写成在服务端还是在客户端还是多路传送还是默认不能复制的事件呢?

答案是写成不能复制的事件即可,我们只是刷新本地的widget就好

2、需求二

玩家输入自己名字后,如果该名字没有被注册,输入框就设为不能再输入,如果改名字已被注册,输入框不禁用

解决方法: 

在Widget中拿到一个PlayerController

在该PlayerController中由于是在客户端上执行逻辑,所以可以调用在服务器上运行的事件,传入Name判断是否注册,并给出是否禁用输入框

   

由于WeatherDisbleInput事件选择在服务端上执行,因此后面调用的事件可以是多路传送,也可以是在其所属的客户端上

  

3、需求三

如果第一个玩家输入名字,所有玩家都能第一个玩家看到名字,在游戏倒计时15秒之前,第一个玩家可以重新修改自己的名字,其他玩家都可以看到修改后的名字

解决方法: 在Player Controller中保存是否注册名字,和注册时选择是红队还是蓝队的变量,变量要选择Replicated,然后调用服务端事件判断名字是否注册

根据是否注册来触发不同事件,触发的事件让所有人都能看到,所以调用多路传送

4、需求四

第一个玩家输入名字后开始游戏倒计时,其他所有玩家都会同步并显示倒计时

解决方法:

在Player Controller中先调用在服务端执行的事件,再写个多路传送的事件,刷新每个客户端的Widget,是这样吗?

错!

之前已经说过了,因为每个人获取不到其他人的Player Controller,因此调用在服务端执行的事件,再调用多路传送或在每个客户端执行的事件只能自己能接收到,其他人接收不到,因此多路传送的事件可以写在GameMode或者GameState中

在GameState中,虽然拿到的PlayerController是第一个人的Controller,但是获取到的HUD只有一份,也就是说所有角色的Controller都共用同一个HUD,但是在多路传送的事件中通过一份HUD拿到的Widget却是各自的。然后更新各自客户端的Widget即可

遇到的一些坑:

1、在player controller类中的事件如果是不能复制的,打印get playercontroller和self有什么不同?

比如在Player Controller中打印下图两种有什么区别?

在player controller类中的事件如果是服务端运行的,打印get playercontroller和self有什么不同?

答:在world中保存了所有的Player controller,用一个数组保存。

如果在Player controller中的本地事件打印get playercontroller,因为事件是本地的,无法从服务端获取,所以得到的controller只有一个,那就是自己,打印self输出的也是自己的controller,而且注意的是这个打印只会在自己客户端上打印。

如果在Player controller中的服务端运行事件里打印get playercontroller,事件在服务端运行,得到的就是world里第index个controller。打印self输出的是该客户端的controller是在world中的第几个controller。

2、Actor如果勾选replicates那么位置信息等默认在各个客户端都同步,如果是Actor里面的组件,那么位置信息就不是同步的,先调用服务端在调用多路

猜你喜欢

转载自blog.csdn.net/zhangxiaofan666/article/details/81395750