UE4网游开发中的RPC和OnRep

Posted 游戏开发手账

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4网游开发中的RPC和OnRep相关的知识,希望对你有一定的参考价值。





如果你在测试的时候发现Client和Server中的子弹没有同步,

or你扔一枚炸弹却听到重叠的爆炸声......





撞了空气?


刚刚接触网络同步的时候,很有可能会出现这样的现象,开了一个Listen Server,然后在Client中跑来跑去,撞撞箱子,打打箱子......

然后发现人物撞着撞着撞不到箱子了,像是被什么看不见的东西挡住了,然后试着开枪打,发现箱子被你打飞了,但是在Server中箱子竟然还在原地。

这是因为你的抛射物子弹没有同步到Server,Server并不知道你用projectile将箱子打飞了,这时,你需要使用远程过程调用(RPC)进行Client和Server之间的通信。

RPC的声明有三种:
  1. 将某个函数声明为在服务器上调用,但在客户端执行

  2. 将某个函数声明为在客户端上调用,但在服务器执行

  3. 从服务器调用,在服务器和当前所有连接的n个客户端上执行(共n+1)


分别对应在函数声明前添加

  
    
    
  
UFUNCTION(Client) UFUNCTION(Server) UFUNCTION(NetMulticast)

RPC默认为不可靠,如果要在远端保证调用,则添加关键字Reliable

  
    
    
  
UFUNCTION(Server, Reliable)

如果需要添加验证,添加关键字WithValidation:

  
    
    
  
UFUNCTION(Server, Reliable, WithValidation)



让抛射物在Server同样显示出来


对于刚才提出的问题:Client的projectile没有同步到Server具体该如何解决呢?

在角色头文件MultiplayerCharacter.h中,添加函数的Server RPC声明。

  
    
    
  
void OnFire(); //原本的OnFire()声明
UFUNCTION(Server, Reliable, WithValidation) void Server_OnFire(FVector Location, FRotator Rotation); // bool Server_OnFire_Validate(FVector Location, FRotator Rotation);  void Server_OnFire_Implementation(FVector Location, FRotator Rotation);

在Server中生成projectile的任务交给Server_OnFire_Implementation完成,在OnFire()函数中调用Server_OnFire(),这样当Client执行OnFire()时,也会通过RPC使Server同样完成OnFire()。

  
    
    
  
void MultiplayerCharacter::OnFire() {     if(ProjectileClass != NULL)     {         if(GetWorld())         {             // 声明位置,旋转,ActorSpawnParams...                          // Spawn一个projectile          GetWorld()->SpawnActor<AMultiplayerProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams)          // 在OnFire()函数中调用Server_OnFire()          Server_OnFire(SpawnLocation, SpawnRotation);         }     }   }
void MultiplayerCharacter::Server_OnFire_Implementation(FVector Location, FRotator Rotation) { // 设置ActorSpawnParams...
    // Implementation中同样Spawn一个projectile,在服务端显示     GetWorld()->SpawnActor<AMultiplayerProjectile>(ProjectileClass, Location, Rotation, ActorSpawnParams) }

但是这时编译运行会发现,在Client中开了一枪,Server上确实是显示出来了,但是Client中却产生了两个子弹,这是因为Client调用OnFire()时,不仅OnFire()本身会Spawn一个projectile,其中调用的Server_OnFire()会在Client中也同样Spawn一个projectile。

所以要记得把OnFire()原本Spawn projectile的逻辑删掉,此任务交给Server_OnFire_Implementation()。



防止Server端调用Server_OnFire()


然后进行测试会发现,不仅在Client开一枪会调用Server_OnFire_Implementation(),在Server开一枪,也会调用Server_OnFire_Implementation()......

解决这个问题的方法就是在执行Server_OnFire()之前进行判断,判断是在客户端还是在服务端,如果确定是在服务端,才继续执行Server_OnFire()。

判断方式有三种:
  • 进行权威(Authority)判断,在UE4中,对Actor的拥有权限分为三种:权威、主控、模拟;比如现有客户端A,客户端B,和一个服务器,服务器拥有最高权限Authority,那么对于服务器来说,其权限为“权威A”,“权威B”,对于A和B来说,它们对自己的权限为“主控”,对另一方的权限为“模拟”,据此可进行这样的判断,保证只有Client会调用到Server_OnFire():


  
    
    
  
if(!HasAuthority()) { Server_OnFire(SpawnLocation, SpawnRotation); }

  • 利用GetWorld()->IsServer():

  
    
    
  
if(!GetWorld()->IsServer()) { Server_OnFire(SpawnLocation, SpawnRotation); }

  • 利用Role和RemoteRole的特点,因为只有服务器能够向已连接的客户端同步Actor,而客户端不能够向服务器同步,所以只有服务器才能看到 Role == ROLE_Authority,并且在UE4中GetLocalRole()返回的枚举类型中ROLE_Authority为最高值,利用此特点可进行判断:

  
    
    
  
if(GetLocalRole() < ROLE_Authority) { Server_OnFire(SpawnLocation, SpawnRotation); }

此三种方式均能区分当前执行位置为Client还是Server。


还没提到OnRep是什么呢,它们有什么联系和区别?

【UE4】网游开发中的RPC和OnRep(一)

咳...内个...下次一定...








以上是关于UE4网游开发中的RPC和OnRep的主要内容,如果未能解决你的问题,请参考以下文章

[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls(代码片段

UE4网络之(二) 远程调用函数(RPC)

ue4 c++代码怎么获取场景中的一个actor

UE4 网络同步原理三 RPC

UE4 Run On owing Client解析(RPC测试)

[UE4]RPC,远程调用