153 lines
4.8 KiB
C#
153 lines
4.8 KiB
C#
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
using System;
|
|
|
|
public class InputManager : MonoBehaviour
|
|
{
|
|
private static InputManager _instance;
|
|
public static InputManager Instance
|
|
{
|
|
get
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
_instance = FindAnyObjectByType<InputManager>();
|
|
if (_instance == null)
|
|
{
|
|
var go = new GameObject("InputManager");
|
|
_instance = go.AddComponent<InputManager>();
|
|
DontDestroyOnLoad(go);
|
|
}
|
|
}
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
private PlayerInput playerInput;
|
|
private InputAction touchPressAction;
|
|
private InputAction touchPositionAction;
|
|
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
|
|
|
|
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;
|
|
}
|
|
// Find actions by name in the assigned action map
|
|
touchPressAction = playerInput.actions.FindAction("TouchPress", false);
|
|
touchPositionAction = playerInput.actions.FindAction("TouchPosition", false);
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
if (touchPressAction != null)
|
|
{
|
|
touchPressAction.started += OnTouchPressStarted;
|
|
touchPressAction.canceled += OnTouchPressCanceled;
|
|
}
|
|
if (touchPositionAction != null)
|
|
touchPositionAction.performed += OnTouchPositionPerformed;
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
if (touchPressAction != null)
|
|
{
|
|
touchPressAction.started -= OnTouchPressStarted;
|
|
touchPressAction.canceled -= OnTouchPressCanceled;
|
|
}
|
|
if (touchPositionAction != null)
|
|
touchPositionAction.performed -= OnTouchPositionPerformed;
|
|
}
|
|
|
|
public void SetDefaultConsumer(ITouchInputConsumer consumer)
|
|
{
|
|
defaultConsumer = consumer;
|
|
}
|
|
|
|
private void OnTouchPressStarted(InputAction.CallbackContext ctx)
|
|
{
|
|
// Touch started (finger down)
|
|
Vector2 screenPos = touchPositionAction.ReadValue<Vector2>();
|
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
|
lastFrameInteracted = TryDelegateToInteractable(worldPos2D);
|
|
if (!lastFrameInteracted)
|
|
{
|
|
pressStartPosition = screenPos;
|
|
pressStartTime = Time.time;
|
|
isPressed = true;
|
|
isDragging = false;
|
|
defaultConsumer?.OnHoldStart(worldPos2D);
|
|
}
|
|
}
|
|
|
|
private void OnTouchPressCanceled(InputAction.CallbackContext ctx)
|
|
{
|
|
Vector2 screenPos = touchPositionAction.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;
|
|
}
|
|
|
|
private void OnTouchPositionPerformed(InputAction.CallbackContext ctx)
|
|
{
|
|
// No longer needed, OnTouchHeld will be handled in Update
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (isPressed && touchPositionAction != null)
|
|
{
|
|
Vector2 screenPos = touchPositionAction.ReadValue<Vector2>();
|
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
|
defaultConsumer?.OnHold(worldPos2D);
|
|
}
|
|
}
|
|
|
|
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>();
|
|
if (interactable != null)
|
|
{
|
|
interactable.OnTap(worldPos);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|