2026-04-29 14:44:30 +08:00
|
|
|
|
#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION
|
2026-03-20 16:46:06 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using UnityEngine.InputSystem;
|
|
|
|
|
|
using UnityEngine.InputSystem.Controls;
|
|
|
|
|
|
|
|
|
|
|
|
namespace UnityEngine.UI
|
|
|
|
|
|
{
|
|
|
|
|
|
internal sealed class UXInputModeService : MonoBehaviour
|
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
private const float StickThresholdSqr = 0.04f;
|
|
|
|
|
|
private const float AxisThreshold = 0.2f;
|
|
|
|
|
|
|
2026-04-29 14:44:30 +08:00
|
|
|
|
private const string PointerActionName = "UXPointerInput";
|
|
|
|
|
|
private const string KeyboardActionName = "UXKeyboardInput";
|
|
|
|
|
|
private const string GamepadActionName = "UXGamepadInput";
|
|
|
|
|
|
private const string TouchActionName = "UXTouchInput";
|
|
|
|
|
|
private const string MouseDeltaBinding = "<Mouse>/delta";
|
|
|
|
|
|
private const string MouseScrollBinding = "<Mouse>/scroll";
|
|
|
|
|
|
private const string MouseLeftButtonBinding = "<Mouse>/leftButton";
|
|
|
|
|
|
private const string MouseRightButtonBinding = "<Mouse>/rightButton";
|
|
|
|
|
|
private const string MouseMiddleButtonBinding = "<Mouse>/middleButton";
|
|
|
|
|
|
private const string KeyboardAnyKeyBinding = "<Keyboard>/anyKey";
|
|
|
|
|
|
private const string GamepadButtonSouthBinding = "<Gamepad>/buttonSouth";
|
|
|
|
|
|
private const string GamepadButtonNorthBinding = "<Gamepad>/buttonNorth";
|
|
|
|
|
|
private const string GamepadButtonEastBinding = "<Gamepad>/buttonEast";
|
|
|
|
|
|
private const string GamepadButtonWestBinding = "<Gamepad>/buttonWest";
|
|
|
|
|
|
private const string GamepadStartButtonBinding = "<Gamepad>/startButton";
|
|
|
|
|
|
private const string GamepadSelectButtonBinding = "<Gamepad>/selectButton";
|
|
|
|
|
|
private const string GamepadLeftShoulderBinding = "<Gamepad>/leftShoulder";
|
|
|
|
|
|
private const string GamepadRightShoulderBinding = "<Gamepad>/rightShoulder";
|
|
|
|
|
|
private const string GamepadDpadBinding = "<Gamepad>/dpad";
|
|
|
|
|
|
private const string GamepadLeftStickBinding = "<Gamepad>/leftStick";
|
|
|
|
|
|
private const string GamepadRightStickBinding = "<Gamepad>/rightStick";
|
|
|
|
|
|
private const string TouchPressBinding = "<Touchscreen>/primaryTouch/press";
|
|
|
|
|
|
private const string TouchDeltaBinding = "<Touchscreen>/primaryTouch/delta";
|
|
|
|
|
|
|
2026-03-20 16:46:06 +08:00
|
|
|
|
private static UXInputModeService _instance;
|
|
|
|
|
|
|
|
|
|
|
|
private InputAction _pointerAction;
|
2026-04-28 20:52:06 +08:00
|
|
|
|
private InputAction _keyboardAction;
|
2026-03-20 16:46:06 +08:00
|
|
|
|
private InputAction _gamepadAction;
|
2026-04-28 20:52:06 +08:00
|
|
|
|
private InputAction _touchAction;
|
2026-03-20 16:46:06 +08:00
|
|
|
|
|
|
|
|
|
|
public static UXInputMode CurrentMode { get; private set; } = UXInputMode.Pointer;
|
|
|
|
|
|
|
|
|
|
|
|
public static event Action<UXInputMode> OnModeChanged;
|
|
|
|
|
|
|
|
|
|
|
|
internal static UXInputModeService EnsureInstance()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
|
GameObject go = new GameObject("[UXInputModeService]");
|
2026-03-20 16:46:06 +08:00
|
|
|
|
go.hideFlags = HideFlags.HideAndDontSave;
|
|
|
|
|
|
DontDestroyOnLoad(go);
|
|
|
|
|
|
_instance = go.AddComponent<UXInputModeService>();
|
|
|
|
|
|
return _instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_instance != null && _instance != this)
|
|
|
|
|
|
{
|
|
|
|
|
|
Destroy(gameObject);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_instance = this;
|
|
|
|
|
|
DontDestroyOnLoad(gameObject);
|
|
|
|
|
|
hideFlags = HideFlags.HideAndDontSave;
|
2026-04-28 20:52:06 +08:00
|
|
|
|
CreateActions();
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnEnable()
|
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
SetActionsEnabled(true);
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnDisable()
|
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
SetActionsEnabled(false);
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
DisposeActions();
|
|
|
|
|
|
if (_instance == this)
|
|
|
|
|
|
{
|
|
|
|
|
|
_instance = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CreateActions()
|
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
if (_pointerAction != null)
|
2026-03-20 16:46:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-29 14:44:30 +08:00
|
|
|
|
_pointerAction = new InputAction(PointerActionName, InputActionType.PassThrough);
|
|
|
|
|
|
_pointerAction.AddBinding(MouseDeltaBinding);
|
|
|
|
|
|
_pointerAction.AddBinding(MouseScrollBinding);
|
|
|
|
|
|
_pointerAction.AddBinding(MouseLeftButtonBinding);
|
|
|
|
|
|
_pointerAction.AddBinding(MouseRightButtonBinding);
|
|
|
|
|
|
_pointerAction.AddBinding(MouseMiddleButtonBinding);
|
2026-03-20 16:46:06 +08:00
|
|
|
|
_pointerAction.performed += OnPointerInput;
|
2026-04-28 20:52:06 +08:00
|
|
|
|
|
2026-04-29 14:44:30 +08:00
|
|
|
|
_keyboardAction = new InputAction(KeyboardActionName, InputActionType.PassThrough);
|
|
|
|
|
|
_keyboardAction.AddBinding(KeyboardAnyKeyBinding);
|
2026-04-28 20:52:06 +08:00
|
|
|
|
_keyboardAction.performed += OnKeyboardInput;
|
2026-03-20 16:46:06 +08:00
|
|
|
|
|
2026-04-29 14:44:30 +08:00
|
|
|
|
_gamepadAction = new InputAction(GamepadActionName, InputActionType.PassThrough);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadButtonSouthBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadButtonNorthBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadButtonEastBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadButtonWestBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadStartButtonBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadSelectButtonBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadLeftShoulderBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadRightShoulderBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadDpadBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadLeftStickBinding);
|
|
|
|
|
|
_gamepadAction.AddBinding(GamepadRightStickBinding);
|
2026-03-20 16:46:06 +08:00
|
|
|
|
_gamepadAction.performed += OnGamepadInput;
|
2026-04-28 20:52:06 +08:00
|
|
|
|
|
2026-04-29 14:44:30 +08:00
|
|
|
|
_touchAction = new InputAction(TouchActionName, InputActionType.PassThrough);
|
|
|
|
|
|
_touchAction.AddBinding(TouchPressBinding);
|
|
|
|
|
|
_touchAction.AddBinding(TouchDeltaBinding);
|
2026-04-28 20:52:06 +08:00
|
|
|
|
_touchAction.performed += OnTouchInput;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void SetActionsEnabled(bool enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_pointerAction == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
_pointerAction.Enable();
|
|
|
|
|
|
_keyboardAction.Enable();
|
|
|
|
|
|
_gamepadAction.Enable();
|
|
|
|
|
|
_touchAction.Enable();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_pointerAction.Disable();
|
|
|
|
|
|
_keyboardAction.Disable();
|
|
|
|
|
|
_gamepadAction.Disable();
|
|
|
|
|
|
_touchAction.Disable();
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DisposeActions()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_pointerAction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_pointerAction.performed -= OnPointerInput;
|
|
|
|
|
|
_pointerAction.Dispose();
|
|
|
|
|
|
_pointerAction = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
|
if (_keyboardAction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_keyboardAction.performed -= OnKeyboardInput;
|
|
|
|
|
|
_keyboardAction.Dispose();
|
|
|
|
|
|
_keyboardAction = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 16:46:06 +08:00
|
|
|
|
if (_gamepadAction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_gamepadAction.performed -= OnGamepadInput;
|
|
|
|
|
|
_gamepadAction.Dispose();
|
|
|
|
|
|
_gamepadAction = null;
|
|
|
|
|
|
}
|
2026-04-28 20:52:06 +08:00
|
|
|
|
|
|
|
|
|
|
if (_touchAction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_touchAction.performed -= OnTouchInput;
|
|
|
|
|
|
_touchAction.Dispose();
|
|
|
|
|
|
_touchAction = null;
|
|
|
|
|
|
}
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void OnPointerInput(InputAction.CallbackContext context)
|
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
if (IsInputMeaningful(context.control))
|
2026-03-20 16:46:06 +08:00
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
SetMode(UXInputMode.Pointer);
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
2026-04-28 20:52:06 +08:00
|
|
|
|
}
|
2026-03-20 16:46:06 +08:00
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
|
private static void OnKeyboardInput(InputAction.CallbackContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (IsInputMeaningful(context.control))
|
|
|
|
|
|
{
|
|
|
|
|
|
SetMode(UXInputMode.Keyboard);
|
|
|
|
|
|
}
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void OnGamepadInput(InputAction.CallbackContext context)
|
|
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
if (IsInputMeaningful(context.control))
|
2026-03-20 16:46:06 +08:00
|
|
|
|
{
|
2026-04-28 20:52:06 +08:00
|
|
|
|
SetMode(UXInputMode.Gamepad);
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
2026-04-28 20:52:06 +08:00
|
|
|
|
}
|
2026-03-20 16:46:06 +08:00
|
|
|
|
|
2026-04-28 20:52:06 +08:00
|
|
|
|
private static void OnTouchInput(InputAction.CallbackContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (IsInputMeaningful(context.control))
|
|
|
|
|
|
{
|
|
|
|
|
|
SetMode(UXInputMode.Touch);
|
|
|
|
|
|
}
|
2026-03-20 16:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static bool IsInputMeaningful(InputControl control)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (control == null || control.device == null || control.synthetic)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (control)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ButtonControl button:
|
|
|
|
|
|
return button.IsPressed();
|
|
|
|
|
|
case StickControl stick:
|
2026-04-28 20:52:06 +08:00
|
|
|
|
return stick.ReadValue().sqrMagnitude >= StickThresholdSqr;
|
2026-03-20 16:46:06 +08:00
|
|
|
|
case Vector2Control vector2:
|
2026-04-28 20:52:06 +08:00
|
|
|
|
return vector2.ReadValue().sqrMagnitude >= StickThresholdSqr;
|
2026-03-20 16:46:06 +08:00
|
|
|
|
case AxisControl axis:
|
2026-04-28 20:52:06 +08:00
|
|
|
|
return Mathf.Abs(axis.ReadValue()) >= AxisThreshold;
|
2026-03-20 16:46:06 +08:00
|
|
|
|
default:
|
|
|
|
|
|
return !control.noisy;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal static void SetMode(UXInputMode mode)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CurrentMode == mode)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CurrentMode = mode;
|
|
|
|
|
|
OnModeChanged?.Invoke(mode);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
2026-04-29 14:44:30 +08:00
|
|
|
|
|