属性复制
.h文件:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UPROPERTY(Replicated)
bool bAiming;
UPROPERTY(ReplicatedUsing = OnRep_EquippedWeapon)
AWeapon* EquippedWeapon;
UFUNCTION()
void OnRep_EquippedWeapon();
.cpp文件:
void UCombatComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UCombatComponent, EquippedWeapon);
DOREPLIFETIME(UCombatComponent, bAiming);
}
void UCombatComponent::OnRep_EquippedWeapon()
{
if(EquippedWeapon && Character)
{
EquippedWeapon->SetWeaponState(EWeaponState::EWS_Equipped);
AttachActorToRightHand(EquippedWeapon);
Character->GetCharacterMovement()->bOrientRotationToMovement = false;
Character->bUseControllerRotationYaw = true;
PlayEquipWeaponSound(EquippedWeapon);
EquippedWeapon->SetHUDAmmo();
}
}
启用属性复制的变量需重写GetLifetimeReplicatedProps函数,告知UE4这些变量需要复制。属性复制可以是单纯的对这个变量进行复制,如上面的bAiming变量,通过在上方添加 UPROPERTY(Replicated) 实现,这个变量可以传给AnimInstance,AnimInstance中会根据bAiming的值决定是否在瞄准状态。通过将这个变量设为复制,本地客户端在瞄准时会播放瞄准动画,其他人也会同步显示瞄准动画。
另一种是带有通知的属性复制,如上面的EquippedWeapon变量。在变量上方添加 UPROPERTY(ReplicatedUsing = OnRep_EquippedWeapon) 就会在EquippedWeapon变量发生改变时执行OnRep_EquippedWeapon函数,执行一些额外的操作。
RPC
.h文件:
UFUNCTION(Server, Reliable)
void ServerFire(const FVector_NetQuantize& TraceHitTarget);
UFUNCTION(NetMulticast, Reliable)
void MulticastFire(const FVector_NetQuantize& TraceHitTarget);
.cpp文件:
void UCombatComponent::Fire()
{
if(CanFire())
{
ServerFire(HitTarget);
if(EquippedWeapon)
{
CrosshairShootingFactor = 1.f;
}
StartFireTimer();
}
}
void UCombatComponent::ServerFire_Implementation(const FVector_NetQuantize& TraceHitTarget)
{
MulticastFire(TraceHitTarget);
}
void UCombatComponent::MulticastFire_Implementation(const FVector_NetQuantize& TraceHitTarget)
{
if(EquippedWeapon == nullptr) return;
if(Character && CombatState == ECombatState::ECS_Reloading && EquippedWeapon->GetWeaponType() == EWeaponType::EWT_Shotgun)
{
Character->PlayFireMontage(bAiming);
EquippedWeapon->Fire(TraceHitTarget);
CombatState = ECombatState::ECS_Unoccupied;
return;
}
if(Character && CombatState == ECombatState::ECS_Unoccupied)
{
Character->PlayFireMontage(bAiming);
EquippedWeapon->Fire(TraceHitTarget);
}
}
RPC有Multicast、Run On Server、Run On owning Client几种,我的项目中Run On owning Client没有用到,这里就只展示另外两种。RPC函数上方需声明这个函数是哪一种RPC,在.cpp文件中实现时,函数名后面要加上_Implementation,告诉编译器这是个RPC函数。上面代码的逻辑是,本地在开火时,执行Fire()函数,检测是否达成开火的条件,达成的话执行ServerFire()函数,这是一个服务器RPC,会在服务器上执行。ServerFire()中又执行了MulticastFire()函数,这是一个多播RPC,由服务器执行多播RPC的话,会同步在所有客户端上执行。因此经由上述代码,本地执行开火函数调用服务器RPC,服务器又调用多播RPC,实现了所有端同步执行开火逻辑。