Description

Context

AController has a blueprint assignable delegate that fires when the possessed pawn changes. This should fire in both singleplayer and networked games. The bug is reproducible in single player.

Problem

When a PlayerController possesses the same pawn again, for example as happens inside AGameModeBase::FinishRestartPlayer:

 

void AGameModeBase::FinishRestartPlayer(AController* NewPlayer, const FRotator& StartRotation)
{
    NewPlayer->Possess(NewPlayer->GetPawn()); 
    ...
}

then the broadcasting of OnPossessedPawnChanged should reflect that the controller ends up possessing the pawn (the same pawn in this case). Currently, it only fires a broadcast for the unpossession which is incorrect.

Suggested Fix

Either broadcast OnPossessedPawnChanged zero times when repossessing same pawn, or twice (pawn -> null on unpossession, null -> pawn on repossession). I lean towards firing twice, since existing projects may depend on OnPossessedPawnChanged already being redundantly fired for the unpossession for cleanup. The pawn also receives a redundant PossessedBy() call, so I believe it's better to remain consistent in this.

 

 

[Image Removed]

Steps to Reproduce

Use the ThirdPerson template and create a PlayerController blueprint. Make the game mode use that class as PlayerController class.

  • In the PlayerController blueprint on BeginPlay, bind an event to OnPossessedPawnChanged. Print out the name of the old and new pawn.
  • In the PlayerController native class, create a BlueprintCallable function 'RestartMe' to restart the player via game mode:

 

void AMyPlayerController::RestartMe()
{
    GetWorld()->GetAuthGameMode()->RestartPlayer(this);
} 
  • In the PlayerController blueprint, call that function with a debug key
  • Start PIE in single player.
  • Press the debug key to restart the player.
  • Observe: OnPossessedPawnChanged fires once with OldPawn = current pawn, NewPawn = null. It doesn't fire again, with NewPawn = a valid pawn.
  • Expected: OnPossessedPawnChanged either fires zero times (when repossessing same pawn), or twice (pawn->null, null->pawn).

 

Callstack

Callstack is non-fatal, just for reference. This is the broadcast on unpossession.  The broadcast on repossession is missing.

     [Inline Frame] UnrealEditor-Engine.dll!FOnPossessedPawnChanged::Broadcast(APawn *) Line 20    C++
     UnrealEditor-Engine.dll!AController::UnPossess() Line 410    C++
>    UnrealEditor-Engine.dll!APlayerController::OnPossess(APawn * PawnToPossess) Line 889    C++
     UnrealEditor-Engine.dll!AController::Possess(APawn * InPawn) Line 348    C++
     UnrealEditor-Engine.dll!AGameModeBase::FinishRestartPlayer(AController * NewPlayer, const UE::Math::TRotator<double> & StartRotation) Line 1382    C++
     UnrealEditor-Engine.dll!AGameModeBase::RestartPlayerAtPlayerStart(AController * NewPlayer, AActor * StartSpot) Line 1326    C++
     UnrealEditor-Engine.dll!AGameModeBase::RestartPlayer(AController * NewPlayer) Line 1277    C++
     UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) Line 7540    C++
     UnrealEditor-CoreUObject.dll!UObject::CallFunction(FFrame & Stack, void * const Z_Param__Result, UFunction * Function) Line 1157    C++
 

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-328292 in the post.

0
Login to Vote

Unresolved
ComponentUE - Gameplay
Affects Versions5.55.6
Target Fix5.8
CreatedSep 17, 2025
UpdatedSep 17, 2025
View Jira Issue