Setting up custom Player Controller class in third person C++ template project

After having added some custom input logic to the third person C++ project I read that a best practice is to put all input logic in a custom Player Controller class instead of directly in the Pawn. I am endeavoring to do this.

In my game mode class constructor I’ve added:

PlayerControllerClass = APTPlayerController::StaticClass();

This will then trigger my APTPlayerController::SetupInputComponent() to be called. This makes me think I’m on the right path. The contents of that function is as follows:

void APTPlayerController::SetupInputComponent()
{
	APlayerController::SetupInputComponent();

	// Perhaps this is where I've gone wrong? This is the only way I've found
	// to lookup the pawn class so that this player controller can possess it.
	for(TObjectIterator<APewterStrongCharacter> Itr; Itr; ++Itr)
	{
		if(Itr->IsA(APewterStrongCharacter::StaticClass()))
		{
			Possess(*Itr);
			break;
		}
	}

	ACharacter* pCharacter = GetCharacter();
	if(pCharacter)
	{

		InputComponent->BindAction("Jump", IE_Pressed, pCharacter, &ACharacter::Jump);
		InputComponent->BindAction("Jump", IE_Released, pCharacter, &ACharacter::StopJumping);

		InputComponent->BindAxis("MoveForward", this, &APTPlayerController::MoveForward);
		InputComponent->BindAxis("MoveRight", this, &APTPlayerController::MoveRight);

		// We have 2 versions of the rotation bindings to handle different kinds of devices differently
		// "turn" handles devices that provide an absolute delta, such as a mouse.
		// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
		InputComponent->BindAxis("Turn", pCharacter, &APawn::AddControllerYawInput);
		InputComponent->BindAxis("TurnRate", this, &APTPlayerController::TurnAtRate);
		InputComponent->BindAxis("LookUp", pCharacter, &APawn::AddControllerPitchInput);
		InputComponent->BindAxis("LookUpRate", this, &APTPlayerController::LookUpAtRate);

		InputComponent->BindAxis("CameraZoom", this, &APTPlayerController::CameraZoom);
		InputComponent->BindAxis("CameraZoomController", this, &APTPlayerController::CameraZoomController);

		// handle touch devices
		InputComponent->BindTouch(IE_Pressed, this, &APTPlayerController::TouchStarted);
		InputComponent->BindTouch(IE_Released, this, &APTPlayerController::TouchStopped);
	}
}

In my custom player controller class I copy over all of the input logic from the ThirdPersonCharacter class. I then comment out the corresponding input logic in the ThirdPersonCharacter class. And lastly in the ThirdPersonCharacter blueprint this is actually spawned on the map, I change “AI Controller Class” from the default “PlayerController” to my custom “PTPlayerController”.

Even though my SetupInputComponent is called, the pawn seems to be possess correctly, the player controller has a character from GetCharacter() class, none of my input callbacks are hit when I press buttons during testing.

And a secondary but related mystery is I seem to be unable to add a constructor for my custom Player Controller class. I need it to initialize some state variables but any arrangement I try results in “already defined” errors from the compiler.

I was able to finally solve this! It was (as usual) a very tiny oversight. In my player controller PlayerTick() function I was incorrectly calling the parent PlayerTick() function. My code originally looked like this:

void APTPlayerController::PlayerTick(float DeltaTime)
{
	APlayerController::Tick(DeltaTime);
}

I’m not sure why the compiler let me get away with this, since I called it “Tick” instead of “PlayerTick”, but when it compiled I thought I had it right and never looked closely at this again. But finally I realized Unreal’s preferred way of calling to the parent class is to use the Super call. The fix was this:

void APTPlayerController::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime);
}

Hopefully this post helps others who have stumbled in the same way I have.