How Can I prevent Controlled Pawns from Being Deleted On Reset?

Ok, this resetting behavior is built in at a few places in C++:

  1. APawn::Reset()
  2. APlayerController::Reset()
  3. APlayerController::ClientReset_Implementation()

The cleanest solution I find is to make subclasses and override the default behaviors. I made 3:

ResettablePawn.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "ResettablePawn.generated.h"

UCLASS()
class BUMPERCROP_API AResettablePawn : public APawn
{
	GENERATED_BODY()

public:	

	AResettablePawn(); //constructor

	virtual void Reset() override;

};

ResettablePawn.cpp

#include "ResettablePawn.h"
#include "Engine/GameEngine.h"

// constructor
AResettablePawn::AResettablePawn()
{
	PrimaryActorTick.bCanEverTick = true;
}

void AResettablePawn::Reset()
{
	K2_OnReset();
}

(K2_OnReset() is the function that triggers the Blueprint OnReset event)

ResettablePlayerController.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ResettablePlayerController.generated.h"

UCLASS()
class BUMPERCROP_API AResettablePlayerController : public APlayerController
{
	GENERATED_BODY()
	
public:

	AResettablePlayerController(); //constructor

	virtual void Reset() override;

};

ResettablePlayerController.cpp

#include "ResettablePlayerController.h"

AResettablePlayerController::AResettablePlayerController()
{
	PrimaryActorTick.bCanEverTick = true;

}


void AResettablePlayerController::Reset()
{

	K2_OnReset();

}

That takes care of the first two functions, but the third one isn’t virtual. For that one, we can override the GameModeBase::ResetLevel() function, keep most of the code the same, but remove the client reset call.

The client reset would need to be replaced with a different function if the game were networked, but that’s out of scope for now.

ResettableGameMode.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameMode.h"
#include "ResettableGameMode.generated.h"

/**
 * 
 */
UCLASS()
class BUMPERCROP_API AResettableGameMode : public AGameMode
{
	GENERATED_BODY()
	
public:
	virtual void ResetLevel() override;
};

ResettableGameMode.cpp

#include "ResettableGameMode.h"
#include "EngineUtils.h"
#include "Engine/LevelScriptActor.h"

void AResettableGameMode::ResetLevel()
{
	UE_LOG(LogGameMode, Verbose, TEXT("Reset %s"), *GetName());

	// Reset ALL controllers first
	for (FConstControllerIterator Iterator = GetWorld()->GetControllerIterator(); Iterator; ++Iterator)
	{
		AController* Controller = Iterator->Get();
		Controller->Reset();
	}

	// Reset all actors (except controllers, the GameMode, and any other actors specified by ShouldReset())
	for (FActorIterator It(GetWorld()); It; ++It)
	{
		AActor* A = *It;
		if (IsValid(A) && A != this && !A->IsA<AController>() && ShouldReset(A))
		{
			A->Reset();
		}
	}

	// Reset the GameMode
	Reset();

	// Notify the level script that the level has been reset
	ALevelScriptActor* LevelScript = GetWorld()->GetLevelScriptActor();
	if (LevelScript)
	{
		LevelScript->LevelReset();
	}
}

Then I just switched the parent classes of my custom Blueprint pawn, player controller, and game mode to these new versions.


This is perhaps a bit overkill - another option is just creating a new type of Reset that didn’t have these built-in behaviors might be easier, but at least it was good getting practice digging into the engine.