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

【UE4】网游开发中的RPC和OnRep(一)_第1张图片

​如果你在测试的时候发现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()会在Server中也同样Spawn一个projectile,这个projectile会通过我们原本勾选的replicates复制一份回Client。

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

防止Server端调用Server_OnFire()

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

解决这个问题的方法就是在执行Server_OnFire()之前进行判断,判断是在客户端还是在服务端,如果确定是在客户端,才继续调用Server_OnFire()。
【UE4】网游开发中的RPC和OnRep(一)_第2张图片
图片来源:页游http://www.hp91.cn/页游

判断方式有三种:

  • 进行权威(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

你可能感兴趣的:(游戏开发)