How to handle input from PlayerController instead of Pawn

I have a custom APlayerController class called AMyPlayerController and im trying to control my pawn from within it.

i am trying to use the Epic Games examples as a basis for my implementation but im get an Access violation reading location exception when i start the game:

below is how im binding the input axis and is what is causing the access violation exception

void AMyPlayerController::SetupInputComponent()
{
	//check(InputComponent);

	if(Cast<APlayerCharacter>(GetControlledPawn()) != NULL)
	{
		InputComponent->BindAxis("Walk", Cast<APlayerCharacter>(GetControlledPawn()), &APlayerCharacter::Walk);
		InputComponent->BindAxis("Strafe", Cast<APlayerCharacter>(GetControlledPawn()), &APlayerCharacter::Strafe);
	}
	else
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, "Cast<APlayerCharacter>(GetControlledPawn()) is NULL");
	}
}

I know i am getting something wrong in those two lines where i call the BindAxis function but i dont know how it should be done

if anyone has any idea how i can bind the axis to the Walk and Strafe functions of the PlayerCharacter that would be great!

Ohh so instead of calling AddMovementInput() from the pawn i want to call it from the playercontroller right?

If you get the UInputComponent::BindAxis() docs you’ll see that the 2nd parameter is an UserClass* (User = PlayerController) and on your calls you’re trying to assign the Character (pawn) class.

Try instead: InputComponent->BindAxis(“Walk”, IE_Axis, this, &APlayerCharacter::Walk);

And into your PlayerControllerClass get a Walk( ) method that access your pawn, check the Y value and make it move accordingly.

Will also help get more specific namings, as example your binding could be “YJoyAxis” and so on. :smiley:

Oops… I meant:
InputComponent->BindAxis(“Walk”, IE_Axis, this, &YourPlayerController::Walk);

Yep, the PlayerController is the class that holds SetupInputComponent( ) method.

After reading through the forums for a very long time i discovered that the pawn is where the input logic is supposed to be. the you possess a pawn the input goes to the pawn and when you possess a pawn that input is no longer sent to the pawn. so essentially pawns can only receive input when possessed by a controller. Someone please correct me if i am wrong but this is what i understand from the many threads i read.

after trying this i still got the exception so i decided to go with the way the templates handle the input. i have posted an answer as to why i decided to do it this way

Thank you for helping me creasso

I realize that this thread is already quite old, but in case anyone else is searching for an answer about how to do this, I decided to post my solution anyways.

You cannot bind a delegate of the pawn function directly in AMyPlayerController::SetupInputComponents because the Pawn will not yet be possessed when the function is called, so GetPawn() will return nullptr. Hence the error. My solution is to define a handler function in the PlayerController class itself, then delegate the actual implementation to the pawn during runtime.

TestPlayerController.cpp

void ATestPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();
	InputComponent->BindAxis("MoveForward", this, &ATestPlayerController::MoveForward);
}

void ATestPlayerController::MoveForward(float Value)
{
	auto pawn = Cast<ADefaultPawn>(this->GetPawn());
	if (pawn) 
	{
		pawn->MoveForward(Value);
	}
}

It is important to include the Super::SetupInputComponent(); line, as otherwise the input component will not be set up properly (I believe this is what threw the OP in his last version).

Since my Pawn inherits from DefaultPawn, no custom implementation of MoveForward is necessary. No relevant code was added. You obviously have to add an axis called "MoveForward" in the project settings. The solution was tested with engine version 4.7.5


In case anyone is wondering, here is my rationale for why I want to handle input in the PlayerController: As I understand it, the design intent behind the Pawn is to be the physical representation of a controlled object, while the PlayerController is intended to represent the Player’s will. From the PlayerController documentation:

A PlayerController is the interface between the Pawn and the human player controlling it. The PlayerController essentially represents the human player’s will.

One thing to consider when setting up your PlayerController is what functionality should be in the PlayerController, and what should be in your Pawn. It is possible to handle all input in the Pawn, especially for less complex cases. However, if you have more complex needs, like multiple players on one game client, or the ability to change characters dynamically at runtime, it might be better to handle input in the PlayerController. In this case, the PlayerController decides what to do and then issues commands to the Pawn (e.g. “start crouching”, “jump”).

Also, in some cases, putting input handling or other functionality into the PlayerController is necessary. The PlayerController persists throughout the game, while the Pawn can be transient. For example, in deathmatch style gameplay, you may die and respawn, so you would get a new Pawn but your PlayerController would be the same. In this example, if you kept your score on your Pawn, the score would reset, but if you kept your score on your PlayerController, it would not.

My take on this is the following: input deals with player intent, not physical motion, so I want to wrap the input handling in a PlayerController class while delegating the actual implementation of the result to the Pawn class. Let’s say the player presses W because he wants to move forward. This is a statement of intent and should be handled in PlayerController. Then, the PlayerController issues a command to the Pawn to physically move. The Pawn, being a physical class, then handles the nitty-gritty details like collision and actually changing the pawn’s position.

To me, handling input in the PlayerController is mostly preferable because of the neater encapsulation. I just don’t like to clutter a possibly AI-controlled Pawn with a lot of irrelevant human-centric code. The doc mentions some other good points.

2 Likes

That is incorrect. Pawns can receive input without a controller (see all the tutorial videos that just throw input logic into a player character). Input actions are consumed by the following in order : PlayerController > Level Blueprint > PlayerPawn Input | Unreal Engine Documentation

Just BTW, the code should execute faster when done like this (only 1 cast operation instead of potentially 3)

const APlayerCharacter* ControlledCharacter = Cast<APlayerCharacter>(GetControlledPawn());

if (ControlledCharacter)
     {
         InputComponent->BindAxis("Walk", ControlledCharacter, &APlayerCharacter::Walk);
         InputComponent->BindAxis("Strafe", ControlledCharacter, &APlayerCharacter::Strafe);
     }
     else
     {
         GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, "Cast<APlayerCharacter>(GetControlledPawn()) is NULL");
     }
1 Like

@alpaka solution is the correct way of doing this. A couple of things. The controller has not yet possessed a pawn during OnSetupInputComponent. The other thing is you need to use GetPawn() and not GetControlledPawn(). Make sure you NULL check to issue to make sure the pawn is valid before attempting to access it.

“input deals with player intent, not physical motion”

Agreed++.

Last project I did the player could switch control between 3 different pawns - a regular first person character, a flying drone, and a computer interface. There were also interface hotkeys that had nothing to do with any pawn.

This worked nicely:

  • set up all key bindings in the PlayerController
  • movement key functions would redirect to the controlled pawn
  • all player controllable pawns sub-classed a base class, so it was trivial to direct input to controlled pawn
  • PlayerController was one-stop place for all input that wasn’t related to Pawn movement (UI hotkeys huge example).
1 Like

Yes, but what about Tab Menu functionality (Score Menu in MP game for example) or escape menu? If theres no pawn, then player cant even quit!