First we can detect whether the player is using a keyboard with an AZERTY or QWERTZ layout by copying this useful snippet from UE4's InputSettings.cpp
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
bool bIsAzertyKeyboard = false;
bool bIsQwertzKeyboard = false;
#if PLATFORM_WINDOWS
switch (PRIMARYLANGID(LOWORD(GetKeyboardLayout(0))))
{
case LANG_FRENCH:
bIsAzertyKeyboard = true;
break;
case LANG_GERMAN:
bIsQwertzKeyboard = true;
break;
}
#endif
Next I created a two separate .ini files, one for Azerty and one for Qwertz, in which I stored any keybindings that I wanted to override.
For example in defaultInputAzertyOverrides.ini
, I change WASD movement bindings to use ZQSD instead:
[/Script/Engine.InputSettings]
-ActionMappings=(ActionName="PanLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=A)
+ActionMappings=(ActionName="PanLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Q)
-ActionMappings=(ActionName="PanUp",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=W)
+ActionMappings=(ActionName="PanUp",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Z)
I created some helper classes to make it easier to work out where the .ini files are stored.
UCLASS(config=InputAzertyOverrides, defaultconfig)
class UBUIInputSettings_AZERTY : public UInputSettings
{
GENERATED_BODY()
};
UCLASS(config=InputQwertzOverrides, defaultconfig)
class UBUIInputSettings_QWERTZ : public UInputSettings
{
GENERATED_BODY()
};
The trick is to then use FConfigFile::Combine
and load your defaultInput.ini
, then defaultInputAzertyOverrides.ini
. Combine will respect the -
and +
flags and give you a merged FConfigFile
.
FConfigFile InputConfig;
InputConfig.Combine(UInputSettings::GetInputSettings()->GetDefaultConfigFilename());
#if PLATFORM_WINDOWS
switch (PRIMARYLANGID(LOWORD(GetKeyboardLayout(0))))
{
case LANG_FRENCH:
InputConfig.Combine(GetDefault<UBUIInputSettings_AZERTY>()->GetDefaultConfigFilename());
break;
case LANG_GERMAN:
InputConfig.Combine(GetDefault<UBUIInputSettings_QWERTZ>()->GetDefaultConfigFilename());
break;
}
#endif
// Now loop through merged settings and set up bindings
const FConfigSection* Section = InputConfig.Find("/Script/Engine.InputSettings");
if (Section)
{
for (const auto& Pair : *Section)
{
// ...
}
}
As to where to do this snippet, it kind of depends on your setup, but we do it within our subclass of UGameUserSettings
, during our overridden LoadSettings
function.
void UBUIGameUserSettings::LoadSettings(bool bForceReload /*= false */)
{
Super::LoadSettings(bForceReload);
// Do our local override stuff mentioned above
}