UE5学习笔记25-游戏中时间同步

一、原因

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;
	}
}
相关推荐
无敌贵点大王2 小时前
RTThread学习记录11——RT-Thread 设备模型吃透:UART/ADC/PWM/PIN 到底有什么区别?
c语言·stm32·学习·链表
是翎4 小时前
深度长文|2026产品经理AI实践手册
人工智能·深度学习·学习·自然语言处理·数据挖掘
Linux-lucky4 小时前
32-38-Linux学习之旅之MySQL综合
linux·运维·学习·mysql·ubuntu
知识分享小能手5 小时前
C++ 学习教程,从入门到精通,C++ 入门知识 — 完整知识点(1)
开发语言·c++·学习
马可家的菠萝6 小时前
你收藏的资料,有多少真的被你第二次打开过?
笔记·知识管理·效率提升·个人知识库
小刘在重生~7 小时前
Java Lock 显式锁案例|ReentrantLock 解决多线程安全问题(超详细实战)
java·笔记·面试
职豚求职小程序7 小时前
中国人寿27秋招笔试[特殊字符]北森机考|备考攻略
笔记
江湖人称菠萝包8 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter9-Qt Charts
笔记·qt·qt5
乐橙开放平台8 小时前
从「多套客户端」到一套开放能力:乐橙视频监控能力复盘
笔记·物联网·自动化·音视频·智能家居
AI职业加油站8 小时前
数据要素价值释放年:大数据治理工程师,站上职业新风口
人工智能·学习·职场和发展·数据分析·职场发展