Player controller input handling

Hi all,
I’m trying to make APlayerController handle the input for ACharacter, although I have a problem where ACharacter is linked to APlayerController but not the other way around nor both accessing each other.
I’m trying to make my code more modular, and would lake to create different controllers for a particular character e.g. controller, mouse, keyboard .etc

Here’s what I have so far… And ideas what the problem is or any tips, I’m a beginner to the framework although am quite fluent in C++ with a dislike for blueprints (So please don’t suggest any thank you).

The Game Mode:



#include "TheGameCharacterTest.h"
#include "TheGameCharacterTestGameMode.h"
#include "CharacterClasses/MainCharacter.h"
#include "CharacterControllers/MainCharacterKeyboard.h"

ATheGameCharacterTestGameMode::ATheGameCharacterTestGameMode()
{
	DefaultPawnClass = AMainCharacter::StaticClass();
	PlayerControllerClass = AMainCharacterKeyboard::StaticClass();
}


The Character:



#include "TheGameCharacterTest.h"
#include "MainCharacter.h"

// Sets default values
AMainCharacter::AMainCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	AMainCharacter::AttachCamera();
	AutoPossessPlayer = EAutoReceiveInput::Player0;
}

void AMainCharacter::AttachCamera()
{
	// Create a CameraComponent	and arm
	GetCapsuleComponent()->SetWorldLocation(FVector(0.0f, 0.0f, 0.0f));
	CameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
	CameraSpringArm->AttachParent = GetCapsuleComponent();
	CameraSpringArm->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
	CameraSpringArm->SetRelativeRotation(FRotator(0.0f, 0.0f, 0.0f));
	CameraSpringArm->TargetArmLength = 0.0f;
	CameraSpringArm->bEnableCameraLag = true;
	CameraSpringArm->CameraLagMaxDistance = 10.0f;
	CameraSpringArm->CameraLagSpeed = 5.0f;

	FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
	FirstPersonCameraComponent->AttachTo(CameraSpringArm, USpringArmComponent::SocketName);
	FirstPersonCameraComponent->RelativeLocation = FVector(0.0f, 0.0f, 64.f); // Position the camera
	FirstPersonCameraComponent->RelativeRotation = FRotator(0.0f, 0.0f, 0.0f);
}

// Called when the game starts or when spawned
void AMainCharacter::BeginPlay()
{
	Super::BeginPlay();	
}

// Called every frame
void AMainCharacter::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );
}

// Called to bind functionality to input
//void AMainCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
//{
//	Super::SetupPlayerInputComponent(InputComponent);
//}


The Player keyboard controller



#include "TheGameCharacterTest.h"
#include "MainCharacterKeyboard.h"
#include "Engine.h"

AMainCharacterKeyboard::AMainCharacterKeyboard()
{
	Pawn = GetPawn();
	if (Pawn != NULL) GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Green, FString::Printf(TEXT("Pawn")));
	else GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Green, FString::Printf(TEXT("No Pawn")));
	this->Possess(Pawn);
};


void AMainCharacterKeyboard::SetupInputComponent() 
{
	Super::SetupInputComponent();
	static const FName InputComponentName(TEXT("TravelInputComponent0"));
	InputComponent = ConstructObject<UInputComponent>(UInputComponent::StaticClass(), this, InputComponentName);

	check(InputComponent);

	if (InputComponent != NULL)
	{
		InputComponent->BindAxis("MoveForward", this, &AMainCharacterKeyboard::MoveForward);
	}
};


void AMainCharacterKeyboard::MoveForward(float Value)
{
	if (Value != 0.0f && (Pawn != NULL))
	{
		GEngine->AddOnScreenDebugMessage(-1, 0.005f, FColor::Green, FString::Printf(TEXT("Moveing %f"), Value));
		Pawn->AddMovementInput(Pawn->GetActorForwardVector(), Value);

		//FRotator const ControlSpaceRot = GetControlRotation();
		// transform to world space and add it
		//GetPawn()->AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Y), Value);

	}
};


Hi! Did you find a solution? I have almost the same problem. Please check my thread and can you give me any advice how to resolve it?

I’m not sure if I understand your objective or your problem, so I’ll give it a shot:

Input devices in UE4 are automatically-assigned to PlayerControllers in order:

  • kb/m and first controller are always assigned to the first registered local PlayerController
  • the second controller is always assigned to the second registered local PlayerController
  • the third controller is always assigned to the third registered local PlayerController
  • etc, etc…

You want your PlayerController to define inputs for kb/m and also for controller, right? If so I’d suggest handling both keyboard and controller input in the same PlayerController. Actually, most of the work will be done in Project settings > Input. Here’s an example of how input would be defined for “Attack”:

Now, both the keyboard and the controller will know how to handle the “Attack” action. So in your general-purpose PlayerController, when you bind the “Attack” action to some method in your character, it’ll work properly on kb and on controller. No need for separate PlayerControllers (it wouldn’t really make sense, structurally, to separate PlayerControllers by input device, since that separation is already handled under-the-hood by the Input System’s abstraction layer)

PS: I feel your pain regarding BPs!

Thank you for your reply you have cleared some things I was not able to understand. Anyway it was not the issue everything was setup in Input section. The problem was I derived MyPawn from APawn but not from ADefaultPawn class, which has ability to move.

1 Like

Sorry for the necropost, but I might have found the original problem. I guess it answered my own question in a weird way. :slight_smile: In which case, I’m trying to use Player Controller for input without using any Pawn or Character, and I used your code … and then it hit me.

The problem is that you created an input component out of thin air without attaching it to your character. With any APawn-derived actor, I guess the class automatically creates its own user input (so no need to use CreateObject or NewObject) and you must bind its actions via the virtual SetupPlayerInputComponent method. APawn::SetupPlayerInputComponent | Unreal Engine Documentation.

In my case, using APlayerController, I was wondering where the hell was this Input Component being attached to. And … there is an Input Component stack in which I can push the newly created Input into it. APlayerController::PushInputComponent | Unreal Engine Documentation . Without it, there is no way this controller will receive any input.

Make this day a great day.

EDIT: After looking at the APlayerController code, I see you don’t need to create a new input component. When you call Super::SetupInputComponent(), InputComponnent is already created, you just need to add your own bindings.


void APlayerController::SetupInputComponent()
{
	// A subclass could create a different InputComponent class but still want the default bindings
	if (InputComponent == NULL)
	{
		InputComponent = NewObject<UInputComponent>(this, TEXT("PC_InputComponent0"));
		InputComponent->RegisterComponent();
	}

        (...)


In your code, you simply override the existing one and, just like I said earlier, without registering or adding it to the input stack, it’s not going to do anything.

Gonna stick this here because although @DrHobo is correct I still needed a little more info to flush out my implementation. This article was very clear.

Binding Input in C++ with Unreal Engine 4 | Unrealistic

2 Likes

My advice, which you may or may not agree with:

Don’t put input into the character/pawn; put input in the player controller.

All the sample projects read the input in the character, which is an easy way to get started, but will just cause problems if you ever want to switch pawns for a player, or go networked.

Also, don’t use automatic input binding for ability system components. Wire up the buttons in the player controller, and have them invoke the appropriate parts on the ASC. (This goes for confirm/cancel, too.)