Skip to content

Commit 3f7c30e

Browse files
committed
[Bug][UE5.4] Fixed remap inputs are not saved after restart: started using Enhanced Input User Settings instead of manually saving mappings to the config
1 parent 9f1f523 commit 3f7c30e

File tree

11 files changed

+148
-165
lines changed

11 files changed

+148
-165
lines changed

Diff for: Config/DefaultInput.ini

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
1+
[/Script/EnhancedInput.EnhancedInputDeveloperSettings]
2+
; Enable the Enhanced Input User Settings, which allows to remap input bindings at runtime
3+
bEnableUserSettings=True
24

35
[/Script/Engine.InputSettings]
46
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))

Diff for: Plugins/MyEditorUtils/Source/MyUtils/Private/MyUtilsLibraries/InputUtilsLibrary.cpp

+76-92
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
#include "MyUtilsLibraries/InputUtilsLibrary.h"
44
//---
5-
#include "MyUtilsLibraries/UtilsLibrary.h"
6-
//---
75
#include "EnhancedActionKeyMapping.h"
86
#include "EnhancedInputComponent.h"
97
#include "EnhancedInputSubsystems.h"
@@ -12,7 +10,7 @@
1210
#include "Engine/Engine.h"
1311
#include "Engine/LocalPlayer.h"
1412
#include "GameFramework/PlayerController.h"
15-
#include "MyUtilsLibraries/GameplayUtilsLibrary.h"
13+
#include "UserSettings/EnhancedInputUserSettings.h"
1614
//---
1715
#include UE_INLINE_GENERATED_CPP_BY_NAME(InputUtilsLibrary)
1816

@@ -42,6 +40,13 @@ UEnhancedPlayerInput* UInputUtilsLibrary::GetEnhancedPlayerInput(const UObject*
4240
return PlayerController ? Cast<UEnhancedPlayerInput>(PlayerController->PlayerInput) : nullptr;
4341
}
4442

43+
// Returns the Enhanced Input User Settings for remapping keys
44+
class UEnhancedInputUserSettings* UInputUtilsLibrary::GetEnhancedInputUserSettings(const UObject* WorldContext)
45+
{
46+
const UEnhancedInputLocalPlayerSubsystem* InputSubsystem = GetEnhancedInputSubsystem(WorldContext);
47+
return InputSubsystem ? InputSubsystem->GetUserSettings() : nullptr;
48+
}
49+
4550
/*********************************************************************************************
4651
* Input Contexts
4752
********************************************************************************************* */
@@ -159,44 +164,15 @@ bool UInputUtilsLibrary::IsInputActionBound(const UObject* WorldContext, const U
159164
* Mappings
160165
********************************************************************************************* */
161166

162-
// Returns keys mapped to this action in the active input mapping contexts sorted by its priorities
163-
void UInputUtilsLibrary::GetAllMappingsInAction(const UObject* WorldContext, const UInputAction* InInputAction, TArray<FKey>& OutKeys)
164-
{
165-
if (!ensureMsgf(InInputAction, TEXT("ASSERT: [%i] %s:\n'InInputAction' is not valid!"), __LINE__, *FString(__FUNCTION__)))
166-
{
167-
return;
168-
}
169-
170-
const UEnhancedInputLocalPlayerSubsystem* EnhancedInputSubsystem = GetEnhancedInputSubsystem(WorldContext);
171-
if (!EnhancedInputSubsystem)
172-
{
173-
return;
174-
}
175-
176-
if (!OutKeys.IsEmpty())
177-
{
178-
OutKeys.Empty();
179-
}
180-
181-
OutKeys = EnhancedInputSubsystem->QueryKeysMappedToAction(InInputAction);
182-
}
183-
184-
// Returns the first mapped key to this action in most priority active input context
185-
FKey UInputUtilsLibrary::GetFirstMappingInAction(const UObject* WorldContext, const UInputAction* InInputAction)
186-
{
187-
TArray<FKey> OutKeys;
188-
GetAllMappingsInAction(WorldContext, InInputAction, OutKeys);
189-
190-
constexpr int32 KeyIndex = 0;
191-
static const FKey EmptyKey{};
192-
return OutKeys.IsValidIndex(KeyIndex) ? OutKeys[KeyIndex] : EmptyKey;
193-
}
194-
195167
// Returns all mappings where bIsPlayerMappable is true
196-
void UInputUtilsLibrary::GetAllMappingsInContext(const UInputMappingContext* InInputContext, TArray<FEnhancedActionKeyMapping>& OutMappings)
168+
void UInputUtilsLibrary::GetAllMappingsInContext(const UObject* WorldContext, const UInputMappingContext* InInputContext, TArray<FPlayerKeyMapping>& OutMappings)
197169
{
198-
if (!ensureMsgf(InInputContext, TEXT("ASSERT: [%i] %s:\n'InInputContext' is not valid!"), __LINE__, *FString(__FUNCTION__)))
170+
const UEnhancedInputLocalPlayerSubsystem* InputSubsystem = GetEnhancedInputSubsystem(WorldContext);
171+
const UEnhancedInputUserSettings* EnhancedInputUserSettings = InputSubsystem ? InputSubsystem->GetUserSettings() : nullptr;
172+
if (!ensureMsgf(EnhancedInputUserSettings, TEXT("ASSERT: [%i] %hs:\n'EnhancedInputUserSettings' is null, make sure 'Enable User Settings' is enabled in the Project Settings!"), __LINE__, __FUNCTION__)
173+
|| !ensureMsgf(InInputContext, TEXT("ASSERT: [%i] %hs:\n'InInputContext' is not valid!"), __LINE__, __FUNCTION__))
199174
{
175+
// Can be null on remote clients, do nothing
200176
return;
201177
}
202178

@@ -208,61 +184,56 @@ void UInputUtilsLibrary::GetAllMappingsInContext(const UInputMappingContext* InI
208184
const TArray<FEnhancedActionKeyMapping>& AllMappings = InInputContext->GetMappings();
209185
for (const FEnhancedActionKeyMapping& MappingIt : AllMappings)
210186
{
211-
if (MappingIt.IsPlayerMappable())
187+
if (!MappingIt.IsPlayerMappable())
212188
{
213-
OutMappings.Emplace(MappingIt);
189+
continue;
214190
}
215-
}
216-
}
217-
218-
// Returns mappings by specified input action
219-
void UInputUtilsLibrary::GetMappingsInContextByAction(const UInputMappingContext* InInputContext, const UInputAction* ByInputAction, TArray<FEnhancedActionKeyMapping>& OutMappings)
220-
{
221-
if (!ensureMsgf(InInputContext, TEXT("ASSERT: [%i] %s:\n'InInputContext' is not valid!"), __LINE__, *FString(__FUNCTION__))
222-
|| !ensureMsgf(ByInputAction, TEXT("ASSERT: [%i] %s:\n'ByInputAction' is not valid!"), __LINE__, *FString(__FUNCTION__)))
223-
{
224-
return;
225-
}
226-
227-
if (!OutMappings.IsEmpty())
228-
{
229-
OutMappings.Empty();
230-
}
231191

232-
TArray<FEnhancedActionKeyMapping> AllMappings;
233-
GetAllMappingsInContext(InInputContext, /*out*/AllMappings);
234-
for (const FEnhancedActionKeyMapping& MappingIt : AllMappings)
235-
{
236-
if (MappingIt.Action == ByInputAction)
192+
const FPlayerKeyMapping* FoundMapping = EnhancedInputUserSettings->FindCurrentMappingForSlot(MappingIt.GetMappingName(), EPlayerMappableKeySlot::First);
193+
if (!FoundMapping)
237194
{
238-
OutMappings.Emplace(MappingIt);
195+
// The key is mappable, but it's not even registered in Enhanced Input User Settings (SetAllMappingsRegisteredInContext is not called)
196+
continue;
239197
}
198+
199+
FPlayerKeyMapping RemappedMapping = *FoundMapping;
200+
OutMappings.Emplace(MoveTemp(RemappedMapping));
240201
}
241202
}
242203

243204
// Returns true if specified key is mapped to given input context
244-
bool UInputUtilsLibrary::IsMappedKeyInContext(const FKey& Key, const UInputMappingContext* InInputContext)
205+
bool UInputUtilsLibrary::IsMappedKeyInContext(const UObject* WorldContext, const FKey& Key, const UInputMappingContext* InInputContext)
245206
{
246-
if (!ensureMsgf(InInputContext, TEXT("ASSERT: [%i] %s:\n'InInputContext' is not valid!"), __LINE__, *FString(__FUNCTION__)))
207+
const UEnhancedInputLocalPlayerSubsystem* InputSubsystem = GetEnhancedInputSubsystem(WorldContext);
208+
const UEnhancedInputUserSettings* EnhancedInputUserSettings = InputSubsystem ? InputSubsystem->GetUserSettings() : nullptr;
209+
if (!ensureMsgf(EnhancedInputUserSettings, TEXT("ASSERT: [%i] %hs:\n'EnhancedInputUserSettings' is null, make sure 'Enable User Settings' is enabled in the Project Settings!"), __LINE__, __FUNCTION__)
210+
|| !ensureMsgf(InInputContext, TEXT("ASSERT: [%i] %hs:\n'InInputContext' is not valid!"), __LINE__, __FUNCTION__))
247211
{
248212
return false;
249213
}
250214

251-
TArray<FEnhancedActionKeyMapping> AllMappings;
252-
GetAllMappingsInContext(InInputContext, /*out*/AllMappings);
253-
return AllMappings.ContainsByPredicate([&Key](const FEnhancedActionKeyMapping& MappingIt)
215+
TArray<FPlayerKeyMapping> AllMappings;
216+
GetAllMappingsInContext(WorldContext, InInputContext, /*out*/AllMappings);
217+
return AllMappings.ContainsByPredicate([&Key](const FPlayerKeyMapping& MappingIt)
254218
{
255-
return MappingIt.Key == Key;
219+
return MappingIt.GetCurrentKey() == Key;
256220
});
257221
}
258222

259223
// Unmap previous key and map new one
260-
bool UInputUtilsLibrary::RemapKeyInContext(UInputMappingContext* InInputContext, const UInputAction* ByInputAction, const FKey& PrevKey, const FKey& NewKey)
224+
bool UInputUtilsLibrary::RemapKeyInContext(const UObject* WorldContextObject, const UInputMappingContext* InInputContext, const UInputAction* ByInputAction, const FKey& PrevKey, const FKey& NewKey)
261225
{
262226
if (!ensureMsgf(InInputContext, TEXT("ASSERT: 'InInputContext' is not valid"))
263227
|| !ensureMsgf(ByInputAction, TEXT("ASSERT: [%i] %s:\n'ByInputAction' is not valid!"), __LINE__, *FString(__FUNCTION__))
264228
|| NewKey == PrevKey
265-
|| IsMappedKeyInContext(NewKey, InInputContext))
229+
|| IsMappedKeyInContext(WorldContextObject, NewKey, InInputContext))
230+
{
231+
return false;
232+
}
233+
234+
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = GetEnhancedInputSubsystem(WorldContextObject);
235+
UEnhancedInputUserSettings* EnhancedInputUserSettings = InputSubsystem ? InputSubsystem->GetUserSettings() : nullptr;
236+
if (!ensureMsgf(EnhancedInputUserSettings, TEXT("ASSERT: [%i] %hs:\n'EnhancedInputUserSettings' is null, make sure 'Enable User Settings' is enabled in the Project Settings!"), __LINE__, __FUNCTION__))
266237
{
267238
return false;
268239
}
@@ -271,34 +242,55 @@ bool UInputUtilsLibrary::RemapKeyInContext(UInputMappingContext* InInputContext,
271242
const TArray<FEnhancedActionKeyMapping>& AllMappings = InInputContext->GetMappings();
272243
for (const FEnhancedActionKeyMapping& MappingIt : AllMappings)
273244
{
274-
if (MappingIt.Action == ByInputAction
275-
&& MappingIt.Key == PrevKey)
245+
if (MappingIt.Action != ByInputAction)
276246
{
277-
InInputContext->UnmapKey(ByInputAction, PrevKey);
278-
InInputContext->MapKey(ByInputAction, NewKey);
279-
bRemapped = true;
280-
break;
247+
continue;
281248
}
249+
250+
FMapPlayerKeyArgs Args;
251+
Args.MappingName = MappingIt.GetMappingName();
252+
Args.Slot = EPlayerMappableKeySlot::First;
253+
Args.NewKey = NewKey;
254+
Args.bCreateMatchingSlotIfNeeded = true;
255+
256+
FGameplayTagContainer FailureReason;
257+
EnhancedInputUserSettings->UnMapPlayerKey(Args, FailureReason);
258+
259+
EnhancedInputUserSettings->MapPlayerKey(Args, FailureReason);
260+
bRemapped = FailureReason.IsEmpty();
261+
262+
break;
282263
}
283264

284265
if (!bRemapped)
285266
{
286267
return false;
287268
}
288269

289-
if (CanSaveMappingsInConfig())
290-
{
291-
UGameplayUtilsLibrary::SaveConfig(InInputContext);
292-
}
270+
InputSubsystem->RequestRebuildControlMappings();
271+
EnhancedInputUserSettings->ApplySettings();
272+
EnhancedInputUserSettings->AsyncSaveSettings();
293273

294274
return true;
295275
}
296276

297-
// Unmap previous key and map new one
298-
bool UInputUtilsLibrary::RemapKeyInContext(const UInputMappingContext* InInputContext, const FEnhancedActionKeyMapping& InMapping, const FKey& NewKey)
277+
// Register all mappings in the specified contexts
278+
void UInputUtilsLibrary::SetAllMappingsRegisteredInContext(const UObject* WorldContext, bool bRegister, const UInputMappingContext* InInputContext)
299279
{
300-
UInputMappingContext* ContextToRemap = const_cast<UInputMappingContext*>(InInputContext);
301-
return RemapKeyInContext(ContextToRemap, InMapping.Action, InMapping.Key, NewKey);
280+
UEnhancedInputUserSettings* EnhancedInputUserSettings = GetEnhancedInputUserSettings(WorldContext);
281+
if (!ensureMsgf(EnhancedInputUserSettings, TEXT("ASSERT: [%i] %hs:\n'EnhancedInputUserSettings' is null, make sure 'Enable User Settings' is enabled in the Project Settings!"), __LINE__, __FUNCTION__))
282+
{
283+
return;
284+
}
285+
286+
if (bRegister)
287+
{
288+
EnhancedInputUserSettings->RegisterInputMappingContext(InInputContext);
289+
}
290+
else
291+
{
292+
EnhancedInputUserSettings->UnregisterInputMappingContext(InInputContext);
293+
}
302294
}
303295

304296
/*********************************************************************************************
@@ -322,11 +314,3 @@ APlayerController* UInputUtilsLibrary::GetLocalPlayerController(const UObject* W
322314

323315
return nullptr;
324316
}
325-
326-
// Returns true if remapped key is allowed to be saved in config
327-
bool UInputUtilsLibrary::CanSaveMappingsInConfig()
328-
{
329-
// Always return true in cook since there remaps should be saved into config file and taken there.
330-
// We don't want to save remaps in Editor, it gets serialised right into asset
331-
return !UUtilsLibrary::IsEditor();
332-
}

0 commit comments

Comments
 (0)