多客戶端出現時間不同步的情況,,
由於每個客戶端本地的時間,時區不同或者存在網路延遲造成的。所以為了能夠使每個客戶端顯示的時間相同,就需要在Server上利用GetWorld()->GetTimeSeconds()獲取伺服器時間,然後傳給客戶端顯示。
在傳輸過程中,要考慮到每個客戶端的延遲需要做一些計算偏移,才能實現最終每個客戶端的時間一致
利用RPC實現同步
UE中提過了RPC實現伺服器和客戶端的網路連線功能,其中ServerRPC表示在客戶端呼叫,伺服器執行,ClientRPC表示伺服器呼叫,客戶端執行。
在這裡預設從客戶端到伺服器,從伺服器到客戶端的時間傳輸是一樣的。
計算時間差
該時間差是指從客戶端請求當前時間到伺服器上呼叫ClientRPC後Client執行計算到的時間的差值,而該時間差就是產生每個客戶端時間不同步的那些主要因素導致的。
- 首先在客戶端上呼叫ServerRPC函式,該函式將接受客戶端當前的時間GetTimeSeconds()作為引數,該時間作為起始時間,在伺服器執行該函式,將得到伺服器上當前時間
- ClientRPC將接受兩個引數分別是客戶端的起始時間和伺服器當前時間,其中起始時間是用來計算RTT的,伺服器當前時間是用來計算需要顯示的時間的。
Time_RTT = PreClientTime - NowClientTime。
透過該RTT可以計算得到當伺服器呼叫的ClientRPC函式在得到NowClientTime後,即該函式命令傳遞到客戶端時,此時的ServerTime。
NowServerTime = PreServerTime + RTT/2;
當前的這個時間就應該是每個客戶端最後應該顯示的時間。為了分離,利用一個變數來接受差值NowServerTime - NowClientTime而不是直接的數值
void AXBlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequese)
{
//獲取接受時伺服器的時刻
float SeverTimeOfReceipt = GetWorld()->GetTimeSeconds();
ClientReportServerTime(TimeOfClientRequese, SeverTimeOfReceipt);
}
//Client Run GetWorld()->GetTimeSeconds() is ClientCurrentTime
void AXBlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest)
{
//計算RTT
float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest;
SingleTripTime = RoundTripTime * 0.5f;
//ServerCurrentTime
float CurrentServerTime = TimeServerReceivedClientRequest + SingleTripTime;
ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds();
}
Tick呼叫
該過程是確保客戶端始終保持同步,透過設定的時間間隔呼叫
void AXBlasterPlayerController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
SetHUDTime();
CheckTimeSync(DeltaTime);
}
//在遊戲過程中每隔一段時間同步伺服器時間到客戶端
void AXBlasterPlayerController::CheckTimeSync(float DeltaTime)
{
TimeSyncRunningTime += DeltaTime;
if (IsLocalController() && TimeSyncRunningTime > TimeSyncFrequency)
{
ServerRequestServerTime(GetWorld()->GetTimeSeconds());
TimeSyncRunningTime = 0.f;
}
}
HUD顯示時間
透過對不同遊戲狀態(比如遊戲中,中途加入遊戲或者等待)提供相應的偏移,然後加上之前獲得的NowServerTime即可
獲取ServerTime
float AXBlasterPlayerController::GetSeverTime()
{
//伺服器上執行
if (HasAuthority())
{
return GetWorld()->GetTimeSeconds();
}
//客戶端上執行需要加上偏移量,最終顯現的是伺服器上時間
else
{
return GetWorld()->GetTimeSeconds() + ClientServerDelta;
}
}