176 lines
6.1 KiB
C#
176 lines
6.1 KiB
C#
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
/// <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;
|
|
private static bool _isQuitting = false;
|
|
|
|
public static InputManager Instance
|
|
{
|
|
get
|
|
{
|
|
if (_instance == null && Application.isPlaying && !_isQuitting)
|
|
{
|
|
_instance = FindAnyObjectByType<InputManager>();
|
|
if (_instance == null)
|
|
{
|
|
var go = new GameObject("InputManager");
|
|
_instance = go.AddComponent<InputManager>();
|
|
// DontDestroyOnLoad(go);
|
|
}
|
|
}
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
private PlayerInput playerInput;
|
|
private InputAction tapMoveAction;
|
|
private InputAction holdMoveAction;
|
|
private InputAction positionAction;
|
|
private ITouchInputConsumer defaultConsumer;
|
|
private bool isHoldActive;
|
|
|
|
void Awake()
|
|
{
|
|
_instance = this;
|
|
// DontDestroyOnLoad(gameObject);
|
|
playerInput = GetComponent<PlayerInput>();
|
|
if (playerInput == null)
|
|
{
|
|
Debug.LogError("[InputManager] InputManager requires a PlayerInput component attached to the same GameObject.");
|
|
return;
|
|
}
|
|
tapMoveAction = playerInput.actions.FindAction("TapMove", false);
|
|
holdMoveAction = playerInput.actions.FindAction("HoldMove", false);
|
|
positionAction = playerInput.actions.FindAction("TouchPosition", false);
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
if (tapMoveAction != null)
|
|
tapMoveAction.performed += OnTapMovePerformed;
|
|
if (holdMoveAction != null)
|
|
{
|
|
holdMoveAction.performed += OnHoldMoveStarted;
|
|
holdMoveAction.canceled += OnHoldMoveCanceled;
|
|
}
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
if (tapMoveAction != null)
|
|
tapMoveAction.performed -= OnTapMovePerformed;
|
|
if (holdMoveAction != null)
|
|
{
|
|
holdMoveAction.performed -= OnHoldMoveStarted;
|
|
holdMoveAction.canceled -= OnHoldMoveCanceled;
|
|
}
|
|
}
|
|
|
|
void OnApplicationQuit()
|
|
{
|
|
_isQuitting = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the default ITouchInputConsumer to receive input events.
|
|
/// </summary>
|
|
public void SetDefaultConsumer(ITouchInputConsumer consumer)
|
|
{
|
|
defaultConsumer = consumer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles tap input, delegates to interactable if present, otherwise to default consumer.
|
|
/// </summary>
|
|
private void OnTapMovePerformed(InputAction.CallbackContext ctx)
|
|
{
|
|
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
|
Debug.Log($"[InputManager] TapMove performed at {worldPos2D}");
|
|
if (!TryDelegateToInteractable(worldPos2D))
|
|
{
|
|
Debug.Log("[InputManager] No interactable found, forwarding tap to default consumer");
|
|
defaultConsumer?.OnTap(worldPos2D);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("[InputManager] Tap delegated to interactable");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the start of a hold input.
|
|
/// </summary>
|
|
private void OnHoldMoveStarted(InputAction.CallbackContext ctx)
|
|
{
|
|
isHoldActive = true;
|
|
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
|
Debug.Log($"[InputManager] HoldMove started at {worldPos2D}");
|
|
defaultConsumer?.OnHoldStart(worldPos2D);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the end of a hold input.
|
|
/// </summary>
|
|
private void OnHoldMoveCanceled(InputAction.CallbackContext ctx)
|
|
{
|
|
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 (isHoldActive && holdMoveAction != null && holdMoveAction.phase == InputActionPhase.Performed)
|
|
{
|
|
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
|
// Debug.Log($"[InputManager] HoldMove update at {worldPos2D}");
|
|
defaultConsumer?.OnHoldMove(worldPos2D);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to delegate a tap to an interactable at the given world position.
|
|
/// Traces on the "Interactable" channel and logs detailed info.
|
|
/// </summary>
|
|
private bool TryDelegateToInteractable(Vector2 worldPos)
|
|
{
|
|
LayerMask mask = GameManager.Instance != null ? GameManager.Instance.InteractableLayerMask : -1;
|
|
Collider2D hit = Physics2D.OverlapPoint(worldPos, mask);
|
|
if (hit != null)
|
|
{
|
|
var consumer = hit.GetComponent<ITouchInputConsumer>();
|
|
if (consumer != null)
|
|
{
|
|
Debug.unityLogger.Log("Interactable", $"[InputManager] Delegating tap to consumer at {worldPos} (GameObject: {hit.gameObject.name})");
|
|
consumer.OnTap(worldPos);
|
|
return true;
|
|
}
|
|
Debug.unityLogger.Log("Interactable", $"[InputManager] Collider2D hit at {worldPos} (GameObject: {hit.gameObject.name}), but no ITouchInputConsumer found.");
|
|
}
|
|
else
|
|
{
|
|
Debug.unityLogger.Log("Interactable", $"[InputManager] No Collider2D found at {worldPos} for interactable delegation.");
|
|
}
|
|
return false;
|
|
}
|
|
}
|