chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
427
Assets/Scripts/Combat/LookInputRouter.cs
Normal file
427
Assets/Scripts/Combat/LookInputRouter.cs
Normal file
@@ -0,0 +1,427 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.EnhancedTouch;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using EnhancedTouchSupport = UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport;
|
||||
using EnhancedTouchTouch = UnityEngine.InputSystem.EnhancedTouch.Touch;
|
||||
using EnhancedTouchPhase = UnityEngine.InputSystem.TouchPhase;
|
||||
using UnityTouch = UnityEngine.Touch;
|
||||
using TouchPhaseLegacy = UnityEngine.TouchPhase;
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates look input from mouse, gamepad sticks, and mobile touch into a single delta value.
|
||||
/// Works with either the legacy Input Manager or the newer Input System.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class LookInputRouter : MonoBehaviour
|
||||
{
|
||||
private const float TinyThreshold = 0.0001f;
|
||||
|
||||
[Header("General")]
|
||||
[SerializeField] private bool autoDetectInputSystem = true;
|
||||
[SerializeField] private bool preferNewInputSystem = true;
|
||||
|
||||
[Header("Input System (New)")]
|
||||
[Tooltip("Optional PlayerInput component containing the action map with a Look action.")]
|
||||
[SerializeField] private PlayerInput playerInput;
|
||||
|
||||
[Tooltip("Action name used for look deltas when using the new Input System.")]
|
||||
[SerializeField] private string lookActionName = "Look";
|
||||
|
||||
[Header("Legacy Input Manager")]
|
||||
[SerializeField] private string mouseXAxis = "Mouse X";
|
||||
[SerializeField] private string mouseYAxis = "Mouse Y";
|
||||
[SerializeField] private string rightStickXAxis = "Joystick X";
|
||||
[SerializeField] private string rightStickYAxis = "Joystick Y";
|
||||
|
||||
[Header("Sensitivities")]
|
||||
[SerializeField] private float mouseSensitivity = 1.0f;
|
||||
[SerializeField] private float stickSensitivity = 120.0f;
|
||||
[SerializeField] private float touchSensitivity = 0.15f;
|
||||
|
||||
[Header("Filtering")]
|
||||
[Tooltip("Inputs below this magnitude will be ignored and reset to zero.")]
|
||||
[SerializeField] private float deadzone = 0.05f;
|
||||
|
||||
[Tooltip("Optional smoothing duration in seconds. Set to zero for raw input.")]
|
||||
[SerializeField] private float smoothingTime = 0.08f;
|
||||
|
||||
[Tooltip("Maximum magnitude allowed for a single look delta.")]
|
||||
[SerializeField] private float maxMagnitude = 5.0f;
|
||||
|
||||
private InputAction cachedLookAction;
|
||||
private bool usingNewInputSystem;
|
||||
|
||||
private Vector2 currentDelta;
|
||||
private Vector2 smoothVelocity;
|
||||
|
||||
// Touch state
|
||||
private int activeTouchId = -1;
|
||||
private Vector2 lastTouchPosition;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
DetectInputSystem();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Enable Enhanced Touch so we can use the high-level Touch struct API.
|
||||
EnhancedTouchSupport.Enable();
|
||||
|
||||
if (usingNewInputSystem)
|
||||
{
|
||||
EnableLookAction();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (cachedLookAction != null)
|
||||
{
|
||||
cachedLookAction.Disable();
|
||||
}
|
||||
cachedLookAction = null;
|
||||
|
||||
// Pair with Enable() to keep the global reference count balanced.
|
||||
EnhancedTouchSupport.Disable();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
Vector2 rawDelta = usingNewInputSystem ? ReadNewSystemDelta() : ReadLegacyDelta();
|
||||
|
||||
if (!usingNewInputSystem && preferNewInputSystem)
|
||||
{
|
||||
// If no PlayerInput was supplied but the Input System package is present,
|
||||
// fall back to gathering from Gamepad/Touchscreen directly.
|
||||
rawDelta += ReadDirectDeviceDelta();
|
||||
}
|
||||
|
||||
rawDelta = Vector2.ClampMagnitude(rawDelta, maxMagnitude);
|
||||
|
||||
if (smoothingTime > TinyThreshold)
|
||||
{
|
||||
currentDelta = Vector2.SmoothDamp(currentDelta, rawDelta, ref smoothVelocity, smoothingTime, Mathf.Infinity, Time.unscaledDeltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentDelta = rawDelta;
|
||||
}
|
||||
|
||||
if (currentDelta.magnitude < deadzone)
|
||||
{
|
||||
currentDelta = Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetLookDelta(out Vector2 delta)
|
||||
{
|
||||
delta = currentDelta;
|
||||
return delta.sqrMagnitude > TinyThreshold;
|
||||
}
|
||||
|
||||
private void DetectInputSystem()
|
||||
{
|
||||
if (!autoDetectInputSystem)
|
||||
{
|
||||
usingNewInputSystem = preferNewInputSystem && playerInput != null;
|
||||
CacheLookAction();
|
||||
return;
|
||||
}
|
||||
|
||||
// If PlayerInput is explicitly assigned, use it.
|
||||
if (playerInput == null)
|
||||
{
|
||||
playerInput = GetComponent<PlayerInput>();
|
||||
}
|
||||
|
||||
if (playerInput != null && preferNewInputSystem)
|
||||
{
|
||||
usingNewInputSystem = true;
|
||||
CacheLookAction();
|
||||
}
|
||||
else
|
||||
{
|
||||
usingNewInputSystem = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheLookAction()
|
||||
{
|
||||
if (playerInput == null)
|
||||
return;
|
||||
|
||||
var actions = playerInput.actions;
|
||||
if (actions == null)
|
||||
return;
|
||||
|
||||
cachedLookAction = actions.FindAction(lookActionName, false);
|
||||
if (cachedLookAction != null && !cachedLookAction.enabled && isActiveAndEnabled)
|
||||
{
|
||||
cachedLookAction.SafeEnable();
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableLookAction()
|
||||
{
|
||||
if (cachedLookAction == null && playerInput != null)
|
||||
{
|
||||
CacheLookAction();
|
||||
}
|
||||
|
||||
cachedLookAction?.SafeEnable();
|
||||
}
|
||||
|
||||
private Vector2 ReadNewSystemDelta()
|
||||
{
|
||||
if (cachedLookAction == null)
|
||||
return ReadDirectDeviceDelta();
|
||||
|
||||
Vector2 value = cachedLookAction.ReadValue<Vector2>();
|
||||
return value * Time.unscaledDeltaTime;
|
||||
}
|
||||
|
||||
private Vector2 ReadLegacyDelta()
|
||||
{
|
||||
// Replace legacy Input.GetAxis usage with InputSystem device reads where possible.
|
||||
float x = 0f, y = 0f;
|
||||
|
||||
if (UnityEngine.InputSystem.Mouse.current != null)
|
||||
{
|
||||
Vector2 mouseDelta = UnityEngine.InputSystem.Mouse.current.delta.ReadValue();
|
||||
x = mouseDelta.x * mouseSensitivity;
|
||||
y = mouseDelta.y * mouseSensitivity;
|
||||
}
|
||||
|
||||
float stickX = 0f, stickY = 0f;
|
||||
if (UnityEngine.InputSystem.Gamepad.current != null)
|
||||
{
|
||||
Vector2 right = UnityEngine.InputSystem.Gamepad.current.rightStick.ReadValue();
|
||||
stickX = right.x * stickSensitivity * Time.unscaledDeltaTime;
|
||||
stickY = right.y * stickSensitivity * Time.unscaledDeltaTime;
|
||||
}
|
||||
|
||||
return new Vector2(x + stickX, y + stickY);
|
||||
}
|
||||
|
||||
private Vector2 ReadDirectDeviceDelta()
|
||||
{
|
||||
Vector2 aggregate = Vector2.zero;
|
||||
|
||||
if (Mouse.current != null)
|
||||
{
|
||||
Vector2 mouseDelta = Mouse.current.delta.ReadValue();
|
||||
aggregate += mouseDelta * mouseSensitivity * Time.unscaledDeltaTime;
|
||||
}
|
||||
|
||||
if (Gamepad.current != null)
|
||||
{
|
||||
Vector2 stick = Gamepad.current.rightStick.ReadValue();
|
||||
aggregate += stick * stickSensitivity * Time.unscaledDeltaTime;
|
||||
}
|
||||
|
||||
aggregate += ReadTouchDelta();
|
||||
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
private Vector2 ReadTouchDelta()
|
||||
{
|
||||
if (EnhancedTouchSupport.enabled)
|
||||
{
|
||||
var enhancedDelta = ReadEnhancedTouchDelta();
|
||||
if (enhancedDelta != Vector2.zero || EnhancedTouchTouch.activeTouches.Count > 0)
|
||||
return enhancedDelta;
|
||||
}
|
||||
|
||||
if (Touchscreen.current != null)
|
||||
{
|
||||
var touchscreenDelta = ReadTouchscreenDelta();
|
||||
if (touchscreenDelta != Vector2.zero)
|
||||
return touchscreenDelta;
|
||||
}
|
||||
|
||||
return ReadLegacyTouch();
|
||||
}
|
||||
|
||||
private Vector2 ReadEnhancedTouchDelta()
|
||||
{
|
||||
var touches = EnhancedTouchTouch.activeTouches;
|
||||
if (touches.Count <= 0)
|
||||
{
|
||||
ResetTouchTracking();
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
for (int i = 0; i < touches.Count; ++i)
|
||||
{
|
||||
var touch = touches[i];
|
||||
if (!touch.valid)
|
||||
continue;
|
||||
|
||||
if (activeTouchId == -1)
|
||||
{
|
||||
activeTouchId = touch.touchId;
|
||||
lastTouchPosition = touch.screenPosition;
|
||||
}
|
||||
|
||||
if (touch.touchId != activeTouchId)
|
||||
continue;
|
||||
|
||||
if (touch.phase == EnhancedTouchPhase.Ended || touch.phase == EnhancedTouchPhase.Canceled)
|
||||
{
|
||||
ResetTouchTracking();
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
if (touch.phase == EnhancedTouchPhase.Began)
|
||||
{
|
||||
lastTouchPosition = touch.screenPosition;
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
Vector2 delta = (touch.screenPosition - lastTouchPosition) * touchSensitivity * Time.unscaledDeltaTime;
|
||||
lastTouchPosition = touch.screenPosition;
|
||||
return delta;
|
||||
}
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
private Vector2 ReadTouchscreenDelta()
|
||||
{
|
||||
if (Touchscreen.current == null)
|
||||
{
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
if (!AnyTouchPressed(Touchscreen.current.touches))
|
||||
{
|
||||
ResetTouchTracking();
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
foreach (var touchControl in Touchscreen.current.touches)
|
||||
{
|
||||
if (!touchControl.press.isPressed)
|
||||
continue;
|
||||
|
||||
int id = touchControl.touchId.ReadValue();
|
||||
Vector2 position = touchControl.position.ReadValue();
|
||||
|
||||
if (activeTouchId == -1)
|
||||
{
|
||||
activeTouchId = id;
|
||||
lastTouchPosition = position;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (activeTouchId != id)
|
||||
continue;
|
||||
|
||||
Vector2 delta = (position - lastTouchPosition) * touchSensitivity;
|
||||
lastTouchPosition = position;
|
||||
return delta * Time.unscaledDeltaTime;
|
||||
}
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
private Vector2 ReadLegacyTouch()
|
||||
{
|
||||
// Prefer the Input System touchscreen if available. Fall back to legacy Input.touches only when necessary.
|
||||
if (UnityEngine.InputSystem.Touchscreen.current != null)
|
||||
{
|
||||
var touches = UnityEngine.InputSystem.Touchscreen.current.touches;
|
||||
if (touches.Count <= 0)
|
||||
{
|
||||
ResetTouchTracking();
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
for (int i = 0; i < touches.Count; ++i)
|
||||
{
|
||||
var tc = touches[i];
|
||||
if (!tc.press.isPressed)
|
||||
continue;
|
||||
|
||||
int id = tc.touchId.ReadValue();
|
||||
Vector2 pos = tc.position.ReadValue();
|
||||
|
||||
// We already check tc.press.isPressed above. No additional phase check is required
|
||||
// here for the Input System touch path. This avoids ambiguous TouchPhase enum
|
||||
// comparisons between the legacy and new Input System types.
|
||||
|
||||
if (activeTouchId == -1)
|
||||
{
|
||||
activeTouchId = id;
|
||||
lastTouchPosition = pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (activeTouchId != id)
|
||||
continue;
|
||||
|
||||
Vector2 delta = (pos - lastTouchPosition) * touchSensitivity * Time.unscaledDeltaTime;
|
||||
lastTouchPosition = pos;
|
||||
return delta;
|
||||
}
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
// Legacy fallback
|
||||
if (Input.touchCount <= 0)
|
||||
{
|
||||
ResetTouchTracking();
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Input.touchCount; i++)
|
||||
{
|
||||
UnityTouch touch = Input.GetTouch(i);
|
||||
if (touch.phase == TouchPhaseLegacy.Ended || touch.phase == TouchPhaseLegacy.Canceled)
|
||||
continue;
|
||||
|
||||
if (activeTouchId == -1)
|
||||
{
|
||||
activeTouchId = touch.fingerId;
|
||||
lastTouchPosition = touch.position;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (activeTouchId != touch.fingerId)
|
||||
continue;
|
||||
|
||||
Vector2 delta = touch.deltaPosition * touchSensitivity * Time.unscaledDeltaTime;
|
||||
lastTouchPosition = touch.position;
|
||||
return delta;
|
||||
}
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
private void ResetTouchTracking()
|
||||
{
|
||||
activeTouchId = -1;
|
||||
lastTouchPosition = Vector2.zero;
|
||||
}
|
||||
|
||||
public void ResetLookDelta()
|
||||
{
|
||||
currentDelta = Vector2.zero;
|
||||
smoothVelocity = Vector2.zero;
|
||||
}
|
||||
private static bool AnyTouchPressed(ReadOnlyArray<TouchControl> touches)
|
||||
{
|
||||
for (int i = 0; i < touches.Count; ++i)
|
||||
{
|
||||
if (touches[i].press.isPressed)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user