如果你在测试的时候发现Client和Server中的子弹没有同步,or你扔一枚炸弹却听到重叠的爆炸声…
刚刚接触网络同步的时候,很有可能会出现这样的现象,开了一个Listen Server,然后在Client中跑来跑去,撞撞箱子,打打箱子…
然后发现人物撞着撞着撞不到箱子了,像是被什么看不见的东西挡住了,然后试着开枪打,发现箱子被你打飞了,但是在Server中箱子竟然还在原地。
这是因为你的抛射物子弹没有同步到Server,Server并不知道你用projectile将箱子打飞了,这时,你需要使用远程过程调用(RPC)进行Client和Server之间的通信。
RPC的声明有三种:
分别对应在函数声明前添加
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()。
图片来源:页游http://www.hp91.cn/页游
判断方式有三种:
if(!HasAuthority())
{
Server_OnFire(SpawnLocation, SpawnRotation);
}
if(!GetWorld()->IsServer())
{
Server_OnFire(SpawnLocation, SpawnRotation);
}
if(GetLocalRole() < ROLE_Authority)
{
Server_OnFire(SpawnLocation, SpawnRotation);
}
此三种方式均能区分当前执行位置为Client还是Server。