I’m posting this so folks who are trying to figure out how to do this on their own, without having to modify the engine itself, can try it. I really wanted to be able to support modifiers with both axis and action mappings using a single solution, so this is what I came up with. It’s working well for me, but may require customization. Sorry, I do not have a Blueprints implementation available.
Note: the code is not totally finished, I was still in the middle of tweaking it while developing it. It has major room for improvement, etc. The point is to share the general concept.
For performance reasons: whenever the game loads, and/or the user updates their bindings, I keep a TMap updated with key and modifier information.
The code is very specific to how I set things up, but it gives you an idea:
void USomeSettingsClass::RefreshKeyboardCommandKeyCache()
{
if (!GameEngineInputSettings)
return;
const auto SelectedProfile = GetSelectedProfile();
auto KeyboardDevice = SelectedProfile.Controllers.Keyboard;
KeyboardCommandKeyCache.Empty();
for (const auto Binding : KeyboardDevice.Bindings)
{
if (Binding.FKeyValue.Len() <= 0)
continue;
if (Binding.WithShift || Binding.WithCtrl || Binding.WithAlt || Binding.WithCmd)
{
auto CacheStruct = FAscSettingsKeyboardCommandKeyCacheStruct();
CacheStruct.BindingMappingType = Binding.BindingMappingType;
CacheStruct.BindingType = Binding.BindingType;
CacheStruct.WithShift = Binding.WithShift;
CacheStruct.WithCtrl = Binding.WithCtrl;
CacheStruct.WithAlt = Binding.WithAlt;
CacheStruct.WithCmd = Binding.WithCmd;
KeyboardCommandKeyCache.Add(FKey(FName(*Binding.FKeyValue)), CacheStruct);
}
}
}
Then, I have a utility method that checks the for validity of the key combinations. The TMap is essential for performance.
Note: You may want to pass in the axis binding name as a parameter; or however you want to handle it. My implementation is very specific to my game, so removed.
bool UYourClass::IsValidKeyboardCommand(
AAscPlayerController *PlayerController,
float Val,
bool IsInputValid,
bool IsActionBinding,
FKey FKeyValue)
{
if (!IsInputValid)
return false;
if (PlayerController == null || PlayerController->IsPendingKillPending())
return false;
if (PlayerController->PlayerInput == null)
return false;
auto ActualKeyValue = FKeyValue;
if (!IsActionBinding)
{
const auto AxisName = TEXT(""); // Your axis binding name passed here as a parameter? or however you want to handle it
const auto KeysForAxis = PlayerController->PlayerInput->GetKeysForAxis(FName(*AxisName));
for (auto Item : KeysForAxis)
{
if (Item.Scale != Val)
continue;
if (!PlayerController->IsInputKeyDown(Item.Key))
continue;
ActualKeyValue = Item.Key;
break;
}
}
FAscSettingsKeyboardCommandKeyCacheStruct FoundCacheData;
if (KeyboardCommandKeyCache.Contains(ActualKeyValue))
FoundCacheData = KeyboardCommandKeyCache[ActualKeyValue];
else
return true;
const auto IsShiftKeyDown = PlayerController->PlayerInput->IsShiftPressed();
const auto IsCtrlKeyDown = PlayerController->PlayerInput->IsCtrlPressed();
const auto IsAltKeyDown = PlayerController->PlayerInput->IsAltPressed();
const auto IsCmdKeyDown = PlayerController->PlayerInput->IsCmdPressed();
auto Result = true;
if (FoundCacheData.WithShift && !IsShiftKeyDown)
Result = false;
if (FoundCacheData.WithCtrl && !IsCtrlKeyDown)
Result = false;
if (FoundCacheData.WithAlt && !IsAltKeyDown)
Result = false;
if (FoundCacheData.WithCmd && !IsCmdKeyDown)
Result = false;
return Result;
}
Finally, this is how it would look:
void APlayerController::MoveForwardBackward(float Val)
{
const auto IsValidKeyboardCommand = UYourClass->IsValidKeyboardCommand(this, Val, Val != 0.f, false);
if (!IsValidKeyboardCommand)
Val = 0.f; // do whatever
// rest of your code
}
Note: the solution will also work for action bindings.
void APlayerController::MyAction(FKey ActualFKey)
{
const auto IsValidKeyboardCommand = UYourClass->IsValidKeyboardCommand(this, 0, true, true, ActualFKey);
// rest of your code
}