Files
Faye/Plugins/StevesUEHelpers/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp
2026-07-04 21:15:59 +02:00

716 lines
24 KiB
C++

// Copyright Steve Streeting 2020 onwards
// Released under the MIT license
#include "StevesGameSubsystem.h"
#include "EngineUtils.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "StevesGameViewportClientBase.h"
#include "StevesPluginSettings.h"
#include "StevesUEHelpers.h"
#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"
#include "StevesUI/StevesUI.h"
#include "TimerManager.h"
#include "AssetRegistry/AssetData.h"
#include "Engine/Engine.h"
#include "Engine/LocalPlayer.h"
#include "UnrealClient.h"
#include "Slate/SceneViewport.h"
//PRAGMA_DISABLE_OPTIMIZATION
void UStevesGameSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
#if !UE_SERVER
CreateInputDetector();
InitTheme();
InitForegroundCheck();
NotifyEnhancedInputMappingsChanged();
InitViewport();
#endif
}
void UStevesGameSubsystem::Deinitialize()
{
Super::Deinitialize();
#if !UE_SERVER
DestroyInputDetector();
#endif
}
void UStevesGameSubsystem::InitViewport()
{
FViewport::ViewportResizedEvent.AddUObject(this, &UStevesGameSubsystem::ViewportResized);
if (auto GI = GetGameInstance())
{
if (auto VC = GI->GetGameViewportClient())
{
VC->OnToggleFullscreen().AddUObject(this, &UStevesGameSubsystem::FullscreenToggled);
}
}
}
void UStevesGameSubsystem::ViewportResized(FViewport* Viewport, unsigned Unused)
{
FIntPoint Sz = Viewport->GetSizeXY();
OnViewportResized.Broadcast(Sz.X, Sz.Y);
}
void UStevesGameSubsystem::FullscreenToggled(bool bFullscreen)
{
if (auto GI = GetGameInstance())
{
if (auto VC = GI->GetGameViewportClient())
{
FIntPoint Sz = VC->GetGameViewport()->GetSizeXY();
OnViewportResized.Broadcast(Sz.X, Sz.Y);
}
}
}
void UStevesGameSubsystem::CreateInputDetector()
{
#if !UE_SERVER
if (!InputDetector.IsValid())
{
InputDetector = MakeShareable(new FInputModeDetector());
FSlateApplication::Get().RegisterInputPreProcessor(InputDetector);
InputDetector->OnInputModeChanged.BindUObject(this, &UStevesGameSubsystem::OnInputDetectorModeChanged);
InputDetector->OnButtonInputModeChanged.BindUObject(this, &UStevesGameSubsystem::OnButtonInputDetectorModeChanged);
InputDetector->OnAxisInputModeChanged.BindUObject(this, &UStevesGameSubsystem::OnAxisInputDetectorModeChanged);
}
#endif
}
void UStevesGameSubsystem::DestroyInputDetector()
{
#if !UE_SERVER
if (InputDetector.IsValid())
{
FSlateApplication::Get().UnregisterInputPreProcessor(InputDetector);
InputDetector.Reset();
}
#endif
}
void UStevesGameSubsystem::NotifyEnhancedInputMappingsChanged()
{
// delay to ensure there's a tick in between which updates the mappings, it's not synchronous
auto DelayedFunc = [this]()
{
OnEnhancedInputMappingsChanged.Broadcast();
};
FTimerHandle TempHandle;
GetWorld()->GetTimerManager().SetTimer(TempHandle, FTimerDelegate::CreateLambda(DelayedFunc), 0.05f, false);
}
TSoftObjectPtr<UInputAction> UStevesGameSubsystem::FindEnhancedInputAction(const FString& Name)
{
if (FAssetRegistryModule* AssetRegistryModule = FModuleManager::LoadModulePtr<FAssetRegistryModule>(TEXT("AssetRegistry")))
{
IAssetRegistry& AssetRegistry = AssetRegistryModule->Get();
if (auto Settings = GetDefault<UStevesPluginSettings>())
{
for (const auto& Dir : Settings->EnhancedInputActionSearchDirectories)
{
if (!FPackageName::IsValidPath(Dir.Path))
{
continue;
}
TArray<FAssetData> Assets;
FString Package = FPaths::Combine(Dir.Path, Name);
if (AssetRegistry.GetAssetsByPackageName(FName(*Package), Assets, true))
{
for (const FAssetData& Asset : Assets)
{
if (Asset.GetClass() == UInputAction::StaticClass())
{
return TSoftObjectPtr<UInputAction>(Asset.GetSoftObjectPath());
}
}
}
}
}
}
return nullptr;
}
void UStevesGameSubsystem::RegisterInterestInEnhancedInputAction(const UInputAction* Action, ETriggerEvent TriggerEvent)
{
// Avoid registering duplicate interest
FEnhancedInputInterest Interest(Action, TriggerEvent);
if (!RegisteredEnhancedInputActionInterests.Contains(Interest))
{
if (auto GI = GetGameInstance())
{
if (auto PC = GI->GetFirstLocalPlayerController())
{
if (auto EIC = Cast<UEnhancedInputComponent>(PC->InputComponent))
{
EIC->BindAction(Action, TriggerEvent, this, &ThisClass::EnhancedInputActionTriggered);
RegisteredEnhancedInputActionInterests.Add(Interest);
}
}
}
}
}
void UStevesGameSubsystem::UnregisterAllInterestInEnhancedInputActions()
{
if (auto GI = GetGameInstance())
{
if (auto PC = GI->GetFirstLocalPlayerController())
{
if (auto EIC = Cast<UEnhancedInputComponent>(PC->InputComponent))
{
TArray<uint32> HandlesToRemove;
for (auto& Binding : EIC->GetActionEventBindings())
{
if (Binding->IsBoundToObject(this))
{
HandlesToRemove.Add(Binding->GetHandle());
}
}
for (const uint32 Handle : HandlesToRemove)
{
EIC->RemoveBindingByHandle(Handle);
}
}
}
}
RegisteredEnhancedInputActionInterests.Empty();
}
void UStevesGameSubsystem::EnhancedInputActionTriggered(const FInputActionInstance& InputActionInstance)
{
OnEnhancedInputActionTriggered.Broadcast(InputActionInstance.GetSourceAction(), InputActionInstance.GetTriggerEvent());
}
void UStevesGameSubsystem::InitTheme()
{
DefaultUiTheme = LoadObject<UUiTheme>(nullptr, *DefaultUiThemePath, nullptr);
}
void UStevesGameSubsystem::InitForegroundCheck()
{
// Check foreground status every 0.5 seconds
GetWorld()->GetTimerManager().SetTimer(ForegroundCheckHandle, this, &UStevesGameSubsystem::CheckForeground, 0.5, true);
}
void UStevesGameSubsystem::CheckForeground()
{
bool bNewForeground = bIsForeground;
if (IsValid(GEngine) && IsValid(GEngine->GameViewport) && GEngine->GameViewport->Viewport)
bNewForeground = GEngine->GameViewport->Viewport->IsForegroundWindow();
if (bNewForeground != bIsForeground)
{
bIsForeground = bNewForeground;
InputDetector->bIgnoreEvents = !bIsForeground;
OnWindowForegroundChanged.Broadcast(bIsForeground);
}
}
void UStevesGameSubsystem::OnInputDetectorModeChanged(int PlayerIndex, EInputMode NewMode)
{
// We can't check this during Initialize because it's too early
if (!bCheckedViewportClient)
{
auto GI = GetGameInstance();
auto VC = Cast<UStevesGameViewportClientBase>(GI->GetGameViewportClient());
if (!VC)
UE_LOG(LogStevesUEHelpers, Log, TEXT("Consider using UStevesGameViewportClientBase for your GameViewportClient"))
bCheckedViewportClient = true;
}
auto Settings = GetDefault<UStevesPluginSettings>();
if (Settings->bHideMouseWhenGamepadUsed)
{
auto GI = GetGameInstance();
auto VC = GI->GetGameViewportClient();
auto SVC = Cast<UStevesGameViewportClientBase>(VC);
if (VC)
{
if (NewMode == EInputMode::Gamepad)
{
// First move mouse pointer out of the way because it still generates mouse hits (unless we make source changes to Slate, ugh)
MoveMouseOffScreen(true);
}
else if (NewMode == EInputMode::Mouse)
{
if (SVC)
SVC->SetSuppressMouseCursor(false);
}
}
}
OnInputModeChanged.Broadcast(PlayerIndex, NewMode);
}
void UStevesGameSubsystem::MoveMouseOffScreen(bool bAlsoHide) const
{
if (auto GI = GetGameInstance())
{
auto VC = GI->GetGameViewportClient();
auto SVC = Cast<UStevesGameViewportClientBase>(VC);
auto PC = GI->GetFirstLocalPlayerController();
FVector2D Sz;
VC->GetViewportSize(Sz);
// -1 because if you move cursor outside window when captured, Slate blows up when you press Return, ughghh
// To make sure we don't generate a mouse move by doing this
// Bizarrely the ViewportSize is correct for moving the mouse pointer but the mouse events have different geometry
InputDetector->IgnoreNextMouseMove();
PC->SetMouseLocation(Sz.X-1,Sz.Y-1);
// Now hide it
// I've seen people use PC->bShowMouseCursor but this messes with capturing when you switch back & forth
// especially when pausing in the editor
// instead, I'm using a separate flag to suppress it, see UiFixGameViewportClient for usage
if (SVC && bAlsoHide)
SVC->SetSuppressMouseCursor(true);
}
}
void UStevesGameSubsystem::OnButtonInputDetectorModeChanged(int PlayerIndex, EInputMode NewMode)
{
// This is specifically for button changes; if this is a different main input mode it will also be registered in OnInputDetectorModeChanged
// Just relay this one
OnButtonInputModeChanged.Broadcast(PlayerIndex, NewMode);
}
void UStevesGameSubsystem::OnAxisInputDetectorModeChanged(int PlayerIndex, EInputMode NewMode)
{
// This is specifically for button changes; if this is a different main input mode it will also be registered in OnInputDetectorModeChanged
// Just relay this one
OnAxisInputModeChanged.Broadcast(PlayerIndex, NewMode);
}
FFocusSystem* UStevesGameSubsystem::GetFocusSystem()
{
return &FocusSystem;
}
UPaperSprite* UStevesGameSubsystem::GetInputImageSprite(EInputBindingType BindingType,
FName ActionOrAxis,
FKey Key,
EInputImageDevicePreference DevicePreference,
int PlayerIdx,
const UUiTheme* Theme)
{
switch(BindingType)
{
case EInputBindingType::Action:
return GetInputImageSpriteFromAction(ActionOrAxis, DevicePreference, PlayerIdx, Theme);
case EInputBindingType::Axis:
return GetInputImageSpriteFromAxis(ActionOrAxis, DevicePreference, PlayerIdx, Theme);
case EInputBindingType::Key:
return GetInputImageSpriteFromKey(Key, PlayerIdx, 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,
EInputImageDevicePreference DevicePreference,
int PlayerIdx,
const UUiTheme* Theme)
{
UInputSettings* Settings = UInputSettings::GetInputSettings();
GS_TempActionMap.Empty();
Settings->GetActionMappingByName(Name, GS_TempActionMap);
// For default, prefer latest press keyboard/mouse for buttons
if (DevicePreference == EInputImageDevicePreference::Auto)
DevicePreference = EInputImageDevicePreference::Gamepad_Keyboard_Mouse_Button;
const EInputMode LastInput = GetLastInputModeUsed(PlayerIdx);
const EInputMode LastButtonInput = GetLastInputButtonPressed(PlayerIdx);
const EInputMode LastAxisInput = GetLastInputAxisMoved(PlayerIdx);
const auto Preferred = GetPreferedActionOrAxisMapping<FInputActionKeyMapping>(GS_TempActionMap, Name, DevicePreference, LastInput, LastButtonInput, LastAxisInput);
if (Preferred)
{
return GetInputImageSpriteFromKey(Preferred->Key, PlayerIdx, Theme);
}
return nullptr;
}
UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromAxis(const FName& Name,
EInputImageDevicePreference DevicePreference,
int PlayerIdx,
const UUiTheme* Theme)
{
// Look up the key for this axis
UInputSettings* Settings = UInputSettings::GetInputSettings();
GS_TempAxisMap.Empty();
Settings->GetAxisMappingByName(Name, GS_TempAxisMap);
// For default, prefer mouse for axes
if (DevicePreference == EInputImageDevicePreference::Auto)
DevicePreference = EInputImageDevicePreference::Gamepad_Mouse_Keyboard;
const EInputMode LastInput = GetLastInputModeUsed(PlayerIdx);
const EInputMode LastButtonInput = GetLastInputButtonPressed(PlayerIdx);
const EInputMode LastAxisInput = GetLastInputAxisMoved(PlayerIdx);
const auto Preferred = GetPreferedActionOrAxisMapping<FInputAxisKeyMapping>(GS_TempAxisMap, Name, DevicePreference, LastInput, LastButtonInput, LastAxisInput);
if (Preferred)
{
return GetInputImageSpriteFromKey(Preferred->Key, PlayerIdx, Theme);
}
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)
{
// TODO: determine type of controller
return Theme->XboxControllerImages;
}
UPaperSprite* UStevesGameSubsystem::GetInputImageSpriteFromKey(const FKey& InKey, int PlayerIndex, const UUiTheme* Theme)
{
if (!IsValid(Theme))
Theme = GetDefaultUiTheme();
if (Theme)
{
if (InKey.IsGamepadKey())
return GetImageSpriteFromTable(InKey, GetGamepadImages(PlayerIndex, Theme));
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;
}
void UStevesGameSubsystem::SetBrushFromAtlas(FSlateBrush* Brush, TScriptInterface<ISlateTextureAtlasInterface> AtlasRegion, bool bMatchSize)
{
if(Brush->GetResourceObject() != AtlasRegion.GetObject())
{
Brush->SetResourceObject(AtlasRegion.GetObject());
if (bMatchSize)
{
if (AtlasRegion)
{
const FSlateAtlasData AtlasData = AtlasRegion->GetSlateAtlasData();
Brush->ImageSize = AtlasData.GetSourceDimensions();
}
else
{
Brush->ImageSize = FVector2D(0, 0);
}
}
}
}
FStevesTextureRenderTargetPoolPtr UStevesGameSubsystem::GetTextureRenderTargetPool(FName Name, bool bAutoCreate)
{
// On the assumption there won't be *loads* of pools, not worth a map, just iterate
for (auto Tex : TextureRenderTargetPools)
{
if (Tex->GetName() == Name)
return Tex;
}
if (bAutoCreate)
{
FStevesTextureRenderTargetPoolPtr Pool = MakeShared<FStevesTextureRenderTargetPool>(Name, this);
TextureRenderTargetPools.Add(Pool);
return Pool;
}
return nullptr;
}
bool UStevesGameSubsystem::FInputModeDetector::ShouldProcessInputEvents() const
{
return !bIgnoreEvents;
}
UStevesGameSubsystem::FInputModeDetector::FInputModeDetector()
{
// 4 local players should be plenty usually (will expand if necessary)
LastInputModeByPlayer.Init(DefaultInputMode, 4);
LastButtonPressByPlayer.Init(DefaultButtonInputMode, 4);
LastAxisMoveByPlayer.Init(DefaultAxisInputMode, 4);
}
bool UStevesGameSubsystem::FInputModeDetector::HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent)
{
if (ShouldProcessInputEvents())
{
// Key down also registers for gamepad buttons
ProcessKeyOrButton(InKeyEvent.GetUserIndex(), InKeyEvent.GetKey());
}
// Don't consume
return false;
}
bool UStevesGameSubsystem::FInputModeDetector::HandleAnalogInputEvent(FSlateApplication& SlateApp,
const FAnalogInputEvent& InAnalogInputEvent)
{
if (ShouldProcessInputEvents())
{
if (InAnalogInputEvent.GetAnalogValue() > GamepadAxisThreshold)
SetMode(InAnalogInputEvent.GetUserIndex(), EInputMode::Gamepad, false);
}
// Don't consume
return false;
}
bool UStevesGameSubsystem::FInputModeDetector::HandleMouseMoveEvent(FSlateApplication& SlateApp, const FPointerEvent& MouseEvent)
{
if (ShouldProcessInputEvents())
{
if (bIgnoreNextMouseMove)
{
bIgnoreNextMouseMove = false;
}
else
{
FVector2D Dist = MouseEvent.GetScreenSpacePosition() - MouseEvent.GetLastScreenSpacePosition();
if (FMath::Abs(Dist.X) > MouseMoveThreshold || FMath::Abs(Dist.Y) > MouseMoveThreshold)
{
SetMode(MouseEvent.GetUserIndex(), EInputMode::Mouse, false);
}
}
}
// Don't consume
return false;
}
bool UStevesGameSubsystem::FInputModeDetector::HandleMouseButtonDownEvent(FSlateApplication& SlateApp, const FPointerEvent& MouseEvent)
{
if (ShouldProcessInputEvents())
{
// We don't care which button
SetMode(MouseEvent.GetUserIndex(), EInputMode::Mouse, true);
}
// Don't consume
return false;
}
bool UStevesGameSubsystem::FInputModeDetector::HandleMouseWheelOrGestureEvent(FSlateApplication& SlateApp, const FPointerEvent& InWheelEvent,
const FPointerEvent* InGestureEvent)
{
if (ShouldProcessInputEvents())
{
SetMode(InWheelEvent.GetUserIndex(), EInputMode::Mouse, false);
}
// Don't consume
return false;
}
EInputMode UStevesGameSubsystem::FInputModeDetector::GetLastInputMode(int PlayerIndex)
{
if (PlayerIndex >= 0 && PlayerIndex < LastInputModeByPlayer.Num())
return LastInputModeByPlayer[PlayerIndex];
// Assume default if never told
return DefaultInputMode;
}
EInputMode UStevesGameSubsystem::FInputModeDetector::GetLastButtonInputMode(int PlayerIndex)
{
if (PlayerIndex >= 0 && PlayerIndex < LastButtonPressByPlayer.Num())
return LastButtonPressByPlayer[PlayerIndex];
// Assume default if never told
return DefaultButtonInputMode;
}
EInputMode UStevesGameSubsystem::FInputModeDetector::GetLastAxisInputMode(int PlayerIndex)
{
if (PlayerIndex >= 0 && PlayerIndex < LastAxisMoveByPlayer.Num())
return LastAxisMoveByPlayer[PlayerIndex];
// Assume default if never told
return DefaultAxisInputMode;
}
void UStevesGameSubsystem::FInputModeDetector::ProcessKeyOrButton(int PlayerIndex, FKey Key)
{
if (Key.IsGamepadKey())
{
SetMode(PlayerIndex, EInputMode::Gamepad, IsAGamepadButton(Key));
}
else if (Key.IsMouseButton())
{
// Assuming mice don't have analog buttons!
SetMode(PlayerIndex, EInputMode::Mouse, true);
}
else
{
// We assume anything that's not mouse and not gamepad is a keyboard
// Assuming keyboards don't have analog buttons!
SetMode(PlayerIndex, EInputMode::Keyboard, true);
}
}
bool UStevesGameSubsystem::FInputModeDetector::IsAGamepadButton(const FKey& Key)
{
// Key.IsButtonAxis() returns true for some thumbstick movement events, because the axis type is EInputAxisType::Button for
// some reason. That means you get button events for thumbstick movements, which is super dumb.
// See core engine InputCoreTypes.cpp for the stick axes which are defined FKeyDetails::GamepadKey | FKeyDetails::ButtonAxis
// This is for some kind of virtual input but it's a nasty hack, omit them
return Key.IsGamepadKey() &&
Key != EKeys::Gamepad_LeftStick_Up &&
Key != EKeys::Gamepad_LeftStick_Down &&
Key != EKeys::Gamepad_LeftStick_Left &&
Key != EKeys::Gamepad_LeftStick_Right &&
Key != EKeys::Gamepad_RightStick_Up &&
Key != EKeys::Gamepad_RightStick_Down &&
Key != EKeys::Gamepad_RightStick_Left &&
Key != EKeys::Gamepad_RightStick_Right;
}
void UStevesGameSubsystem::FInputModeDetector::SetMode(int PlayerIndex, EInputMode NewMode, bool bIsButton)
{
bool bButtonChanged = false;
bool bAxisChanged = false;
bool bMainChanged = false;
if (bIsButton)
{
if (NewMode != EInputMode::Unknown && NewMode != GetLastButtonInputMode(PlayerIndex))
{
if (PlayerIndex >= LastButtonPressByPlayer.Num())
LastButtonPressByPlayer.SetNum(PlayerIndex + 1);
LastButtonPressByPlayer[PlayerIndex] = NewMode;
bButtonChanged = true;
}
}
else
{
if (NewMode != EInputMode::Unknown && NewMode != GetLastAxisInputMode(PlayerIndex))
{
if (PlayerIndex >= LastAxisMoveByPlayer.Num())
LastAxisMoveByPlayer.SetNum(PlayerIndex + 1);
LastAxisMoveByPlayer[PlayerIndex] = NewMode;
bAxisChanged = true;
}
}
// Whether it's a button or not it can affect the main input mode
if (NewMode != EInputMode::Unknown && NewMode != GetLastInputMode(PlayerIndex))
{
if (PlayerIndex >= LastInputModeByPlayer.Num())
LastInputModeByPlayer.SetNum(PlayerIndex + 1);
LastInputModeByPlayer[PlayerIndex] = NewMode;
bMainChanged = true;
}
// Raise events at the end once all state has changed
if (bButtonChanged)
{
// ReSharper disable once CppExpressionWithoutSideEffects
OnButtonInputModeChanged.ExecuteIfBound(PlayerIndex, NewMode);
//UE_LOG(LogStevesUEHelpers, Display, TEXT("Button mode for player %d changed: %s"), PlayerIndex, *UEnum::GetValueAsString(NewMode));
}
if (bAxisChanged)
{
// ReSharper disable once CppExpressionWithoutSideEffects
OnAxisInputModeChanged.ExecuteIfBound(PlayerIndex, NewMode);
//UE_LOG(LogStevesUEHelpers, Display, TEXT("Axis mode for player %d changed: %s"), PlayerIndex, *UEnum::GetValueAsString(NewMode));
}
if (bMainChanged)
{
// ReSharper disable once CppExpressionWithoutSideEffects
OnInputModeChanged.ExecuteIfBound(PlayerIndex, NewMode);
//UE_LOG(LogStevesUEHelpers, Display, TEXT("Input mode for player %d changed: %s"), PlayerIndex, *UEnum::GetValueAsString(NewMode));
}
}
//PRAGMA_ENABLE_OPTIMIZATION