笔者最近正在从头开始搭建一个多人射击游戏,用于熟悉UE4 DS的方方面面,以及设计/性能优化等方面的课题,做些零碎的笔记
3.关于网络同步中,Character的Rotation同步。
在搭建游戏过程中,笔者发现直接设置Character的Rotation,并不能同步到服务器及其他客户端,所以趴一趴UE4对位置同步中Rotation的处理。
这里给各位的童鞋推荐大佬博客 https://blog.csdn.net/u012999985/article/details/78669947,对移动同步方向写的很详细,所以我这里只对 Rotation 的问题进说明。
首先找到服务器同步的移动数据ReplicatedMovement,ReplicatedUsing类型的同步变量:
在服务器进行Actor同步的时候,会采集Movement信息,进行Movement同步,从代码中发现,同步的Rotation直接使用的RootComponent的Rotation,下面继续从客户端发送移动请求寻找来源。
首先找到客户端发给服务器的同步请求servemove:
void UCharacterMovementComponent::ServerMove(float TimeStamp, FVector_NetQuantize10 InAccel,
FVector_NetQuantize100 ClientLoc, uint8 CompressedMoveFlags, uint8 ClientRoll, uint32 View,
UPrimitiveComponent* ClientMovementBase, FName ClientBaseBoneName, uint8 ClientMovementMode)
{
if (MovementBaseUtility::IsDynamicBase(ClientMovementBase))
{
CharacterOwner->ServerMove(TimeStamp, InAccel, ClientLoc, CompressedMoveFlags, ClientRoll, View, ClientMovementBase, ClientBaseBoneName, ClientMovementMode);
}
else
{
CharacterOwner->ServerMoveNoBase(TimeStamp, InAccel, ClientLoc, CompressedMoveFlags, ClientRoll, View, ClientMovementMode);
}
}
发现 Rotation 相关数据来自参数 uint32 View,向上追溯参数来源,可以看出其实客户端发给服务器的Rotation只是ControlRotation。
void UCharacterMovementComponent::CallServerMove
(
const class FSavedMove_Character* NewMove,
const class FSavedMove_Character* OldMove
)
{
check(NewMove != nullptr);
// Compress rotation down to 5 bytes
const uint32 ClientYawPitchINT = PackYawAndPitchTo32(NewMove->SavedControlRotation.Yaw, NewMove->SavedControlRotation.Pitch);
const uint8 ClientRollBYTE = FRotator::CompressAxisToByte(NewMove->SavedControlRotation.Roll);
...
在服务器调用RPC处理客户端移动请求时调用UpdateRotation,在UpdateRotation中直接设置了Actor的Rotation为ControlRotation(当然会考虑bUseControllerRotationPitch/bUseControllerRotationYaw/bUseControllerRotationRoll)的设置,所以说了一大堆,就是DS同步以ControlRotation为基础。
void UCharacterMovementComponent::ServerMove_Implementation(
float TimeStamp,
FVector_NetQuantize10 InAccel,
FVector_NetQuantize100 ClientLoc,
uint8 MoveFlags,
uint8 ClientRoll,
uint32 View,
UPrimitiveComponent* ClientMovementBase,
FName ClientBaseBoneName,
uint8 ClientMovementMode)
{
...
// Perform actual movement
if ((MyWorld->GetWorldSettings()->Pauser == NULL) && (DeltaTime > 0.f))
{
if (PC)
{
PC->UpdateRotation(DeltaTime);
}
MoveAutonomous(TimeStamp, DeltaTime, MoveFlags, Accel);
}
...
}
void APlayerController::UpdateRotation( float DeltaTime )
{
// Calculate Delta to be applied on ViewRotation
FRotator DeltaRot(RotationInput);
FRotator ViewRotation = GetControlRotation();
if (PlayerCameraManager)
{
PlayerCameraManager->ProcessViewRotation(DeltaTime, ViewRotation, DeltaRot);
}
AActor* ViewTarget = GetViewTarget();
if (!PlayerCameraManager || !ViewTarget || !ViewTarget->HasActiveCameraComponent() || ViewTarget->HasActivePawnControlCameraComponent())
{
if (IsLocalPlayerController() && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed())
{
auto XRCamera = GEngine->XRSystem->GetXRCamera();
if (XRCamera.IsValid())
{
XRCamera->ApplyHMDRotation(this, ViewRotation);
}
}
}
SetControlRotation(ViewRotation);
APawn* const P = GetPawnOrSpectator();
if (P)
{
P->FaceRotation(ViewRotation, DeltaTime);
}
}
void APawn::FaceRotation(FRotator NewControlRotation, float DeltaTime)
{
// Only if we actually are going to use any component of rotation.
if (bUseControllerRotationPitch || bUseControllerRotationYaw || bUseControllerRotationRoll)
{
const FRotator CurrentRotation = GetActorRotation();
if (!bUseControllerRotationPitch)
{
NewControlRotation.Pitch = CurrentRotation.Pitch;
}
if (!bUseControllerRotationYaw)
{
NewControlRotation.Yaw = CurrentRotation.Yaw;
}
if (!bUseControllerRotationRoll)
{
NewControlRotation.Roll = CurrentRotation.Roll;
}
#if ENABLE_NAN_DIAGNOSTIC
if (NewControlRotation.ContainsNaN())
{
logOrEnsureNanError(TEXT("APawn::FaceRotation about to apply NaN-containing rotation to actor! New:(%s), Current:(%s)"), *NewControlRotation.ToString(), *CurrentRotation.ToString());
}
#endif
SetActorRotation(NewControlRotation);
}
}
对于这种情况,可以有几种方法同步ActorRotation:
1.如果对Controller Rotation没有特殊限制,可以设置 bUseControllerRotationYaw = true,在需要修改转向时直接SetControlRotation 即可;
2.不通过Controller的Rotation同步,设置bUseControllerRotationYaw = false,设置CharacterMovementCompoennt中的bOrientRotationToMovement = true,这个变量的意思是在移动时自动转向你移动的方向,可以通过RotationRate控制转向速度;
3.添加自定义变量ActorYaw,在需要修改时给服务器发RPC。