一、原因
1.由于网络问题会导致服务器上的时间和客户端上获得的时间不一致
二、解决方法
在程序启动时向服务器请求服务器的时间返回给客户端并获得客户端发送消息的往返的时间,在获得客户端上的时间,用服务器上获得的时间加上往返时间减去客户端上的时间获得差值,在通过获得客户端上的时间加上差值获得当前服务器上的时间
三、实现
1.在界面添加显示时间的文本框用来显示时间(MatchCountDownText)
2.添加设置界面的函数
2.1界面类中添加
cpp
UPROPERTY(meta = (Bindwidget))
UTextBlock* MatchCountDownText;
2.2.在PlayerController类中添加设置界面函数
cpp
void SetHUDMatchCountDowntime(float CountdownTime);
void ABlasterPlayerController::SetHUDMatchCountDowntime(float CountdownTime)
{
BlasterHUD = BlasterHUD == nullptr ? Cast<AABasterHUD>(GetHUD()) : BlasterHUD;
bool bHUDValid = BlasterHUD &&
BlasterHUD->CharacterOverlay &&
BlasterHUD->CharacterOverlay->MatchCountDownText;
if (bHUDValid)
{
int32 Minutes = FMath::FloorToInt(CountdownTime / 60.f);
int32 Seconds = CountdownTime - Minutes * 60;
FString CarriedDownText = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds);
BlasterHUD->CharacterOverlay->MatchCountDownText->SetText(FText::FromString(CarriedDownText));
}
}
四、 同步
4.1代码流程
ReceivedPlayer()函数会在客户端启动时最开始调用,通过if判断当前是否是客户端通过GetWorld()->GetTimeSeconds();获得当前的客户端上的时间TClientTime1,调用服务器的RPC请求服务器的时间,在服务器上通过GetWorld()->GetTimeSeconds()获得当前服务器上的时间TServerTime1,在将TClientTime1,TServerTime1作为参数调用客户端的RPC将当前服务器时间TServerTime1和客户端请求同步的时间TClientTime1在给会客户端,在通过GetWorld()->GetTimeSeconds()获得当前客户端时间TClientTime2,通过TClientTime2-TClientTime1获得从请求到收到回复一共用了多长时间,在通过TServerTime1 + (TClientTime2-TClientTime1)/2 在客户端上获得当前服务器上的时间,再通过GetWorld()->GetTimeSeconds();获得客户端现在的时间,用当前服务器上的时间将去当前客户端的时间获得时间的差值。在游戏进行时通过Tick函数进行相同过程的判断。
4.2代码实现,添加服务端RPC函数,客户端RPC函数等
cpp
/**
* 同步客户端和服务器上的时间
*/
virtual void Tick(float DeltaTime) override;
//请求当前的服务器上的时间,在发送请求时传入 Client 端时间,参数是客户端请求需要的时间
UFUNCTION(Server , Reliable)
void ServerRequestServerTime(float TimeOfClientRequest);
// 给客户端报告当前的服务器的时间响应服务器请求服务器的时间
UFUNCTION(Client, Reliable)
void ClientRequestServerTime(float TimeOfClientRequest,float TimeServerReceivedClientRequest);
float ClientServerDelta = 0.f; // 客户端和服务器端的时间的差值
UPROPERTY(EditAnywhere , Category = Time)
float TimeSyncFrequency = 5.f;//同步频率
float TimeSyncRunningTime = 0.f; // 距离上一次同步过去的时间
void CheckTimeSync(float DeltaTime);
virtual float GetServerTime(); // 获得服务器上的时间
virtual void ReceivedPlayer() override; // 尽快同步服务器时间
void SetHUDTime();
float MatchTime = 120.f; // 匹配时间
uint32 CountdownInt;
cpp
void ABlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequest)
{
float ServerTimeOfReceipt = GetWorld()->GetTimeSeconds(); // 服务器上的时间
//UE_LOG(LogTemp, Warning, TEXT("My variable: %f"), ServerTimeOfReceipt);
ClientRequestServerTime(TimeOfClientRequest, ServerTimeOfReceipt);
}
void ABlasterPlayerController::ClientRequestServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest)
{
float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest; // 获得往返时间
float CurrentServerTime = TimeServerReceivedClientRequest + (0.5f * RoundTripTime); // 当前服务器时间
ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds();
//UE_LOG(LogTemp, Warning, TEXT("My variable: %f"), ClientServerDelta);
}
float ABlasterPlayerController::GetServerTime()
{
float time = GetWorld()->GetTimeSeconds() + ClientServerDelta;
//UE_LOG(LogTemp, Warning, TEXT("My variable: %f"), time);
return time;
}
void ABlasterPlayerController::ReceivedPlayer()
{
Super::ReceivedPlayer();
if (IsLocalController())
{
float Time = GetWorld()->GetTimeSeconds();
//UE_LOG(LogTemp, Warning, TEXT("My variable: %f"), Time);
ServerRequestServerTime(Time);
}
}
void ABlasterPlayerController::SetHUDTime()
{
uint32 SecondsLeft = FMath::CeilToInt(MatchTime - GetServerTime());
if (CountdownInt != SecondsLeft)
{
//UE_LOG(LogTemp, Warning, TEXT("My variable: %f"), MatchTime - GetServerTime());
SetHUDMatchCountDowntime(MatchTime - GetServerTime());
}
CountdownInt = SecondsLeft;
}
void ABlasterPlayerController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
SetHUDTime();
CheckTimeSync(DeltaTime);
}
void ABlasterPlayerController::CheckTimeSync(float DeltaTime)
{
TimeSyncRunningTime += DeltaTime;
if (IsLocalController() && TimeSyncRunningTime > TimeSyncFrequency)
{
ServerRequestServerTime(GetWorld()->GetTimeSeconds());
TimeSyncRunningTime = 0.f;
}
}