UE4服务器有DedicatedServer和ListenServer两种模式,前者适合联网大型多人游戏,反作弊,后者适合本地多人游戏,快速开发。
两种模式的区别就在于服务器会不会额外运行一个客户端,而这个客户端却拥有服务器的权限(客户端服务器?有点绕)总之将这个服务器额外运行的客户端直接看做服务器就行了,所以下面我会把客户端叫client,服务器这边的客户端叫server。
这里只讨论ListenServer。
网络属性存在于Common Classes ,也就是新建蓝图选项里面如图所示的7类,
不要告诉我你不知道这个东西其中Actor、ActorComponent、SceneComponent默认是不启用网络属性的,需要手动勾选replicate才会启用。只有开启网络属性的actor才遵循ue4里面的网络规则:只有被服务器生成的Actor才能被修改网络属性,在客户端本地生成的Actor就算被设置为启用网络属性也不会被同步到服务器,更不会同步到别的客户端里面去,被启用了网络属性的对象,一切数据将以服务器上的数据为准,任何越过服务器同步,直接在本地进行更改的数据,将会在下一次服务器同步的时候被服务器返回来的数据覆盖。如果没有网络属性的类比如gameinstance,需要将变量值传入具有网络属性的类中进行使用,比如传个bool值到playercontroller,就必须将该变量设置为replicate。playercontroller都存在于服务器上,而且不同客户端是不会同步别人的playercontroller的,具体就表现为:在服务器上,如果如果此时在客户端A上,调用get playercontroller,输入index为0,那么能够获得自己的playercontroller,同理,客户端B也是获得自己的playercontroller,但是如果在客户端上你想要调用get playercontroller,输入index为1,那么你拿到的只能为空值。gamemode只有一个,而且只有开启网络属性的类,才能够通过rpc请求自己在服务器的孪生执行对gamemode的操作。被服务器生成的物体,只有在服务器上的才是实体,客户端上呈现的只不过是本地和服务器同步数据后将拿到的数据呈现出来的虚拟体,实际上到头来只有你和服务器在通信,而且你自己电脑上控制的也是虚拟体,自己看到的其他玩家也一样是虚拟体,其他玩家的虚拟体是服务器处理那边玩家控制器输入之后生成的数据,你在自己电脑本地事件对这个虚拟体的任何操作都不会影响到另一头的玩家,任何修改都会在下一次同步数据时被覆盖,同时这个虚拟体是桥梁,你可以通过获取这个虚拟体,然后执行里面请求服务器的事件,让服务器处理事件之后对在另一头电脑上面的玩家产生行为,比如射击游戏,你点击鼠标,请求服务器生成子弹,子弹在服务器上通过计算后确认打中了对面玩家,需要对面玩家hp归零,服务器将这个结果通过拿到对面玩家的实体执行run on owning client事件将这个结果传给了对面玩家,对面玩家的电脑拿到这个数据,虚拟体就要复制实体的行为,然后如果有需要带参,比如对面玩家本地的pawn里面的数据,那么服务器应该是拿到实体后执行一次run on owning client ,拿到client上面的变量值,然后再run on server 然后是multicast事件,通知所有客户端上面这个实体的虚拟体,让他们根据这个参数执行相应的本地事件。Gamemode数量为1,gamestate数量为1,playercontroller每台机一个,服务器上面的控制器都是远程服务器,所以用is local player controller 在服务器上为假,在客户端上为真,player state每个playercontroller一个,放在场景里面的启用网络属性或者由服务器生成的Actor,以服务器的为准,每台机一个副本。所有输入的事件都只在本地client执行,有人可能会迷惑,比如我add movement input,他是一个函数啊,也不是执行在服务器上面的事件,实际上是已经封装好了直接用就是了,只有其他的自定义输入事件,需要用run on server 请求服务器执行。
对于CustomEvent,有Run on Server,Run on owning client,Multicast,使用规则是
Run on Server的事件——应被客户端调用该事件,客户端会请求服务器在服务器上执行相应的事件,这时有执行环境从客户端到服务器的环境的变化
Run on owning client的事件——应被服务器调用该事件,服务器将会根据ROLE_Authority或者ROLE_AutonomousProxy往前追溯,这个服务器事件是由哪个客户端请求的,然后再在这个客户端调用这个Run on owning client的事件,这时有执行环境从服务器到客户端的环境的变化
NetMulticast的事件——应被服务器调用该事件,服务器将会在全部连接到该服务器的client上面执行相应的NetMulticast的事件
Reliable和Unreliable,调用Unreliable方法在严重的网络压力下,可以不被执行,对于一些不是必须的效果,可以设为Unreliable,而对于一些必要的操作请设为Reliable
这里的环境为启用网络属性的Actor在这行代码追溯最左边的那个事件的属性。输入事件,蓝图接口事件,普通的custom event,function还有marco,beginplay和tick还有destory都是没有明确属性的,他们在哪个环境下被执行,就在哪里执行。注意tick和beginplay还有destory是会同时在客户端和服务器上执行的,所以要注意根据环境的不同设置不一样的逻辑。
只有遵守规则了才会有正常的表现,不然乱来只会导致你一头雾水,因为虚幻引擎并没有禁止比如在client环境下去执行netmulticast的事件(不会达到你想要的再其他客户端上也执行的效果),在server上执行run on server,在client上执行run on owning client(后面这两者的操作和执行一般的事件没有差别)。
接下来可以看一段示例加深理解
——调用execute on server 的事件,该事件内具有对gamemode的操作,比如get game mode这时才能正常运作
——如果需要返回值,则可以在execute on server 的事件结束之后,调用一个execute on owning client 的事件将值传回玩家的客户端
如图所示(不要在意变量命名,仅作演示)
这个图很关键,读懂这个图能够写大部分的rpc函数如果我想要将我本地GameInstance里面的变量传给服务器,让服务器根据我这个本地变量去进行操作,那么可以用如下的方法
index在gameinstance里面,被设置成了replicate FServerPossessPawnMine的实现,注意SwitchHasAuthority这里有个情况,就是你的变量是传到被生成的类里面去的,但是有用到这个变量的component是非replicate状态,那么要在beginplay里面进行初始化(记得beginplay是在客户端和服务器上面都会执行一次的),逻辑是:作为服务器部分,get all actor of class拿到所有相关的pawn(当然这会包括自己),在自己的客户端上面执行请求将自己的变量提交到服务器即Excute on owning client->Excute on server(client执行excute on owning client 的话会被无视,因为自己不是服务器),然后让服务器执行一次multicast事件来用这些变量初始化所有客户端上面的没有设置为replicate但是要用到这个数据初始化的component Excutes On Server->Excutes On All
Switch Has Authority,
执行代码逻辑到当前节点,对该actor进行一次判断:现在是在服务器上还是在客户端上。还是用回上面这个图,中间的部分,代码逻辑是在服务器上运行的,所以在中间这段代码的部分,Has Authority将为真,上下两部分的代码,Has Authority将为假(gamemode和gamestate这俩在服务器上,不需要has authority)
那么我们可以用Switch Has Authority干啥呢?比如说,我有一个函数叫helloworld,然后他要写两种实现,一种是服务器的,一种是客户端的,那么我们就可以在函数的开头加入这个判定来省点功夫(但不省脑子),如图所示
那么问题就要来了,我在pawn里面写入了如下函数(包括上面的helloworld),那么我在client点两下p,分别会打印什么?我在server这边点两下p,会分别打印什么?
client::client server
server:server server
如果到这里也没有问题,那么就基本上理解了rpc的内容了。
0423补充:不会有卡顿感的摄像机
补充说明一下玩家pawn输入如何同步到服务器后继而由服务器同步到各个客户端上的玩家pawn副本。如果你的pawn里面有component设置为replicate而且需要更新旋转之类的数据,这个方法将会很常用,是官方multiplayer示例项目里面的一个做法。
首先将你要复制的replicate组件attach在不复制的camera上,然后这个camera 勾选use pawn control rotation,那么我们就可以根据get control rotation这个数据进行操作了,比如我们通过这个camera 和 get control rotation参数的run on server事件,请求服务器设置当前控制pawn实体camera的relative rotation ,然后在通过服务器执行一次multicast事件,让其他的client根据这个参数值更新他们的camera(注意multicast要的话要通过is locally controlled将本机筛选掉,以免再用回服务器的数值导致玩家视野卡顿,实现视线用的是本地值优先,一些表现上的不影响体验的同步到其他虚体数据用的服务器rpc)
网友评论