Bind input in custom Actor

Hello, I am having troubles binding input actions and axis in my custom actor. Everything runs good but my input functions are not called.



ACOMCameraActor::ACOMCameraActor(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	PrimaryActorTick.bCanEverTick = true;
	bFindCameraComponentWhenViewTarget = true;

	CameraTarget = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("Camera Target"));
	RootComponent = CameraTarget;

	CameraComponent = PCIP.CreateDefaultSubobject<UCameraComponent>(this, TEXT("Camera Component"));
	CameraComponent->AttachTo(RootComponent);

	InputComponent = ConstructObject<UInputComponent>(UInputComponent::StaticClass(), this);
	InputComponent->RegisterComponent();
	InputComponent->bBlockInput = bBlockInput;
}

void ACOMCameraActor::BeginPlay()
{
	Super::BeginPlay();

	if (GetOwner() && Cast<APlayerController>(GetOwner()))
	{
		PCOwner = Cast<APlayerController>(GetOwner());
	}

	if (PCOwner)
	{
		EnableInput(PCOwner);
	}

	if (InputComponent)
	{
		GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "BINDING INPUT");
		InputComponent->BindAction("CameraZoomIn", EInputEvent::IE_Pressed, this, &ACOMCameraActor::ZoomIn);
		InputComponent->BindAction("CameraZoomOut", EInputEvent::IE_Pressed, this, &ACOMCameraActor::ZoomOut);

		InputComponent->BindAction("EnableCameraRot", EInputEvent::IE_Pressed, this, &ACOMCameraActor::StartRotation);
		InputComponent->BindAction("EnableCameraRot", EInputEvent::IE_Released, this, &ACOMCameraActor::StopRotation);
	}
}

void ACOMCameraActor::ZoomIn()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "Zoom In");
}

void ACOMCameraActor::ZoomOut()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "Zoom Out");
}

void ACOMCameraActor::StartRotation()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "Start Rot");
}

void ACOMCameraActor::StopRotation()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "Stop Rot");
}


BINDING INPUT log displays, so I am sure there is not issue in references. Also my bindings in project settings are correct because I am just rewriting working blueprint graph into C++.

Do you know any ideas why I am not able to link my methods to input ?

Hi, Nonder!

When a controller possesses a pawn he calls the SetupPlayerInputComponent() procedure to bind its user controls. Then, I recommend you to put those bind definitions in that procedure!

Ex.:

DOFCharacter.h:


UCLASS()
class DECLINEOFFEAR_API ADOFCharacter : public ACharacter
{
...
protected:

	virtual void SetupPlayerInputComponent(class UInputComponent *InputComponent) override;
...
}

DOFCharacter.cpp:


...
void ADOFCharacter::SetupPlayerInputComponent(class UInputComponent *InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);

	check(InputComponent);
	
	if (InputComponent)
	{
		InputComponent->BindAction("CameraZoomIn", EInputEvent::IE_Released, this, &ADOFCharacter::CameraZoomIn);
		InputComponent->BindAction("CameraZoomOut", EInputEvent::IE_Released, this, &ADOFCharacter::CameraZoomOut);
	}
}
...

See if it works for you!

Hey Nonder, your set up code looks right to me.

If you have engine code could you put a break point in your BeginPlay function and APlayerController::InitInputSystem? My best guess is that your call to enable input is prior to the input system being initialized which clears the input stack. I think this is probably a mistake in how we are doing things and I need to come up with a way that ensures that you don’t run in to problems like this, but before I start messing with it, worth checking if that is indeed the issue.

Thank you guys for replies.

fireapache@ Thank you for showing some example code. I know about this common solution however I would like to link input directly in my custom actor.
Marc Audy@ That it is possible. I had not think about that case and will do some more research later. I do not use engine source now but I tried this and input still does not work:


void ACOMCameraActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	Test++;

	if (Test == 60)
	{
		GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "60 NOW");
		EnableInput(PCOwner);
		if (InputComponent)
		{
			InputComponent->BindAction("CameraZoomIn", EInputEvent::IE_Pressed, this, &ACOMCameraActor::ZoomIn);
			InputComponent->BindAction("CameraZoomOut", EInputEvent::IE_Pressed, this, &ACOMCameraActor::ZoomOut);

			InputComponent->BindAction("EnableCameraRot", EInputEvent::IE_Pressed, this, &ACOMCameraActor::StartRotation);
			InputComponent->BindAction("EnableCameraRot", EInputEvent::IE_Released, this, &ACOMCameraActor::StopRotation);
		}
	}
}

If you want to delay those binds you can use SetTimer.

Sorry if I can’t help you much more.

I just did a quick test and it showed that the init occurs before the begin play so that indeed is not the problem.

Looking closer at your code I see a couple of issues:

You shouldn’t be calling RegisterComponent in the constructor. I don’t think it will have negative effects, but it is definitely unnecessary.
The way you are allocating the InputComponent is wrong. You should be using the PCIP.CreateDefaultSubObject syntax.

Except … that InputComponents are transient. It doesn’t really make sense to be allocating it in the constructor like that. When you call EnableInput that should allocate the InputComponent for you if need be, however, I set it up to only do it if there are blueprint bindings. I’m not feeling that is really correct now, similarly we try to preserve a tiny bit of performance by preventing an InputComponent without bindings from being put on the component stack (another thing I’m not really agreeing with myself now that I look back at it). If you construct your input component with the 3 lines you have in the constructor where you call EnableInput. Then do the bindings. THEN call enable input, I think you should be working.

Thank you very much Marc, this worked!


void ACOMCameraActor::BeginPlay()
{
	Super::BeginPlay();

	if (GetOwner() && Cast<APlayerController>(GetOwner()))
	{
		PCOwner = Cast<APlayerController>(GetOwner());
	}

	if (PCOwner)
	{
		InputComponent = ConstructObject<UInputComponent>(UInputComponent::StaticClass(), this, "Input Component");
		InputComponent->bBlockInput = bBlockInput;

		if (InputComponent)
		{
			InputComponent->BindAction("CameraZoomIn", EInputEvent::IE_Pressed, this, &ACOMCameraActor::ZoomIn);
			InputComponent->BindAction("CameraZoomOut", EInputEvent::IE_Pressed, this, &ACOMCameraActor::ZoomOut);

			InputComponent->BindAction("EnableCameraRot", EInputEvent::IE_Pressed, this, &ACOMCameraActor::StartRotation);
			InputComponent->BindAction("EnableCameraRot", EInputEvent::IE_Released, this, &ACOMCameraActor::StopRotation);

			EnableInput(PCOwner);
		}	
	}