Working with the Enhanced Input System

Hi all,

I’ve been following this course in Unreal 5.1 and using the enhanced input system thanks to this excellent guide by @hwenloff:

https://community.gamedev.tv/t/how-to-for-ue5-1-enhanced-input-system/221375

In this lecture we want to call the Shoot method on ShooterCharacter from within BTTask_Shoot, but if you’ve used the enhanced input system, your signature looks like this:

void AShooterCharacter::Shoot(const FInputActionValue& Value)

We don’t have an input action to pass in BTTask_Shoot and as the argument in our function definition is passed by reference, we can’t just give it a nullptr.

So how do we fix it?


The Simple Solution

The easiest way to fix this problem is to add a separate AI version of our Shoot function with a different name that takes 0 arguments.
e.g.

public:
void AIShoot();
private:
void Shoot(const struct FInputActionValue& Value);

Then in our implementation, we can just duplicate the logic:

void AShooterCharacter::AIShoot()
{
    Gun->PullTrigger();
}

void AShooterCharacter::Shoot(const FInputActionValue& Value)
{
    Gun->PullTrigger();
}

Finally, in the ExecutedTask function in BTTask_Shoot.cpp, we just need to ensure we’re calling the new AI version of our function:

Character->AIShoot();

A Better Solution?

I’m not a fan of the simple solution because regardless of whether it is the player or AI shooting, the functionality will be the same. As such, I think the functions should share the same name. This is where function overloading comes in.

First, we create an overload for the Shoot function which takes 0 parameters and put all the shooting logic in there. Inside the overload of the function that takes an FInputActionValue&, we call the overload that takes no parameters:

ShooterCharacter.h

public:
    void Shoot();
private:
    void Shoot(const struct FInputActionValue& Value);

ShooterCharacter.cpp

void AShooterCharacter::Shoot()
{
    Gun->PullTrigger();
}

void AShooterCharacter::Shoot(const FInputActionValue& Value)
{
    Shoot();
}

If you compile now, you’ll get an error on the line where you bind the input action to the Shoot function:

error C2668: 'UEnhancedInputComponent::BindAction': ambiguous call to overloaded function

BindAction previously had no issue binding to the Shoot method because there was only one function with that name, but now it’s overloaded, it’s not clear which version of the function should be used.

To fix this, add the following line:

FEnhancedInputActionHandlerValueSignature::TMethodPtr<AShooterCharacter> ShootMethodPointer = &AShooterCharacter::Shoot;

Now instead of trying to bind the input action to &AShooterCharacter::Shoot, instead bind it to this new method pointer:

PlayerEIComponent->BindAction(InputFire, ETriggerEvent::Started, this, ShootMethodPointer);

This should compile successfully and hey presto, player and AI shooting are both functioning as intended!


Why This Works

This line:

FEnhancedInputActionHandlerValueSignature::TMethodPtr<AShooterCharacter> ShootMethodPointer = &AShooterCharacter::Shoot;

creates a method pointer for a function in the AShooterCharacter class that takes one const FInputActionValue& argument.

It’s not clear why this new method pointer we declared will take a const FInputActionValue& as an argument - however, if we go to the definition of FEnhancedInputActionHandlerValueSignature, we can see it’s a delegate that is being created by a macro called DECLARE_DELEGATE_OneParam:

DECLARE_DELEGATE_OneParam(FEnhancedInputActionHandlerValueSignature, const FInputActionValue&);`

This is where it is declared that FEnhancedInputActionHandlerValueSignature is a delegate that must take one parameter of type const FInputActionValue&.

So when we declare ShootMethodPointer and assign it &AShooterCharacter::Shoot, we assign the overload of the function that takes an argument of type const FInputActionValue& Value. When we then bind our input action, there’s no longer any ambiguity about which function overload to use because we’ve specified that we want to use the overload that takes an input action argument.


That was pretty-long winded, so if you made it this far, thanks for reading! :slight_smile:

3 Likes

Hi there!

Thanks for this :slight_smile:

I’m finally back to complete this lesson after lots of life stuff happened, and I just got to this point.
Glad you found my write-up on how one can use the enhanced input system useful!

I figured out two ways of solving the situation here as well;

  1. We can pass {} as an argument when we call our Shoot method. This will do a value-initialisation, creating a default object of the required type. Since we don’t actually use the value of the const FInputActionValue & Value in this case, that’s perfectly fine. So, character->Shoot({}); in our BTTask_Shoot.cpp will make it work fine as-is.
  2. Now… Since we don’t use the value, we actually don’t need the value when we define the function to start with! So, instead of
void Shoot(const FInputActionValue & Value);

in our ShooterCharacter, we can just do

void Shoot();

and thus also

void AShooterCharacter::Shoot()
{	
	gun->PullTrigger();
}

and call the function just like Sam does. It still binds nicely to the Enhanced Input System, as far as I can tell.

Either of those methods work fine as well!

Kind regards,
Håkan

Privacy & Terms