Unreal 断线重连、AI控制

浅析UE5 DS的断线重连机制
wizardcell.com
ReloginProject

流程说明

当服务器判断某个玩家掉线的时候,会删除对应的PlayerController

void APlayerController::Destroyed()
	if (Player == NULL && GetLocalRole() == ROLE_Authority)
		// 销毁Pawn
		PawnLeavingGame();
			GetPawn()->Destroy();
	else
		UnPossess();
	Super::Destroyed();

void AController::Destroyed()
	if (GetLocalRole() == ROLE_Authority && PlayerState != NULL)
		GameMode->Logout(this);
		CleanupPlayerState();
	UnPossess();

在GameMode的Logout重载内,会复制一份当前PC的PS,在之后重新登录的时候恢复PS:

void AGameMode::Logout( AController* Exiting )
	RemovePlayerControllerFromPlayerCount(PC);
	AddInactivePlayer(PC->PlayerState, PC);
		APlayerState* const NewPlayerState = PlayerState->Duplicate();
			DispatchCopyProperties(NewPlayerState);
				// PS子类需要重写这个函数,保留自己的信息到复制PS上
				CopyProperties(PlayerState);
		GameState->RemovePlayerState(NewPlayerState);
		NewPlayerState->SetReplicates(false);
		// 保留一定时间,时间到了后还没重连就不管了
		NewPlayerState->SetLifeSpan(InactivePlayerStateLifeSpan);
		InactivePlayerArray.Add(NewPlayerState);

在服务器检测到新加入一个Player时,会创建一个新的PlayerController:

APlayerController* UWorld::SpawnPlayActor(UPlayer* NewPlayer, ENetRole RemoteRole, const FURL& InURL, const FUniqueNetIdRepl& UniqueId, FString& Error, uint8 InNetPlayerIndex)
	if (AGameModeBase* const GameMode = GetAuthGameMode())
		APlayerController* const NewPlayerController = GameMode->Login(NewPlayer, RemoteRole, *InURL.Portal, Options, UniqueId, Error);
			APlayerController* const NewPlayerController = SpawnPlayerController(InRemoteRole, Options);
			InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
		NewPlayerController->SetRole(ROLE_Authority);
		// 附加Player
		NewPlayerController->SetPlayer(NewPlayer);
		GameMode->PostLogin(NewPlayerController);
			FindInactivePlayer(NewPlayer);
			HandleStartingNewPlayer(NewPlayer); // AGameModeBase::PostLogin

还原之前保存的PS:

bool AGameMode::FindInactivePlayer(APlayerController* PC)
	for (int32 i=0; i < InactivePlayerArray.Num(); i++)
		if ((bUseUniqueIdCheck && (CurrentPlayerState->GetUniqueId() == PC->PlayerState->GetUniqueId())) ||
				(!bUseUniqueIdCheck && bHasValidNetworkAddress && (FCString::Stricmp(*CurrentPlayerState->SavedNetworkAddress, *NewNetworkAddress) == 0) && (FCString::Stricmp(*CurrentPlayerState->GetPlayerName(), *NewName) == 0)))
			APlayerState* OldPlayerState = PC->PlayerState;
			PC->PlayerState = CurrentPlayerState;
			PC->PlayerState->SetOwner(PC);
			OverridePlayerState(PC, OldPlayerState);
				// 复制当前的PS信息给保存下来的PS
				PC->PlayerState->DispatchOverrideWith(OldPlayerState);
					OverrideWith(PlayerState);
					ReceiveOverrideWith(PlayerState);
			OldPlayerState->Destroy();

在AGameModeBase::PostLogin内,完成PS初始化后,调用HandleStartingNewPlayer,开始匹配、创建角色

void AGameMode::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
	if (IsMatchInProgress() && PlayerCanRestart(NewPlayer))
		RestartPlayer(NewPlayer);
	else if (GetMatchState() == MatchState::WaitingToStart)
		StartMatch();
void AGameModeBase::RestartPlayer(AController* NewPlayer)
	AActor* StartSpot = FindPlayerStart(NewPlayer);
	RestartPlayerAtPlayerStart(NewPlayer, StartSpot);
		APawn* NewPawn = SpawnDefaultPawnFor(NewPlayer, StartSpot);
			return SpawnDefaultPawnAtTransform(NewPlayer, Transform);
				APawn* ResultPawn = GetWorld()->SpawnActor<APawn>(PawnClass, SpawnTransform, SpawnInfo);

修改

  • 重写PawnLeavingGame
    • 服务器在判断玩家掉线的时候,不进行角色删除
  • 重写AddInactivePlayer
    • 改为创建默认AIController,控制该目标
    • 将PC的PS给AIController持有
  • 重写FindInactivePlayer
    • 当玩家重连的时候,寻找到之前持有PS的AIController
    • 取得PS和Pawn的所有权
  • AGameModeBase::RestartPlayer
    • 因为已经存在Pawn,所以不会再次创建

你可能感兴趣的:(unreal)