Refactor input image lookup logic into GameSubsystem so it can be reused elsewhere

This commit is contained in:
Steve Streeting 2020-11-20 17:40:55 +00:00
parent 70bce95d3b
commit a2ca3076e9
5 changed files with 173 additions and 119 deletions

View File

@ -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<FInputActionKeyMapping> GS_TempActionMap;
TArray<FInputAxisKeyMapping> 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<UDataTable>& 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<FKeySprite>(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)

View File

@ -15,17 +15,20 @@ TSharedRef<SWidget> 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)
{
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)
auto GS = GetStevesGameSubsystem(GetWorld());
if (GS)
{
case EInputBindingType::Action:
UpdateImageFromAction(ActionOrAxisName);
break;
case EInputBindingType::Axis:
UpdateImageFromAxis(ActionOrAxisName);
break;
case EInputBindingType::Key:
UpdateImageFromKey(Key);
break;
default: ;
}
}
TArray<FInputActionKeyMapping> TempActionMap;
TArray<FInputAxisKeyMapping> 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<UDataTable>& 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<FKeySprite>(InKey.GetFName(), "Find Key Image");
if (SpriteRow)
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(SpriteRow->Sprite, true);
SetBrushFromAtlasInterface(Sprite, true);
}
}
}
UUiTheme* UInputImage::GetTheme()
{
if (IsValid(CustomTheme))
return CustomTheme;
auto GS = GetStevesGameSubsystem(GetWorld());
if (GS)
return GS->GetDefaultUiTheme();
return nullptr;
}

View File

@ -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"
@ -104,6 +105,8 @@ protected:
// Called by detector
void OnInputDetectorModeChanged(int PlayerIndex, EInputMode NewMode);
UPaperSprite* GetImageSpriteFromTable(const FKey& Key, const TSoftObjectPtr<UDataTable>& 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);
};

View File

@ -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
};

View File

@ -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<SWidget> RebuildWidget() override;
void UpdateImageFromAction(const FName& Name);
void UpdateImageFromAxis(const FName& Name);
void UpdateImageFromTable(const FKey& Key, const TSoftObjectPtr<UDataTable>& 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);
};