第一次提交
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "UI/Mobile/GUIS_JoystickWidget.h"
|
||||
|
||||
#include "CommonHardwareVisibilityBorder.h"
|
||||
#include "Components/Image.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GUIS_JoystickWidget)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "GUIS_Joystick"
|
||||
|
||||
UGUIS_JoystickWidget::UGUIS_JoystickWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetConsumePointerInput(true);
|
||||
}
|
||||
|
||||
FReply UGUIS_JoystickWidget::NativeOnTouchStarted(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
|
||||
{
|
||||
Super::NativeOnTouchStarted(InGeometry, InGestureEvent);
|
||||
|
||||
TouchOrigin = InGestureEvent.GetScreenSpacePosition();
|
||||
|
||||
FReply Reply = FReply::Handled();
|
||||
if (!HasMouseCaptureByUser(InGestureEvent.GetUserIndex(), InGestureEvent.GetPointerIndex()))
|
||||
{
|
||||
Reply.CaptureMouse(GetCachedWidget().ToSharedRef());
|
||||
}
|
||||
return Reply;
|
||||
}
|
||||
|
||||
FReply UGUIS_JoystickWidget::NativeOnTouchMoved(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
|
||||
{
|
||||
Super::NativeOnTouchMoved(InGeometry, InGestureEvent);
|
||||
HandleTouchDelta(InGeometry, InGestureEvent);
|
||||
|
||||
FReply Reply = FReply::Handled();
|
||||
if (!HasMouseCaptureByUser(InGestureEvent.GetUserIndex(), InGestureEvent.GetPointerIndex()))
|
||||
{
|
||||
Reply.CaptureMouse(GetCachedWidget().ToSharedRef());
|
||||
}
|
||||
return Reply;
|
||||
}
|
||||
|
||||
FReply UGUIS_JoystickWidget::NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
|
||||
{
|
||||
StopInputSimulation();
|
||||
return FReply::Handled().ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
void UGUIS_JoystickWidget::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
Super::NativeOnMouseLeave(InMouseEvent);
|
||||
StopInputSimulation();
|
||||
}
|
||||
|
||||
void UGUIS_JoystickWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
|
||||
if (!CommonVisibilityBorder || CommonVisibilityBorder->IsVisible())
|
||||
{
|
||||
// Move the inner stick icon around with the vector
|
||||
if (JoystickForeground && JoystickBackground)
|
||||
{
|
||||
JoystickForeground->SetRenderTranslation(
|
||||
(bNegateYAxis ? FVector2D(1.0f, -1.0f) : FVector2D(1.0f)) *
|
||||
StickVector *
|
||||
(JoystickBackground->GetDesiredSize() * 0.5f)
|
||||
);
|
||||
}
|
||||
InputKeyValue2D(StickVector);
|
||||
}
|
||||
}
|
||||
|
||||
void UGUIS_JoystickWidget::HandleTouchDelta(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
|
||||
{
|
||||
const FVector2D& ScreenSpacePos = InGestureEvent.GetScreenSpacePosition();
|
||||
|
||||
// The center of the geo locally is just its size
|
||||
FVector2D LocalStickCenter = InGeometry.GetAbsoluteSize();
|
||||
|
||||
FVector2D ScreenSpaceStickCenter = InGeometry.LocalToAbsolute(LocalStickCenter);
|
||||
// Get the offset from the origin
|
||||
FVector2D MoveStickOffset = (ScreenSpacePos - ScreenSpaceStickCenter);
|
||||
if (bNegateYAxis)
|
||||
{
|
||||
MoveStickOffset *= FVector2D(1.0f, -1.0f);
|
||||
}
|
||||
|
||||
FVector2D MoveStickDir = FVector2D::ZeroVector;
|
||||
float MoveStickLength = 0.0f;
|
||||
MoveStickOffset.ToDirectionAndLength(MoveStickDir, MoveStickLength);
|
||||
|
||||
MoveStickLength = FMath::Min(MoveStickLength, StickRange);
|
||||
MoveStickOffset = MoveStickDir * MoveStickLength;
|
||||
|
||||
StickVector = MoveStickOffset / StickRange;
|
||||
}
|
||||
|
||||
void UGUIS_JoystickWidget::StopInputSimulation()
|
||||
{
|
||||
TouchOrigin = FVector2D::ZeroVector;
|
||||
StickVector = FVector2D::ZeroVector;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,183 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "UI/Mobile/GUIS_SimulatedInputWidget.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "GUIS_LogChannels.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GUIS_SimulatedInputWidget)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "GUIS_SimulatedInputWidget"
|
||||
|
||||
UGUIS_SimulatedInputWidget::UGUIS_SimulatedInputWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetConsumePointerInput(true);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
const FText UGUIS_SimulatedInputWidget::GetPaletteCategory()
|
||||
{
|
||||
return LOCTEXT("PalleteCategory", "Generic UI");
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
void UGUIS_SimulatedInputWidget::NativeConstruct()
|
||||
{
|
||||
// Find initial key, then listen for any changes to control mappings
|
||||
QueryKeyToSimulate();
|
||||
|
||||
if (UEnhancedInputLocalPlayerSubsystem* System = GetEnhancedInputSubsystem())
|
||||
{
|
||||
System->ControlMappingsRebuiltDelegate.AddUniqueDynamic(this, &UGUIS_SimulatedInputWidget::OnControlMappingsRebuilt);
|
||||
}
|
||||
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
|
||||
void UGUIS_SimulatedInputWidget::NativeDestruct()
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* System = GetEnhancedInputSubsystem())
|
||||
{
|
||||
System->ControlMappingsRebuiltDelegate.RemoveAll(this);
|
||||
}
|
||||
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
|
||||
FReply UGUIS_SimulatedInputWidget::NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
|
||||
{
|
||||
FlushSimulatedInput();
|
||||
|
||||
return Super::NativeOnTouchEnded(InGeometry, InGestureEvent);
|
||||
}
|
||||
|
||||
UEnhancedInputLocalPlayerSubsystem* UGUIS_SimulatedInputWidget::GetEnhancedInputSubsystem() const
|
||||
{
|
||||
if (APlayerController* PC = GetOwningPlayer())
|
||||
{
|
||||
if (ULocalPlayer* LP = GetOwningLocalPlayer())
|
||||
{
|
||||
return LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UEnhancedPlayerInput* UGUIS_SimulatedInputWidget::GetPlayerInput() const
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* System = GetEnhancedInputSubsystem())
|
||||
{
|
||||
return System->GetPlayerInput();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UGUIS_SimulatedInputWidget::InputKeyValue(const FVector& Value)
|
||||
{
|
||||
const APlayerController* PC = GetOwningPlayer();
|
||||
const FPlatformUserId UserId = PC ? PC->GetPlatformUserId() : PLATFORMUSERID_NONE;
|
||||
// If we have an associated input action then we can use it
|
||||
if (AssociatedAction)
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* System = GetEnhancedInputSubsystem())
|
||||
{
|
||||
// We don't want to apply any modifiers or triggers to this action, but they are required for the function signature
|
||||
TArray<UInputModifier*> Modifiers;
|
||||
TArray<UInputTrigger*> Triggers;
|
||||
System->InjectInputVectorForAction(AssociatedAction, Value, Modifiers, Triggers);
|
||||
}
|
||||
}
|
||||
// In case there is no associated input action, we can attempt to simulate input on the fallback key
|
||||
else if (UEnhancedPlayerInput* Input = GetPlayerInput())
|
||||
{
|
||||
#if ENGINE_MINOR_VERSION < 6
|
||||
if (KeyToSimulate.IsValid())
|
||||
{
|
||||
FInputKeyParams Params;
|
||||
Params.Delta = Value;
|
||||
Params.Key = KeyToSimulate;
|
||||
Params.NumSamples = 1;
|
||||
Params.DeltaTime = GetWorld()->GetDeltaSeconds();
|
||||
Params.bIsGamepadOverride = KeyToSimulate.IsGamepadKey();
|
||||
|
||||
Input->InputKey(Params);
|
||||
}
|
||||
#else
|
||||
const FInputDeviceId DeviceToSimulate = IPlatformInputDeviceMapper::Get().GetPrimaryInputDeviceForUser(UserId);
|
||||
if(KeyToSimulate.IsValid())
|
||||
{
|
||||
const float DeltaTime = GetWorld()->GetDeltaSeconds();
|
||||
auto SimulateKeyPress = [Input, DeltaTime, DeviceToSimulate](const FKey& KeyToSim, const float Value, const EInputEvent Event)
|
||||
{
|
||||
FInputKeyEventArgs Args = FInputKeyEventArgs::CreateSimulated(
|
||||
KeyToSim,
|
||||
Event,
|
||||
Value,
|
||||
KeyToSim.IsAnalog() ? 1 : 0,
|
||||
DeviceToSimulate);
|
||||
|
||||
Args.DeltaTime = DeltaTime;
|
||||
|
||||
Input->InputKey(Args);
|
||||
};
|
||||
|
||||
// For keys which are the "root" of the key pair (such as Mouse2D
|
||||
// being made up of the MouseX and MouseY keys) we should call InputKey for each key in the pair,
|
||||
// not the paired key itself. This is so that the events accumulate correctly in
|
||||
// the key state map of UPlayerInput. All input events
|
||||
// from the message handler and viewport client work this way, so when we simulate key inputs, we should
|
||||
// do so as well.
|
||||
if (const EKeys::FPairedKeyDetails* PairDetails = EKeys::GetPairedKeyDetails(KeyToSimulate))
|
||||
{
|
||||
SimulateKeyPress(PairDetails->XKeyDetails->GetKey(), Value.X, IE_Axis);
|
||||
SimulateKeyPress(PairDetails->YKeyDetails->GetKey(), Value.Y, IE_Axis);
|
||||
}
|
||||
else
|
||||
{
|
||||
SimulateKeyPress(KeyToSimulate, Value.X, IE_Pressed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogGUIS, Error, TEXT("'%s' is attempting to simulate input but has no player input!"), *GetNameSafe(this));
|
||||
}
|
||||
}
|
||||
|
||||
void UGUIS_SimulatedInputWidget::InputKeyValue2D(const FVector2D& Value)
|
||||
{
|
||||
InputKeyValue(FVector(Value.X, Value.Y, 0.0));
|
||||
}
|
||||
|
||||
void UGUIS_SimulatedInputWidget::FlushSimulatedInput()
|
||||
{
|
||||
if (UEnhancedPlayerInput* Input = GetPlayerInput())
|
||||
{
|
||||
Input->FlushPressedKeys();
|
||||
}
|
||||
}
|
||||
|
||||
void UGUIS_SimulatedInputWidget::QueryKeyToSimulate()
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* System = GetEnhancedInputSubsystem())
|
||||
{
|
||||
TArray<FKey> Keys = System->QueryKeysMappedToAction(AssociatedAction);
|
||||
if (!Keys.IsEmpty() && Keys[0].IsValid())
|
||||
{
|
||||
KeyToSimulate = Keys[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyToSimulate = FallbackBindingKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGUIS_SimulatedInputWidget::OnControlMappingsRebuilt()
|
||||
{
|
||||
QueryKeyToSimulate();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
Reference in New Issue
Block a user