Initial support for Enhanced Input in InputImage

This commit is contained in:
Steve Streeting 2023-03-07 15:38:11 +00:00
parent a2d3972eb3
commit 9e4b77d518
8 changed files with 156 additions and 7 deletions

View File

@ -1,4 +1,6 @@
#include "StevesGameSubsystem.h" #include "StevesGameSubsystem.h"
#include "EnhancedInputSubsystems.h"
#include "StevesGameViewportClientBase.h" #include "StevesGameViewportClientBase.h"
#include "StevesUEHelpers.h" #include "StevesUEHelpers.h"
#include "Engine/AssetManager.h" #include "Engine/AssetManager.h"
@ -224,6 +226,42 @@ UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromAxis(const FName& Nam
return nullptr; return nullptr;
} }
UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromEnhancedInputAction(UInputAction* Action,
EInputImageDevicePreference DevicePreference,
int PlayerIdx,
APlayerController* PC,
UUiTheme* Theme)
{
if (const UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
{
const TArray<FKey> 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<UDataTable> UStevesGameSubsystem::GetGamepadImages(int PlayerIndex, const UUiTheme* Theme) TSoftObjectPtr<UDataTable> UStevesGameSubsystem::GetGamepadImages(int PlayerIndex, const UUiTheme* Theme)
{ {
// TODO: determine type of controller // TODO: determine type of controller

View File

@ -12,11 +12,12 @@ TSharedRef<SWidget> UInputImage::RebuildWidget()
auto GS = GetStevesGameSubsystem(GetWorld()); auto GS = GetStevesGameSubsystem(GetWorld());
if (GS && !bSubbedToInputEvents) if (GS && !bSubbedToInputEvents)
{ {
bSubbedToInputEvents = true;
GS->OnInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); GS->OnInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged);
GS->OnButtonInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); GS->OnButtonInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged);
GS->OnAxisInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged); GS->OnAxisInputModeChanged.AddUniqueDynamic(this, &UInputImage::OnInputModeChanged);
} }
bSubbedToInputEvents = true;
UpdateImage(); UpdateImage();
return Ret; return Ret;
@ -94,12 +95,29 @@ void UInputImage::SetFromKey(FKey K)
UpdateImage(); UpdateImage();
} }
void UInputImage::SetFromInputAction(UInputAction* Action)
{
BindingType = EInputBindingType::EnhancedInputAction;
InputAction = Action;
UpdateImage();
}
void UInputImage::UpdateImage() void UInputImage::UpdateImage()
{ {
auto GS = GetStevesGameSubsystem(GetWorld()); auto GS = GetStevesGameSubsystem(GetWorld());
if (GS) 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) if (Sprite)
{ {
// Match size is needed incase size has changed // Match size is needed incase size has changed

View File

@ -49,3 +49,67 @@ void SetWidgetFocusProperly(UWidget* Widget)
Widget->SetFocus(); Widget->SetFocus();
} }
const FKey* GetPreferedKeyMapping(const TArray<FKey>& 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;
}

View File

@ -89,3 +89,10 @@ const T* GetPreferedActionOrAxisMapping(const TArray<T>& AllMappings, const FNam
} }
return Preferred; return Preferred;
} }
const FKey* GetPreferedKeyMapping(const TArray<FKey>& AllKeys,
EInputImageDevicePreference DevicePreference,
EInputMode LastInputDevice,
EInputMode LastButtonInputDevice,
EInputMode LastAxisInputDevice);

View File

@ -8,6 +8,7 @@
#include "StevesHelperCommon.h" #include "StevesHelperCommon.h"
#include "StevesTextureRenderTargetPool.h" #include "StevesTextureRenderTargetPool.h"
#include "StevesUI/FocusSystem.h" #include "StevesUI/FocusSystem.h"
#include "StevesUI/InputImage.h"
#include "StevesUI/UiTheme.h" #include "StevesUI/UiTheme.h"
#include "StevesGameSubsystem.generated.h" #include "StevesGameSubsystem.generated.h"
@ -37,7 +38,6 @@ public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override; virtual void Deinitialize() override;
protected: protected:
DECLARE_DELEGATE_TwoParams(FInternalInputModeChanged, int /* PlayerIndex */, EInputMode) DECLARE_DELEGATE_TwoParams(FInternalInputModeChanged, int /* PlayerIndex */, EInputMode)
/** /**
@ -209,6 +209,17 @@ public:
int PlayerIndex = 0, int PlayerIndex = 0,
const UUiTheme* Theme = nullptr); 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 * @brief Get an input button / key image from an action
* @param Name The name of the action * @param Name The name of the action

View File

@ -41,12 +41,14 @@ enum class EGamePauseChange : uint8
UENUM(BlueprintType) UENUM(BlueprintType)
enum class EInputBindingType : uint8 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, 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, Axis = 1,
/// A manually specified FKey (which can be key, button, axis) /// 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 /// What order of preference should we return input images where an action/axis has multiple mappings

View File

@ -22,6 +22,10 @@ protected:
/// If BindingType is Action/Axis, the name of it /// If BindingType is Action/Axis, the name of it
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
FName ActionOrAxisName; FName ActionOrAxisName;
/// If binding type is EnhancedInputAction, a reference to an enhanced input action
UPROPERTY(EditAnywhere) // can't be inside #if
TSoftObjectPtr<UInputAction> InputAction;
/// Where there are multiple mappings, which to prefer /// Where there are multiple mappings, which to prefer
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
@ -57,6 +61,10 @@ public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
virtual void SetFromKey(FKey K); 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 /// Get the binding type that we'll use to populate the image
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
virtual EInputBindingType GetBindingType() const { return BindingType; } virtual EInputBindingType GetBindingType() const { return BindingType; }

View File

@ -25,10 +25,11 @@ public class StevesUEHelpers : ModuleRules
"CoreUObject", "CoreUObject",
"Engine", "Engine",
"InputCore", "InputCore",
"EnhancedInput",
"Slate", "Slate",
"SlateCore", "SlateCore",
"UMG", "UMG",
"Paper2D" "Paper2D",
} }
); );