// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "InputCoreTypes.h" #include "Math/NumericLimits.h" #include "Math/Ray.h" #include "Math/UnrealMath.h" #include "Math/UnrealMathSSE.h" #include "Math/Vector.h" #include "Math/Vector2D.h" #include "Misc/AssertionMacros.h" #include "Misc/EnumClassFlags.h" #include "UObject/ObjectMacros.h" #include "UObject/WeakObjectPtr.h" #include "UObject/WeakObjectPtrTemplates.h" #include "InputState.generated.h" class UObject; /** * Input event data can be applicable to many possible input devices. * These flags are used to indicate specific or sets of device types. */ UENUM() enum class EInputDevices { None = 0, Keyboard = 1, Mouse = 2, Gamepad = 4, OculusTouch = 8, HTCViveWands = 16, AnySpatialDevice = OculusTouch | HTCViveWands, TabletFingers = 1024 }; ENUM_CLASS_FLAGS(EInputDevices); /* * FInputRayHit is returned by various hit-test interface functions. * Generally this is intended to be returned as the result of a hit-test with a FInputDeviceRay */ USTRUCT(BlueprintType) struct FInputRayHit { GENERATED_BODY() /** true if ray hit something, false otherwise */ UPROPERTY(BlueprintReadWrite, Category = InputRayHit) bool bHit; /** distance along ray at which intersection occurred */ UPROPERTY(BlueprintReadWrite, Category = InputRayHit) double HitDepth; /** Normal at hit point, if available */ UPROPERTY(BlueprintReadWrite, Category = InputRayHit) FVector HitNormal; /** True if HitNormal was set */ UPROPERTY(BlueprintReadWrite, Category = InputRayHit) bool bHasHitNormal; /** Client-defined integer identifier for hit object/element/target/etc */ UPROPERTY(BlueprintReadWrite, Category = InputRayHit) int32 HitIdentifier; /** * Client-defined pointer for hit object/element/target/etc. * HitOwner and HitObject should be set to the same pointer if the HitOwner derives from UObject. */ void* HitOwner; /** * Client-defined pointer for UObject-derived hit owners. * HitOwner and HitObject should be set to the same pointer if the HitOwner derives from UObject. */ UPROPERTY(BlueprintReadWrite, Category = InputRayHit) TWeakObjectPtr HitObject; /** Set hit object, will also set hit owner to the same value */ void SetHitObject(UObject* InHitObject) { HitObject = InHitObject; HitOwner = InHitObject; } FInputRayHit() { bHit = false; HitDepth = (double)TNumericLimits::Max(); HitNormal = FVector(0, 0, 1); bHasHitNormal = false; HitIdentifier = 0; HitOwner = nullptr; HitObject = nullptr; } explicit FInputRayHit(double HitDepthIn) { bHit = true; HitDepth = HitDepthIn; HitNormal = FVector(0, 0, 1); bHasHitNormal = false; HitIdentifier = 0; HitOwner = nullptr; HitObject = nullptr; } explicit FInputRayHit(double HitDepthIn, const FVector& HitNormalIn) { bHit = true; HitDepth = HitDepthIn; HitNormal = HitNormalIn; bHasHitNormal = true; HitIdentifier = 0; HitOwner = nullptr; HitObject = nullptr; } }; /** * Current State of a physical device button (mouse, key, etc) at a point in time. * Each "click" of a button should involve at minimum two separate state * events, one where bPressed=true and one where bReleased=true. * Each of these states should occur only once. * In addition there may be additional frames where the button is * held down and bDown=true and bPressed=false. */ USTRUCT(BlueprintType) struct FDeviceButtonState { GENERATED_BODY() /** Button identifier */ UPROPERTY(transient, BlueprintReadWrite, Category = DeviceButtonState) FKey Button; /** Was the button pressed down this frame. This should happen once per "click" */ UPROPERTY(transient, BlueprintReadWrite, Category = DeviceButtonState) bool bPressed; /** Is the button currently pressed down. This should be true every frame the button is pressed. */ UPROPERTY(transient, BlueprintReadWrite, Category = DeviceButtonState) bool bDown; /** Was the button released this frame. This should happen once per "click" */ UPROPERTY(transient, BlueprintReadWrite, Category = DeviceButtonState) bool bReleased; /** Was the button double clicked this frame. This should happen only once per "double click" */ UPROPERTY(Transient, BlueprintReadWrite, Category = DeviceButtonState) bool bDoubleClicked; FDeviceButtonState() { Button = FKey(); bPressed = bDown = bReleased = bDoubleClicked = false; } FDeviceButtonState(const FKey& ButtonIn) { Button = ButtonIn; bPressed = bDown = bReleased = bDoubleClicked = false; } /** Update the states of this button */ void SetStates(bool bPressedIn, bool bDownIn, bool bReleasedIn, bool bDoubleClickedIn = false) { bPressed = bPressedIn; bDown = bDownIn; bReleased = bReleasedIn; bDoubleClicked = bDoubleClickedIn; } }; /** * Current state of active keyboard key at a point in time * @todo would be useful to track set of active keys */ USTRUCT(BlueprintType) struct FKeyboardInputDeviceState { GENERATED_BODY() /** state of active key that was modified (ie press or release) */ UPROPERTY(transient, BlueprintReadWrite, Category = KeyboardInputDeviceState) FDeviceButtonState ActiveKey; }; /** * Current State of a physical Mouse device at a point in time. */ USTRUCT(BlueprintType) struct FMouseInputDeviceState { GENERATED_BODY() /** State of the left mouse button */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FDeviceButtonState Left; /** State of the middle mouse button */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FDeviceButtonState Middle; /** State of the right mouse button */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FDeviceButtonState Right; /** Change in 'ticks' of the mouse wheel since last state event */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) float WheelDelta; /** Current 2D position of the mouse, in application-defined coordinate system */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FVector2D Position2D; /** Change in 2D mouse position from last state event */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FVector2D Delta2D; /** Ray into current 3D scene at current 2D mouse position */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FRay WorldRay; FMouseInputDeviceState() { Left = FDeviceButtonState(EKeys::LeftMouseButton); Middle = FDeviceButtonState(EKeys::MiddleMouseButton); Right = FDeviceButtonState(EKeys::RightMouseButton); WheelDelta = false; Position2D = FVector2D::ZeroVector; Delta2D = FVector2D::ZeroVector; WorldRay = FRay(); } }; /** * Current state of physical input devices at a point in time. * Assumption is that the state refers to a single physical input device, * ie InputDevice field is a single value of EInputDevices and not a combination. */ USTRUCT(BlueprintType) struct FInputDeviceState { GENERATED_BODY() /** Which InputDevice member is valid in this state */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) EInputDevices InputDevice; // // keyboard modifiers // /** Is they keyboard SHIFT modifier key currently pressed down */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) bool bShiftKeyDown; /** Is they keyboard ALT modifier key currently pressed down */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) bool bAltKeyDown; /** Is they keyboard CTRL modifier key currently pressed down */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) bool bCtrlKeyDown; /** Is they keyboard CMD modifier key currently pressed down (only on Apple devices) */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) bool bCmdKeyDown; /** Current state of Keyboard device, if InputDevice == EInputDevices::Keyboard */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FKeyboardInputDeviceState Keyboard; /** Current state of Mouse device, if InputDevice == EInputDevices::Mouse */ UPROPERTY(transient, BlueprintReadWrite, Category = MouseInputDeviceState) FMouseInputDeviceState Mouse; FInputDeviceState() { InputDevice = EInputDevices::None; bShiftKeyDown = bAltKeyDown = bCtrlKeyDown = bCmdKeyDown = false; Keyboard = FKeyboardInputDeviceState(); Mouse = FMouseInputDeviceState(); } /** Update keyboard modifier key states */ void SetModifierKeyStates(bool bShiftDown, bool bAltDown, bool bCtrlDown, bool bCmdDown) { bShiftKeyDown = bShiftDown; bAltKeyDown = bAltDown; bCtrlKeyDown = bCtrlDown; bCmdKeyDown = bCmdDown; } /** * @param DeviceType Combination of device-type flags * @return true if this input state is for an input device that matches the query flags */ bool IsFromDevice(EInputDevices DeviceType) const { return ((InputDevice & DeviceType) != EInputDevices::None); } // // utility functions to pass as lambdas // /** @return true if shift key is down in input state */ static bool IsShiftKeyDown(const FInputDeviceState& InputState) { return InputState.bShiftKeyDown; } /** @return true if ctrl key is down in input state */ static bool IsCtrlKeyDown(const FInputDeviceState& InputState) { return InputState.bCtrlKeyDown; } /** @return true if alt key is down in input state */ static bool IsAltKeyDown(const FInputDeviceState& InputState) { return InputState.bAltKeyDown; } /** @return true if Apple Command key is down in input state */ static bool IsCmdKeyDown(const FInputDeviceState& InputState) { return InputState.bCmdKeyDown; } }; /** * FInputDeviceRay represents a 3D ray created based on an input device. * If the device is a 2D input device like a mouse, then the ray may * have an associated 2D screen position. */ USTRUCT(BlueprintType) struct FInputDeviceRay { GENERATED_BODY() /** 3D ray in 3D scene, in world coordinates */ UPROPERTY(transient, BlueprintReadWrite, Category = InputDeviceRay) FRay WorldRay; /** If true, WorldRay has 2D device position coordinates */ UPROPERTY(transient, BlueprintReadWrite, Category = InputDeviceRay) bool bHas2D = false; /** 2D device position coordinates associated with the ray */ UPROPERTY(transient, BlueprintReadWrite, Category = InputDeviceRay) FVector2D ScreenPosition; // this is required for a USTRUCT FInputDeviceRay() { WorldRay = FRay(); bHas2D = false; ScreenPosition = FVector2D(0, 0); } explicit FInputDeviceRay(const FRay& WorldRayIn) { WorldRay = WorldRayIn; bHas2D = false; ScreenPosition = FVector2D(0, 0); } FInputDeviceRay(const FRay& WorldRayIn, const FVector2D& ScreenPositionIn) { WorldRay = WorldRayIn; bHas2D = true; ScreenPosition = ScreenPositionIn; } FInputDeviceRay(const FInputDeviceState& Input) { if (Input.IsFromDevice(EInputDevices::Mouse)) { WorldRay = Input.Mouse.WorldRay; bHas2D = true; ScreenPosition = Input.Mouse.Position2D; } else { ensure(false); WorldRay = FRay(FVector::ZeroVector, FVector(0, 0, 1), true); ScreenPosition = FVector2D(0, 0); bHas2D = false; } } };