Tutorial: Player Mappable Keys using Enhanced Input

An overview of how to set up player mappable keys for your game using Enhanced Input.

https://dev.epicgames.com/community/learning/tutorials/Vp69/unreal-engine-player-mappable-keys-using-enhanced-input

1 Like

This was very helpful. Thank you!

1 Like

Thank you for the tutorial @BenjaFriend22!

I’m trying to utilize the slot feature in my input settings menu and seem to have hit a barrier. I can’t set default keys for secondary movement mappings. In the first picture of the tutorial, it looks like Fortnight has the arrow keys as the default secondary mappings for movement. Are they set in an Input Mapping Context?

Lyra’s Melee binding in ShooterCore utilizes the Second slot for the secondary binding of ā€œEā€:

ā€œEā€ is set as the default secondary key in IMC_ShooterGame by inheriting the Player Mappable Key Settings from IA_Melee:

DumpProfileToLog() shows the following:

Melee
Mapping Name: ā€˜Melee’ Slot: ā€˜First’ Default Key: ā€˜C’ Player Mapped Key: ā€˜C’ HardwareDevice: ā€˜None::None’ AssociatedInputAction: ā€˜IA_Melee’
Mapping Name: ā€˜Melee’ Slot: 'Second’ Default Key: ā€˜E’ Player Mapped Key: ā€˜E’ HardwareDevice: ā€˜None::None’ AssociatedInputAction: ā€˜IA_Melee’

Under the hood of IMC_ShooterGame, there are two instances of Player Mappable Key Settings with the same Name property, in this case it’s ā€œMeleeā€.

At some point the first one gets set to Slot ā€œFirstā€ and the second to Slot ā€œSecondā€. Unfortunately, there is a validator which prevents the same Name property from being given to multiple Player Mappable Key Settings in a Input Mapping Context when overriding what’s in the Input Action:

The validator is FEnhancedInputPlayerMappableNameValidator::IsValid() and it prevents the user from inputting text too long to fit in an FName as well as a Name which is already in use. Should the check for already in use be removed? Instead it could ensure there are enough slots for all instances of the Name. EPlayerMappableKeySlot has a max of seven slots, but there is a code comment in the enum suggesting the max is defined in a project setting.

If I’m on the wrong track, is there another method to define default mappings for additional slots I haven’t figured out yet?

Did you ever figure out of it?

@Patterson @LeoTMad1

Add

[ConsoleVariables]

EnhancedInput.bEnableNameValidation=0

To your DefaultEngine.ini, or just set the var in your console in-editor.

Disclaimer - don’t hold me responsible if anything breaks lol. This allowed me to use the same Name across multiple IMC’s, and in addition, the behavior was as anticipated - changes to ā€œForwardā€ in one IMC propagated to another IMC where ā€œForwardā€ Name was also used

Hello!

I’m trying enable remapping of Chorded Input Triggers.

Examples:

W → Move Forward

W + [Shift] → Sprint

LMB → Melee Attack

LMB + [Shift] → Power Melee Attack

I would like to enable players to remap these input to whatever they want. Alternately, I keep a standard set of modifiers (KB/M: Shift, Alt, Ctrl; GamePad: Triggers, shoulders) and let the player chose which one they want to use.

Unfortunately, I’m stuck.

There are no BP nodes that would allow this. I can extract the trigger as a Chorded Input, but I cannot get it’s input key. Nor can I cast it to an Input or use it to create a new Key for remapping. Or, at least, Idk how.

I tried to create a get/set functions in C++ but those functions are not appearing in my BPs.

This is my code:

/**

UInputTriggerChordAction

Applies a chord action that must be triggering for this trigger’s action to trigger
*/
// UCLASS(NotBlueprintable, meta = (DisplayName = ā€œChorded Actionā€, NotInputConfigurable = ā€œtrueā€))
UCLASS(Blueprintable, meta = (DisplayName = ā€œChorded Actionā€, NotInputConfigurable = ā€œfalseā€))
class ENHANCEDINPUT_API UInputTriggerChordAction : public UInputTrigger
{
GENERATED_BODY()

public:

#if WITH_EDITOR
virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
#endif

protected:
// Implicit, so action cannot fire unless this is firing.
virtual ETriggerType GetTriggerType_Implementation() const override { return ETriggerType::Implicit; }

virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override;
virtual bool IsBlocking(const ETriggerState State) const override { return State != ETriggerState::Triggered; }

public:
// The action that must be triggering for this trigger’s action to trigger
UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = ā€œTrigger Settingsā€, meta = (DisplayThumbnail = ā€œfalseā€))
TObjectPtr ChordAction = nullptr;

public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = ā€œTrigger Settingsā€)
UInputAction* getChordActionValueCppFn() const;
virtual UInputAction* getChordActionValueCppFn_Implementation() const;

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Trigger Settings")
bool setChordActionValueCppFn(UInputAction* ChordActionInput);
virtual bool setChordActionValueCppFn_Implementation(UInputAction* ChordActionInput);

};

Any suggestions here would be greatly appreciated.

It may be that my understanding of chorded actions and modified inputs is wrong. If so, I would appreciate it if someone could explain to me where I’m wrong.

Thank you.