From a2ca3076e9e493620116ee2432c700a8b4c080f5 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Fri, 20 Nov 2020 17:40:55 +0000 Subject: [PATCH] Refactor input image lookup logic into GameSubsystem so it can be reused elsewhere --- .../Private/StevesGameSubsystem.cpp | 99 +++++++++++++++ .../Private/StevesUI/InputImage.cpp | 116 +++--------------- .../Public/StevesGameSubsystem.h | 43 ++++++- .../Public/StevesHelperCommon.h | 11 ++ .../Public/StevesUI/InputImage.h | 23 +--- 5 files changed, 173 insertions(+), 119 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp b/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp index ed285e7..0ae5ee8 100644 --- a/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp +++ b/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp @@ -4,7 +4,10 @@ #include "Engine/AssetManager.h" #include "Engine/GameInstance.h" #include "Framework/Application/SlateApplication.h" +#include "GameFramework/InputSettings.h" #include "GameFramework/PlayerController.h" +#include "GameFramework/PlayerInput.h" +#include "StevesUI/KeySprite.h" //PRAGMA_DISABLE_OPTIMIZATION @@ -92,6 +95,102 @@ FFocusSystem* UStevesGameSubsystem::GetFocusSystem() return &FocusSystem; } +UPaperSprite* UStevesGameSubsystem::GetInputImageSprite(EInputBindingType BindingType, FName ActionOrAxis, + FKey Key, int PlayerIdx, const UUiTheme* Theme) +{ + switch(BindingType) + { + case EInputBindingType::Action: + return GetInputImageSpriteFromAction(ActionOrAxis, PlayerIdx, Theme); + case EInputBindingType::Axis: + return GetInputImageSpriteFromAxis(ActionOrAxis, PlayerIdx, Theme); + case EInputBindingType::Key: + return GetInputImageSpriteFromKey(Key, Theme); + default: + return nullptr; + } +} + +// This is not threadsafe! But only used in UI thread in practice +TArray GS_TempActionMap; +TArray GS_TempAxisMap; + +UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromAction(const FName& Name, int PlayerIdx, const UUiTheme* Theme) +{ + + // Look up the key for this action + UInputSettings* Settings = UInputSettings::GetInputSettings(); + GS_TempActionMap.Empty(); + Settings->GetActionMappingByName(Name, GS_TempActionMap); + const bool WantGamepad = LastInputWasGamePad(PlayerIdx); + for (auto && ActionMap : GS_TempActionMap) + { + if (ActionMap.Key.IsGamepadKey() == WantGamepad) + { + return GetInputImageSpriteFromKey(ActionMap.Key, Theme); + } + } + // if we fell through, didn't find a mapping which matched our gamepad preference + if (GS_TempActionMap.Num()) + { + return GetInputImageSpriteFromKey(GS_TempActionMap[0].Key, Theme); + } + return nullptr; +} + +UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromAxis(const FName& Name, int PlayerIdx, const UUiTheme* Theme) +{ + // Look up the key for this axis + UInputSettings* Settings = UInputSettings::GetInputSettings(); + GS_TempAxisMap.Empty(); + Settings->GetAxisMappingByName(Name, GS_TempAxisMap); + const bool WantGamepad = LastInputWasGamePad(PlayerIdx); + for (auto && AxisMap : GS_TempAxisMap) + { + if (AxisMap.Key.IsGamepadKey() == WantGamepad) + { + return GetInputImageSpriteFromKey(AxisMap.Key, Theme); + } + } + // if we fell through, didn't find a mapping which matched our gamepad preference + if (GS_TempAxisMap.Num()) + { + return GetInputImageSpriteFromKey(GS_TempAxisMap[0].Key, Theme); + } + return nullptr; +} + +UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromKey(const FKey& InKey, const UUiTheme* Theme) +{ + if (!IsValid(Theme)) + Theme = GetDefaultUiTheme(); + + if (Theme) + { + if (InKey.IsGamepadKey()) + return GetImageSpriteFromTable(InKey, Theme->XboxControllerImages); + else + return GetImageSpriteFromTable(InKey, Theme->KeyboardMouseImages); + } + + return nullptr; +} + + +UPaperSprite* UStevesGameSubsystem::GetImageSpriteFromTable(const FKey& InKey, + const TSoftObjectPtr& Asset) +{ + // Sync load for simplicity for now + const auto Table = Asset.LoadSynchronous(); + // Rows are named the same as the key name + const auto SpriteRow = Table->FindRow(InKey.GetFName(), "Find Key Image"); + if (SpriteRow) + { + return SpriteRow->Sprite; + } + return nullptr; +} + UStevesGameSubsystem::FInputModeDetector::FInputModeDetector() { // 4 local players should be plenty usually (will expand if necessary) diff --git a/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp b/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp index cd155b7..7f129f0 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp @@ -15,17 +15,20 @@ TSharedRef UInputImage::RebuildWidget() if (GS) { GS->OnInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); - CurrentInputMode = GS->GetLastInputModeUsed(); + CurrentInputMode = GS->GetLastInputModeUsed(PlayerIndex); UpdateImage(); } return Ret; } -void UInputImage::OnInputModeChanged(int PlayerIndex, EInputMode InputMode) +void UInputImage::OnInputModeChanged(int ChangedPlayerIdx, EInputMode InputMode) { - CurrentInputMode = InputMode; - UpdateImage(); + if (ChangedPlayerIdx == PlayerIndex) + { + CurrentInputMode = InputMode; + UpdateImage(); + } } void UInputImage::SetCustomTheme(UUiTheme* Theme) @@ -68,102 +71,15 @@ void UInputImage::SetFromKey(FKey K) void UInputImage::UpdateImage() { - switch(BindingType) - { - case EInputBindingType::Action: - UpdateImageFromAction(ActionOrAxisName); - break; - case EInputBindingType::Axis: - UpdateImageFromAxis(ActionOrAxisName); - break; - case EInputBindingType::Key: - UpdateImageFromKey(Key); - break; - default: ; - } - -} -TArray TempActionMap; -TArray TempAxisMap; - -void UInputImage::UpdateImageFromAction(const FName& Name) -{ - // Look up the key for this action - UInputSettings* Settings = UInputSettings::GetInputSettings(); - TempActionMap.Empty(); - Settings->GetActionMappingByName(Name, TempActionMap); - const bool WantGamepad = CurrentInputMode == EInputMode::Gamepad; - for (auto && ActionMap : TempActionMap) - { - if (ActionMap.Key.IsGamepadKey() == WantGamepad) - { - UpdateImageFromKey(ActionMap.Key); - return; - } - } - // if we fell through, didn't find a mapping which matched our gamepad preference - if (TempActionMap.Num()) - { - UpdateImageFromKey(TempActionMap[0].Key); - } -} - -void UInputImage::UpdateImageFromAxis(const FName& Name) -{ - // Look up the key for this axis - UInputSettings* Settings = UInputSettings::GetInputSettings(); - TempAxisMap.Empty(); - Settings->GetAxisMappingByName(Name, TempAxisMap); - const bool WantGamepad = CurrentInputMode == EInputMode::Gamepad; - for (auto && AxisMap : TempAxisMap) - { - if (AxisMap.Key.IsGamepadKey() == WantGamepad) - { - UpdateImageFromKey(AxisMap.Key); - return; - } - } - // if we fell through, didn't find a mapping which matched our gamepad preference - if (TempAxisMap.Num()) - { - UpdateImageFromKey(TempAxisMap[0].Key); - } -} - -void UInputImage::UpdateImageFromKey(const FKey& InKey) -{ - auto T = GetTheme(); - if (T) - { - if (InKey.IsGamepadKey()) - UpdateImageFromTable(InKey, T->XboxControllerImages); - else - UpdateImageFromTable(InKey, T->KeyboardMouseImages); - } -} - -void UInputImage::UpdateImageFromTable(const FKey& InKey, const TSoftObjectPtr& Asset) -{ - // Sync load for simplicity for now - const auto Table = Asset.LoadSynchronous(); - // Rows are named the same as the key name - const auto SpriteRow = Table->FindRow(InKey.GetFName(), "Find Key Image"); - if (SpriteRow) - { - // Match size is needed incase size has changed - // Need to make it update region in case inside a scale box or something else that needs to adjust - SetBrushFromAtlasInterface(SpriteRow->Sprite, true); - } -} - -UUiTheme* UInputImage::GetTheme() -{ - if (IsValid(CustomTheme)) - return CustomTheme; - auto GS = GetStevesGameSubsystem(GetWorld()); if (GS) - return GS->GetDefaultUiTheme(); - - return nullptr; + { + auto Sprite = GS->GetInputImageSprite(BindingType, ActionOrAxisName, Key, PlayerIndex, CustomTheme); + if (Sprite) + { + // Match size is needed incase size has changed + // Need to make it update region in case inside a scale box or something else that needs to adjust + SetBrushFromAtlasInterface(Sprite, true); + } + } } diff --git a/Source/StevesUEHelpers/Public/StevesGameSubsystem.h b/Source/StevesUEHelpers/Public/StevesGameSubsystem.h index 553bab0..adf44e8 100644 --- a/Source/StevesUEHelpers/Public/StevesGameSubsystem.h +++ b/Source/StevesUEHelpers/Public/StevesGameSubsystem.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "InputCoreTypes.h" +#include "PaperSprite.h" #include "Framework/Application/IInputProcessor.h" #include "StevesHelperCommon.h" #include "StevesUI/FocusSystem.h" @@ -103,7 +104,9 @@ protected: // Called by detector void OnInputDetectorModeChanged(int PlayerIndex, EInputMode NewMode); - + + UPaperSprite* GetImageSpriteFromTable(const FKey& Key, const TSoftObjectPtr& Asset); + public: /// Event raised when input mode changed between gamepad and keyboard / mouse @@ -125,4 +128,42 @@ public: /// Get the global focus system FFocusSystem* GetFocusSystem(); + + + /** + * @brief Get an input button / key / axis image as a sprite based on any combination of action / axis binding or manual key + * @param BindingType The type of input binding to look up + * @param ActionOrAxis The name of the action or axis, if BindingType is looking for that + * @param Key The explicit key you want to display, if the BindingType is set to custom key + * @param PlayerIndex The player index to look up the binding for + * @param Theme Optional explicit theme, if blank use the default theme + * @return + */ + UFUNCTION(BlueprintCallable) + UPaperSprite* GetInputImageSprite(EInputBindingType BindingType, FName ActionOrAxis, FKey Key, int PlayerIndex = 0, const UUiTheme* Theme = nullptr); + + /** + * @brief Get an input button / key image from an action + * @param Name The name of the action + * @param PlayerIndex The player index to look up the binding for + * @param Theme Optional explicit theme, if blank use the default theme + * @return + */ + UPaperSprite* GetInputImageSpriteFromAction(const FName& Name, int PlayerIndex = 0, const UUiTheme* Theme = nullptr); + /** + * @brief Get an input image from an axis + * @param Name The name of the axis + * @param PlayerIndex The player index to look up the binding for + * @param Theme Optional explicit theme, if blank use the default theme + * @return + */ + UPaperSprite* GetInputImageSpriteFromAxis(const FName& Name, int PlayerIndex = 0, const UUiTheme* Theme = nullptr); + /** + * @brief Get an input image for a specific key + * @param Key The key to look up + * @param Theme Optional explicit theme, if blank use the default theme + * @return + */ + UPaperSprite* GetInputImageSpriteFromKey(const FKey& Key, const UUiTheme* Theme = nullptr); + }; diff --git a/Source/StevesUEHelpers/Public/StevesHelperCommon.h b/Source/StevesUEHelpers/Public/StevesHelperCommon.h index bae210e..12154cd 100644 --- a/Source/StevesUEHelpers/Public/StevesHelperCommon.h +++ b/Source/StevesUEHelpers/Public/StevesHelperCommon.h @@ -38,3 +38,14 @@ enum class EGamePauseChange : uint8 Unpaused UMETA(DisplayName="Unpause Game") }; +UENUM(BlueprintType) +enum class EInputBindingType : uint8 +{ + /// A button action, will be looked up based on input mappings + Action = 0, + /// An axis action, will be looked up based on input mappings + Axis = 1, + /// A manually specified FKey (which can be key, button, axis) + Key = 2 +}; + diff --git a/Source/StevesUEHelpers/Public/StevesUI/InputImage.h b/Source/StevesUEHelpers/Public/StevesUI/InputImage.h index 760ef9e..a242880 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/InputImage.h +++ b/Source/StevesUEHelpers/Public/StevesUI/InputImage.h @@ -7,18 +7,6 @@ #include "StevesHelperCommon.h" #include "InputImage.generated.h" -UENUM(BlueprintType) -enum class EInputBindingType : uint8 -{ - /// A button action, will be looked up based on input mappings - Action = 0, - /// An axis action, will be looked up based on input mappings - Axis = 1, - /// A manually specified FKey (which can be key, button, axis) - Key = 2 -}; - - /// A special widget containing an image which populates itself based on an input action / axis and can dynamically /// change based on the active input method. UCLASS() @@ -39,6 +27,10 @@ protected: UPROPERTY(EditAnywhere) FKey Key; + /// The player index for which the input should be looked up + UPROPERTY(EditAnywhere) + int PlayerIndex = 0; + /// Custom theme to use for this input image set; if not supplied will use UStevesGameSubsystem::DefaultUiTheme UPROPERTY(EditAnywhere) UUiTheme* CustomTheme; @@ -80,14 +72,9 @@ public: protected: virtual TSharedRef RebuildWidget() override; - void UpdateImageFromAction(const FName& Name); - void UpdateImageFromAxis(const FName& Name); - void UpdateImageFromTable(const FKey& Key, const TSoftObjectPtr& Asset); - void UpdateImageFromKey(const FKey& Key); virtual void UpdateImage(); - virtual UUiTheme* GetTheme(); UFUNCTION() - void OnInputModeChanged(int PlayerIndex, EInputMode InputMode); + void OnInputModeChanged(int ChangedPlayerIdx, EInputMode InputMode); };