Big Input refactor
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Handles input events and dispatches them to the appropriate ITouchInputConsumer.
|
||||
/// Supports tap and hold/drag logic, with interactable delegation and debug logging.
|
||||
/// </summary>
|
||||
public class InputManager : MonoBehaviour
|
||||
{
|
||||
private static InputManager _instance;
|
||||
@@ -24,20 +27,11 @@ public class InputManager : MonoBehaviour
|
||||
}
|
||||
|
||||
private PlayerInput playerInput;
|
||||
private InputAction touchPressAction;
|
||||
private InputAction touchPositionAction;
|
||||
private InputAction tapMoveAction;
|
||||
private InputAction holdMoveAction;
|
||||
private InputAction positionAction;
|
||||
private ITouchInputConsumer defaultConsumer;
|
||||
|
||||
private bool isTouchHeld = false;
|
||||
private bool lastFrameInteracted = false;
|
||||
|
||||
// Tap/drag detection state
|
||||
private Vector2 pressStartPosition;
|
||||
private float pressStartTime;
|
||||
private bool isPressed = false;
|
||||
private bool isDragging = false;
|
||||
private float dragThreshold = 10f; // pixels
|
||||
private float tapTimeThreshold = 0.2f; // seconds
|
||||
private bool isHoldActive;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
@@ -49,98 +43,112 @@ public class InputManager : MonoBehaviour
|
||||
Debug.LogError("[InputManager] InputManager requires a PlayerInput component attached to the same GameObject.");
|
||||
return;
|
||||
}
|
||||
// Find actions by name in the assigned action map
|
||||
touchPressAction = playerInput.actions.FindAction("TouchPress", false);
|
||||
touchPositionAction = playerInput.actions.FindAction("TouchPosition", false);
|
||||
tapMoveAction = playerInput.actions.FindAction("TapMove", false);
|
||||
holdMoveAction = playerInput.actions.FindAction("HoldMove", false);
|
||||
positionAction = playerInput.actions.FindAction("TouchPosition", false);
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (touchPressAction != null)
|
||||
if (tapMoveAction != null)
|
||||
tapMoveAction.performed += OnTapMovePerformed;
|
||||
if (holdMoveAction != null)
|
||||
{
|
||||
touchPressAction.started += OnTouchPressStarted;
|
||||
touchPressAction.canceled += OnTouchPressCanceled;
|
||||
holdMoveAction.performed += OnHoldMoveStarted;
|
||||
holdMoveAction.canceled += OnHoldMoveCanceled;
|
||||
}
|
||||
if (touchPositionAction != null)
|
||||
touchPositionAction.performed += OnTouchPositionPerformed;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (touchPressAction != null)
|
||||
if (tapMoveAction != null)
|
||||
tapMoveAction.performed -= OnTapMovePerformed;
|
||||
if (holdMoveAction != null)
|
||||
{
|
||||
touchPressAction.started -= OnTouchPressStarted;
|
||||
touchPressAction.canceled -= OnTouchPressCanceled;
|
||||
holdMoveAction.performed -= OnHoldMoveStarted;
|
||||
holdMoveAction.canceled -= OnHoldMoveCanceled;
|
||||
}
|
||||
if (touchPositionAction != null)
|
||||
touchPositionAction.performed -= OnTouchPositionPerformed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default ITouchInputConsumer to receive input events.
|
||||
/// </summary>
|
||||
public void SetDefaultConsumer(ITouchInputConsumer consumer)
|
||||
{
|
||||
defaultConsumer = consumer;
|
||||
}
|
||||
|
||||
private void OnTouchPressStarted(InputAction.CallbackContext ctx)
|
||||
/// <summary>
|
||||
/// Handles tap input, delegates to interactable if present, otherwise to default consumer.
|
||||
/// </summary>
|
||||
private void OnTapMovePerformed(InputAction.CallbackContext ctx)
|
||||
{
|
||||
// Touch started (finger down)
|
||||
Vector2 screenPos = touchPositionAction.ReadValue<Vector2>();
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
lastFrameInteracted = TryDelegateToInteractable(worldPos2D);
|
||||
if (!lastFrameInteracted)
|
||||
Debug.Log($"[InputManager] TapMove performed at {worldPos2D}");
|
||||
if (!TryDelegateToInteractable(worldPos2D))
|
||||
{
|
||||
pressStartPosition = screenPos;
|
||||
pressStartTime = Time.time;
|
||||
isPressed = true;
|
||||
isDragging = false;
|
||||
defaultConsumer?.OnHoldStart(worldPos2D);
|
||||
Debug.Log("[InputManager] No interactable found, forwarding tap to default consumer");
|
||||
defaultConsumer?.OnTap(worldPos2D);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[InputManager] Tap delegated to interactable");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTouchPressCanceled(InputAction.CallbackContext ctx)
|
||||
/// <summary>
|
||||
/// Handles the start of a hold input.
|
||||
/// </summary>
|
||||
private void OnHoldMoveStarted(InputAction.CallbackContext ctx)
|
||||
{
|
||||
Vector2 screenPos = touchPositionAction.ReadValue<Vector2>();
|
||||
isHoldActive = true;
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
if (!lastFrameInteracted)
|
||||
{
|
||||
float timeHeld = Time.time - pressStartTime;
|
||||
float dist = Vector2.Distance(screenPos, pressStartPosition);
|
||||
if (!isDragging && timeHeld < tapTimeThreshold && dist < dragThreshold)
|
||||
{
|
||||
defaultConsumer?.OnTap(worldPos2D);
|
||||
}
|
||||
defaultConsumer?.OnHoldEnd(worldPos2D);
|
||||
}
|
||||
isPressed = false;
|
||||
isDragging = false;
|
||||
lastFrameInteracted = false;
|
||||
Debug.Log($"[InputManager] HoldMove started at {worldPos2D}");
|
||||
defaultConsumer?.OnHoldStart(worldPos2D);
|
||||
}
|
||||
|
||||
private void OnTouchPositionPerformed(InputAction.CallbackContext ctx)
|
||||
/// <summary>
|
||||
/// Handles the end of a hold input.
|
||||
/// </summary>
|
||||
private void OnHoldMoveCanceled(InputAction.CallbackContext ctx)
|
||||
{
|
||||
// No longer needed, OnTouchHeld will be handled in Update
|
||||
if (!isHoldActive) return;
|
||||
isHoldActive = false;
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
Debug.Log($"[InputManager] HoldMove canceled at {worldPos2D}");
|
||||
defaultConsumer?.OnHoldEnd(worldPos2D);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continuously updates hold move input while active.
|
||||
/// </summary>
|
||||
void Update()
|
||||
{
|
||||
if (isPressed && touchPositionAction != null)
|
||||
if (isHoldActive && holdMoveAction != null && holdMoveAction.phase == InputActionPhase.Performed)
|
||||
{
|
||||
Vector2 screenPos = touchPositionAction.ReadValue<Vector2>();
|
||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||
defaultConsumer?.OnHold(worldPos2D);
|
||||
Debug.Log($"[InputManager] HoldMove update at {worldPos2D}");
|
||||
defaultConsumer?.OnHoldMove(worldPos2D);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to delegate a tap to an interactable at the given world position.
|
||||
/// </summary>
|
||||
private bool TryDelegateToInteractable(Vector2 worldPos)
|
||||
{
|
||||
// Raycast at the world position to find an Interactable
|
||||
Collider2D hit = Physics2D.OverlapPoint(worldPos);
|
||||
if (hit != null)
|
||||
{
|
||||
var interactable = hit.GetComponent<Interactable>();
|
||||
var interactable = hit.GetComponent<ITouchInputConsumer>();
|
||||
if (interactable != null)
|
||||
{
|
||||
interactable.OnTap(worldPos);
|
||||
|
||||
Reference in New Issue
Block a user