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!