From 9e4b77d518fa8556fcb6f325da67923cd8364af8 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 7 Mar 2023 15:38:11 +0000 Subject: [PATCH] Initial support for Enhanced Input in InputImage --- .../Private/StevesGameSubsystem.cpp | 38 +++++++++++ .../Private/StevesUI/InputImage.cpp | 22 ++++++- .../Private/StevesUI/StevesUI.cpp | 64 +++++++++++++++++++ .../Private/StevesUI/StevesUI.h | 7 ++ .../Public/StevesGameSubsystem.h | 13 +++- .../Public/StevesHelperCommon.h | 8 ++- .../Public/StevesUI/InputImage.h | 8 +++ .../StevesUEHelpers/StevesUEHelpers.Build.cs | 3 +- 8 files changed, 156 insertions(+), 7 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp b/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp index a36e7bd..1aabaab 100644 --- a/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp +++ b/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp @@ -1,4 +1,6 @@ #include "StevesGameSubsystem.h" + +#include "EnhancedInputSubsystems.h" #include "StevesGameViewportClientBase.h" #include "StevesUEHelpers.h" #include "Engine/AssetManager.h" @@ -224,6 +226,42 @@ UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromAxis(const FName& Nam return nullptr; } +UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromEnhancedInputAction(UInputAction* Action, + EInputImageDevicePreference DevicePreference, + int PlayerIdx, + APlayerController* PC, + UUiTheme* Theme) +{ + + if (const UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) + { + const TArray Keys = Subsystem->QueryKeysMappedToAction(Action); + + // For default, prefer mouse for axes + if (DevicePreference == EInputImageDevicePreference::Auto) + { + if (Action->ValueType == EInputActionValueType::Boolean) + { + DevicePreference = EInputImageDevicePreference::Gamepad_Keyboard_Mouse_Button; + } + else + { + DevicePreference = EInputImageDevicePreference::Gamepad_Mouse_Keyboard; + } + } + const EInputMode LastInput = GetLastInputModeUsed(PlayerIdx); + const EInputMode LastButtonInput = GetLastInputButtonPressed(PlayerIdx); + const EInputMode LastAxisInput = GetLastInputAxisMoved(PlayerIdx); + if (const FKey* PreferredKey = GetPreferedKeyMapping(Keys, DevicePreference, LastInput, LastButtonInput, LastAxisInput)) + { + return GetInputImageSpriteFromKey(*PreferredKey, PlayerIdx, Theme); + } + } + + return nullptr; +} + + TSoftObjectPtr UStevesGameSubsystem::GetGamepadImages(int PlayerIndex, const UUiTheme* Theme) { // TODO: determine type of controller diff --git a/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp b/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp index 5291725..208ce7a 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp @@ -12,11 +12,12 @@ TSharedRef UInputImage::RebuildWidget() auto GS = GetStevesGameSubsystem(GetWorld()); if (GS && !bSubbedToInputEvents) { - bSubbedToInputEvents = true; GS->OnInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); GS->OnButtonInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); GS->OnAxisInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); } + + bSubbedToInputEvents = true; UpdateImage(); return Ret; @@ -94,12 +95,29 @@ void UInputImage::SetFromKey(FKey K) UpdateImage(); } +void UInputImage::SetFromInputAction(UInputAction* Action) +{ + BindingType = EInputBindingType::EnhancedInputAction; + InputAction = Action; + UpdateImage(); +} + void UInputImage::UpdateImage() { auto GS = GetStevesGameSubsystem(GetWorld()); if (GS) { - auto Sprite = GS->GetInputImageSprite(BindingType, ActionOrAxisName, Key, DevicePreference, PlayerIndex, CustomTheme); + UPaperSprite* Sprite = nullptr; + if (BindingType == EInputBindingType::EnhancedInputAction) + { + auto IA = InputAction.LoadSynchronous(); + Sprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, DevicePreference, PlayerIndex, GetOwningPlayer(), CustomTheme); + } + else + { + Sprite = GS->GetInputImageSprite(BindingType, ActionOrAxisName, Key, DevicePreference, PlayerIndex, CustomTheme); + } + if (Sprite) { // Match size is needed incase size has changed diff --git a/Source/StevesUEHelpers/Private/StevesUI/StevesUI.cpp b/Source/StevesUEHelpers/Private/StevesUI/StevesUI.cpp index 28ef663..eef96c7 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/StevesUI.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/StevesUI.cpp @@ -49,3 +49,67 @@ void SetWidgetFocusProperly(UWidget* Widget) Widget->SetFocus(); } + +const FKey* GetPreferedKeyMapping(const TArray& AllKeys, + EInputImageDevicePreference DevicePreference, + EInputMode LastInputDevice, + EInputMode LastButtonInputDevice, + EInputMode LastAxisInputDevice) +{ + // Same as GetPreferedActionOrAxisMapping, just with key directly + const FKey* MouseMapping = nullptr; + const FKey* KeyboardMapping = nullptr; + const FKey* GamepadMapping = nullptr; + for (const FKey& Key : AllKeys) + { + // notice how we take the LAST one in the list as the final version + // this is because UInputSettings::GetActionMappingByName *reverses* the mapping list from Project Settings + if (Key.IsGamepadKey()) + { + GamepadMapping = &Key; + } + else if (Key.IsMouseButton()) // registers true for mouse axes too + { + MouseMapping = &Key; + } + else + { + KeyboardMapping = &Key; + } + } + + const FKey* Preferred = nullptr; + if (GamepadMapping && LastInputDevice == EInputMode::Gamepad) + { + // Always prefer gamepad if used last + Preferred = GamepadMapping; + } + else + { + switch (DevicePreference) + { + // Auto should be pre-converted to another + case EInputImageDevicePreference::Auto: + UE_LOG(LogStevesUI, Error, TEXT("Device Preference should have been converted before this call")) + break; + case EInputImageDevicePreference::Gamepad_Keyboard_Mouse: + Preferred = KeyboardMapping ? KeyboardMapping : MouseMapping; + break; + case EInputImageDevicePreference::Gamepad_Mouse_Keyboard: + Preferred = MouseMapping ? MouseMapping : KeyboardMapping; + break; + + case EInputImageDevicePreference::Gamepad_Keyboard_Mouse_Button: + // Use the latest button press + Preferred = (MouseMapping && (LastButtonInputDevice == EInputMode::Mouse || !KeyboardMapping)) ? MouseMapping : KeyboardMapping; + break; + case EInputImageDevicePreference::Gamepad_Keyboard_Mouse_Axis: + // Use the latest button press + Preferred = (MouseMapping && (LastAxisInputDevice == EInputMode::Mouse || !KeyboardMapping)) ? MouseMapping : KeyboardMapping; + break; + default: + break; + } + } + return Preferred; +} diff --git a/Source/StevesUEHelpers/Private/StevesUI/StevesUI.h b/Source/StevesUEHelpers/Private/StevesUI/StevesUI.h index 4b8aaee..07145a7 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/StevesUI.h +++ b/Source/StevesUEHelpers/Private/StevesUI/StevesUI.h @@ -89,3 +89,10 @@ const T* GetPreferedActionOrAxisMapping(const TArray& AllMappings, const FNam } return Preferred; } + + +const FKey* GetPreferedKeyMapping(const TArray& AllKeys, + EInputImageDevicePreference DevicePreference, + EInputMode LastInputDevice, + EInputMode LastButtonInputDevice, + EInputMode LastAxisInputDevice); diff --git a/Source/StevesUEHelpers/Public/StevesGameSubsystem.h b/Source/StevesUEHelpers/Public/StevesGameSubsystem.h index 986116f..2536377 100644 --- a/Source/StevesUEHelpers/Public/StevesGameSubsystem.h +++ b/Source/StevesUEHelpers/Public/StevesGameSubsystem.h @@ -8,6 +8,7 @@ #include "StevesHelperCommon.h" #include "StevesTextureRenderTargetPool.h" #include "StevesUI/FocusSystem.h" +#include "StevesUI/InputImage.h" #include "StevesUI/UiTheme.h" #include "StevesGameSubsystem.generated.h" @@ -37,7 +38,6 @@ public: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; - protected: DECLARE_DELEGATE_TwoParams(FInternalInputModeChanged, int /* PlayerIndex */, EInputMode) /** @@ -209,6 +209,17 @@ public: int PlayerIndex = 0, const UUiTheme* Theme = nullptr); + /** + * @brief Get an input button / key / axis image as a sprite based on an enhanced input action + * @param Action The input action + * @param DevicePreference The order of preference for images where multiple devices have mappings. In the case of multiple mappings for the same device, the first one will be used. + * @param PlayerIdx The player index to look up the binding for + * @param PC The player controller to look up the binding for + * @param Theme Optional explicit theme, if blank use the default theme + * @return + */ + UPaperSprite* GetInputImageSpriteFromEnhancedInputAction(UInputAction* Action, EInputImageDevicePreference DevicePreference, int PlayerIdx, APlayerController* PC, UUiTheme* Theme); + /** * @brief Get an input button / key image from an action * @param Name The name of the action diff --git a/Source/StevesUEHelpers/Public/StevesHelperCommon.h b/Source/StevesUEHelpers/Public/StevesHelperCommon.h index cdcf30e..746905d 100644 --- a/Source/StevesUEHelpers/Public/StevesHelperCommon.h +++ b/Source/StevesUEHelpers/Public/StevesHelperCommon.h @@ -41,12 +41,14 @@ enum class EGamePauseChange : uint8 UENUM(BlueprintType) enum class EInputBindingType : uint8 { - /// A button action, will be looked up based on input mappings + /// A legacy button action, will be looked up based on input mappings Action = 0, - /// An axis action, will be looked up based on input mappings + /// An legacy axis action, will be looked up based on input mappings Axis = 1, /// A manually specified FKey (which can be key, button, axis) - Key = 2 + Key = 2, + /// An EnhancedInput action + EnhancedInputAction = 3 }; /// What order of preference should we return input images where an action/axis has multiple mappings diff --git a/Source/StevesUEHelpers/Public/StevesUI/InputImage.h b/Source/StevesUEHelpers/Public/StevesUI/InputImage.h index 5cfcff2..c460ded 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/InputImage.h +++ b/Source/StevesUEHelpers/Public/StevesUI/InputImage.h @@ -22,6 +22,10 @@ protected: /// If BindingType is Action/Axis, the name of it UPROPERTY(EditAnywhere) FName ActionOrAxisName; + + /// If binding type is EnhancedInputAction, a reference to an enhanced input action + UPROPERTY(EditAnywhere) // can't be inside #if + TSoftObjectPtr InputAction; /// Where there are multiple mappings, which to prefer UPROPERTY(EditAnywhere) @@ -57,6 +61,10 @@ public: UFUNCTION(BlueprintCallable) virtual void SetFromKey(FKey K); + /// Tell this image to display Enhanced InputAction + UFUNCTION(BlueprintCallable) + virtual void SetFromInputAction(UInputAction* Action); + /// Get the binding type that we'll use to populate the image UFUNCTION(BlueprintCallable) virtual EInputBindingType GetBindingType() const { return BindingType; } diff --git a/Source/StevesUEHelpers/StevesUEHelpers.Build.cs b/Source/StevesUEHelpers/StevesUEHelpers.Build.cs index 2ad9cef..f70f5b2 100644 --- a/Source/StevesUEHelpers/StevesUEHelpers.Build.cs +++ b/Source/StevesUEHelpers/StevesUEHelpers.Build.cs @@ -25,10 +25,11 @@ public class StevesUEHelpers : ModuleRules "CoreUObject", "Engine", "InputCore", + "EnhancedInput", "Slate", "SlateCore", "UMG", - "Paper2D" + "Paper2D", } );