You are missing call for parent function in SetupInputComponent. Super::SetupInputComponent();
I wanted to do the same thing as you. This is the recommended way of handling input for some games. Itâs a perfectly valid question and you are not wanting to do anything the wrong way or an odd way.
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.
Even though it may no longer be of any interest to OP, it could help someone else who is interested in an answer to this question. One possible solution is to create a new C++ project with the third-person template. Then take a look at the generated Character class. Now move the relevant calls to your player controller class.
For a simple setup, this would be:
- SetupPlayerInputComponent (see below)
- DefaultMappingContext, MoveAction and LookAction properties
- Move and Look methods
- Copy/Paste the âAdd Input Mapping Contextâ code from BeginPlay, modifying as needed to work inside player controller.
void AMyPlayerController::::Move(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get<FVector2D>();
// find out which way is forward
const FRotator Rotation = GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
APawn* pawn = GetPawn();
pawn->AddMovementInput(ForwardDirection, MovementVector.Y);
pawn->AddMovementInput(RightDirection, MovementVector.X);
}
void AMyPlayerController::::Look(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D LookAxisVector = Value.Get<FVector2D>();
// add yaw and pitch input to controller
AddYawInput(LookAxisVector.X);
AddPitchInput(LookAxisVector.Y);
}
void AMyPlayerController::::OnPossess(APawn* aPawn)
{
Super::OnPossess(aPawn);
// Call setup player input
SetupPlayerInputComponent();
}
void AMyPlayerController::::SafeRetryClientRestart()
{
Super::SafeRetryClientRestart();
if (AcknowledgedPawn != GetPawn())
{
UWorld* World = GetWorld();
check(World);
SetupPlayerInputComponent();
}
}
void AMyPlayerController::StartFire(uint8 FireModeNum)
{
Super::StartFire(FireModeNum);
if (((IsInState(NAME_Spectating) && bPlayerIsWaiting) || IsInState(NAME_Inactive)) && !IsFrozen())
{
// Call setup player input
SetupPlayerInputComponent();
}
}
Regarding SetupPlayerInputComponent, this is not coming from AActor, but APawn. Thatâs why you canât override it from the player controller. If you look at DefaultPawn.cpp you can see that this method configures bindings of input events to their event handlers.
Implement SetupPlayerInputComponent in your player controller and override StartFire, SafeRetryClientRestart and OnPossess, calling base class (super) implementation and then calling your new SetupPlayerInputComponent under the same conditions that would have been called from the respective APlayerController methods. Your PC SetupPlayerInputComponent method takes no arguments and instead uses the AActor::InputComponent property.
void AMyPlayerController::SetupPlayerInputComponent()
{
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent)) {
// Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyPlayerController::Move);
// Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyPlayerController::Look);
}
else
{
MYGAME_LOG(nullptr, MyDebugLog, Error, false, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
}
}
Remember to setup DefaultMappingConntext and actions in your player controller blueprint.
I know, this is not a whole solution written out with full example code. However, it provides a thread to pull on to get this working. This is not the only way to implement this, but this approach will guarantee that it behaves similar to the ACharacter implementation. In case your wondering, Iâve implemented this in my own game, as described above, and it is working.
I too am trying to add the Enhanced Input via the Controller instead the Character (to allow that segregation).
Thanks @Rogue_Programmer for the help in solving the issue instead of a workaround!
Following your solution I couldnât find in the base classes where âStartFireâ, 'SafeRetryClientRestart ', and âOnPossessâ, should be using âSetupPlayerInputComponentâ, so I couldnât follow the example.
Would love to know where I missed them
BUT I did manage to make my inputs work just the way you are trying @Thalamus01.
Any chance (just like @Rogue_Programmer suggested), that you accidently didnât set up the Mapping and Input in the Controllerâs Blueprint?
This is my code in the controller:
void ATantrumPlayerController::BeginPlay()
{
Super::BeginPlay();
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
Subsystem->AddMappingContext(MappingContext, 0);
}
}
void ATantrumPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(InputComponent);
Input->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ATantrumPlayerController::RequestJumpAction);
Input->BindAction(LookAction, ETriggerEvent::Triggered, this, &ATantrumPlayerController::RequestLookAction);
Input->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ATantrumPlayerController::RequestMoveAction);
}
And this is the Controllerâs Blueprint inputs:
If it still doesnât work please let us know, I would love to try and help further!
Edit: wanted to add that Iâm using UE5.4, donât know if thatâs related in any way, but thought Iâd mention it