Big Input refactor
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
"initialStateCheck": true
|
"initialStateCheck": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "TouchPress",
|
"name": "TapMove",
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"id": "a77a5b0d-26e8-4b6c-b23b-828c50b3108f",
|
"id": "a77a5b0d-26e8-4b6c-b23b-828c50b3108f",
|
||||||
"expectedControlType": "",
|
"expectedControlType": "",
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
"initialStateCheck": false
|
"initialStateCheck": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "TouchDelta",
|
"name": "HoldMove",
|
||||||
"type": "Value",
|
"type": "Button",
|
||||||
"id": "99ae0aa3-a70e-4afc-8a8a-c7d6144fa75b",
|
"id": "99ae0aa3-a70e-4afc-8a8a-c7d6144fa75b",
|
||||||
"expectedControlType": "Vector2",
|
"expectedControlType": "",
|
||||||
"processors": "",
|
"processors": "",
|
||||||
"interactions": "",
|
"interactions": "",
|
||||||
"initialStateCheck": true
|
"initialStateCheck": true
|
||||||
@@ -50,21 +50,21 @@
|
|||||||
"name": "",
|
"name": "",
|
||||||
"id": "f3dcd77f-15e5-4621-af67-001e6b08e3e6",
|
"id": "f3dcd77f-15e5-4621-af67-001e6b08e3e6",
|
||||||
"path": "<Touchscreen>/Press",
|
"path": "<Touchscreen>/Press",
|
||||||
"interactions": "Press(behavior=2)",
|
"interactions": "Tap(duration=0.2)",
|
||||||
"processors": "",
|
"processors": "",
|
||||||
"groups": "",
|
"groups": "",
|
||||||
"action": "TouchPress",
|
"action": "TapMove",
|
||||||
"isComposite": false,
|
"isComposite": false,
|
||||||
"isPartOfComposite": false
|
"isPartOfComposite": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "",
|
"name": "",
|
||||||
"id": "19cc6a3f-9316-4da5-8e35-8297e02b8f6c",
|
"id": "19cc6a3f-9316-4da5-8e35-8297e02b8f6c",
|
||||||
"path": "<Touchscreen>/delta",
|
"path": "<Touchscreen>/Press",
|
||||||
"interactions": "",
|
"interactions": "Hold(duration=0.2)",
|
||||||
"processors": "",
|
"processors": "",
|
||||||
"groups": "",
|
"groups": "",
|
||||||
"action": "TouchDelta",
|
"action": "HoldMove",
|
||||||
"isComposite": false,
|
"isComposite": false,
|
||||||
"isPartOfComposite": false
|
"isPartOfComposite": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles endless descender movement in response to tap and hold input events.
|
||||||
|
/// Moves the character horizontally to follow the finger or tap position.
|
||||||
|
/// </summary>
|
||||||
public class EndlessDescenderController : MonoBehaviour, ITouchInputConsumer
|
public class EndlessDescenderController : MonoBehaviour, ITouchInputConsumer
|
||||||
{
|
{
|
||||||
private float targetFingerX;
|
private float targetFingerX;
|
||||||
@@ -20,45 +24,41 @@ public class EndlessDescenderController : MonoBehaviour, ITouchInputConsumer
|
|||||||
isTouchActive = false;
|
isTouchActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement new ITouchInputConsumer contract
|
/// <summary>
|
||||||
|
/// Handles tap input. Moves to the tapped X position.
|
||||||
|
/// </summary>
|
||||||
public void OnTap(Vector2 worldPosition)
|
public void OnTap(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
// Treat tap as a quick move to the tapped X position
|
Debug.Log($"[EndlessDescenderController] OnTap at {worldPosition}");
|
||||||
targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
||||||
isTouchActive = true;
|
isTouchActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDragStart(Vector2 position)
|
/// <summary>
|
||||||
{
|
/// Handles the start of a hold input. Begins tracking the finger.
|
||||||
//
|
/// </summary>
|
||||||
}
|
|
||||||
|
|
||||||
public void OnDrag(Vector2 position)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnDragEnd(Vector2 position)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnHoldStart(Vector2 worldPosition)
|
public void OnHoldStart(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
// Start hold, update target X
|
Debug.Log($"[EndlessDescenderController] OnHoldStart at {worldPosition}");
|
||||||
targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
||||||
isTouchActive = true;
|
isTouchActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnHold(Vector2 worldPosition)
|
/// <summary>
|
||||||
|
/// Handles hold move input. Updates the target X position as the finger moves.
|
||||||
|
/// </summary>
|
||||||
|
public void OnHoldMove(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
// Update target x as finger moves
|
Debug.Log($"[EndlessDescenderController] OnHoldMove at {worldPosition}");
|
||||||
targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the end of a hold input. Stops tracking.
|
||||||
|
/// </summary>
|
||||||
public void OnHoldEnd(Vector2 worldPosition)
|
public void OnHoldEnd(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
// Stop hold
|
Debug.Log($"[EndlessDescenderController] OnHoldEnd at {worldPosition}");
|
||||||
isTouchActive = false;
|
isTouchActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +87,5 @@ public class EndlessDescenderController : MonoBehaviour, ITouchInputConsumer
|
|||||||
newY += wobble.VerticalOffset;
|
newY += wobble.VerticalOffset;
|
||||||
}
|
}
|
||||||
transform.position = new Vector3(newX, newY, transform.position.z);
|
transform.position = new Vector3(newX, newY, transform.position.z);
|
||||||
// Debug.Log($"EndlessDescenderController: Moved from {oldPos} to {transform.position} (targetX={targetX}, lerpSpeed={lerpSpeed}, offset={offset}, exponent={exponent}, moveStep={moveStep})");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optionally, handle touch release if needed
|
|
||||||
// You can add a method to reset isTouchActive if desired
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
public interface ITouchInputConsumer
|
public interface ITouchInputConsumer
|
||||||
{
|
{
|
||||||
void OnTap(Vector2 position);
|
void OnTap(Vector2 position);
|
||||||
void OnDragStart(Vector2 position);
|
|
||||||
void OnDrag(Vector2 position);
|
|
||||||
void OnDragEnd(Vector2 position);
|
|
||||||
void OnHoldStart(Vector2 position);
|
void OnHoldStart(Vector2 position);
|
||||||
void OnHold(Vector2 position);
|
void OnHoldMove(Vector2 position);
|
||||||
void OnHoldEnd(Vector2 position);
|
void OnHoldEnd(Vector2 position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
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
|
public class InputManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
private static InputManager _instance;
|
private static InputManager _instance;
|
||||||
@@ -24,20 +27,11 @@ public class InputManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PlayerInput playerInput;
|
private PlayerInput playerInput;
|
||||||
private InputAction touchPressAction;
|
private InputAction tapMoveAction;
|
||||||
private InputAction touchPositionAction;
|
private InputAction holdMoveAction;
|
||||||
|
private InputAction positionAction;
|
||||||
private ITouchInputConsumer defaultConsumer;
|
private ITouchInputConsumer defaultConsumer;
|
||||||
|
private bool isHoldActive;
|
||||||
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()
|
void Awake()
|
||||||
{
|
{
|
||||||
@@ -49,98 +43,112 @@ public class InputManager : MonoBehaviour
|
|||||||
Debug.LogError("[InputManager] InputManager requires a PlayerInput component attached to the same GameObject.");
|
Debug.LogError("[InputManager] InputManager requires a PlayerInput component attached to the same GameObject.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Find actions by name in the assigned action map
|
tapMoveAction = playerInput.actions.FindAction("TapMove", false);
|
||||||
touchPressAction = playerInput.actions.FindAction("TouchPress", false);
|
holdMoveAction = playerInput.actions.FindAction("HoldMove", false);
|
||||||
touchPositionAction = playerInput.actions.FindAction("TouchPosition", false);
|
positionAction = playerInput.actions.FindAction("TouchPosition", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnEnable()
|
void OnEnable()
|
||||||
{
|
{
|
||||||
if (touchPressAction != null)
|
if (tapMoveAction != null)
|
||||||
|
tapMoveAction.performed += OnTapMovePerformed;
|
||||||
|
if (holdMoveAction != null)
|
||||||
{
|
{
|
||||||
touchPressAction.started += OnTouchPressStarted;
|
holdMoveAction.performed += OnHoldMoveStarted;
|
||||||
touchPressAction.canceled += OnTouchPressCanceled;
|
holdMoveAction.canceled += OnHoldMoveCanceled;
|
||||||
}
|
}
|
||||||
if (touchPositionAction != null)
|
|
||||||
touchPositionAction.performed += OnTouchPositionPerformed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDisable()
|
void OnDisable()
|
||||||
{
|
{
|
||||||
if (touchPressAction != null)
|
if (tapMoveAction != null)
|
||||||
|
tapMoveAction.performed -= OnTapMovePerformed;
|
||||||
|
if (holdMoveAction != null)
|
||||||
{
|
{
|
||||||
touchPressAction.started -= OnTouchPressStarted;
|
holdMoveAction.performed -= OnHoldMoveStarted;
|
||||||
touchPressAction.canceled -= OnTouchPressCanceled;
|
holdMoveAction.canceled -= OnHoldMoveCanceled;
|
||||||
}
|
}
|
||||||
if (touchPositionAction != null)
|
|
||||||
touchPositionAction.performed -= OnTouchPositionPerformed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the default ITouchInputConsumer to receive input events.
|
||||||
|
/// </summary>
|
||||||
public void SetDefaultConsumer(ITouchInputConsumer consumer)
|
public void SetDefaultConsumer(ITouchInputConsumer consumer)
|
||||||
{
|
{
|
||||||
defaultConsumer = 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 = positionAction.ReadValue<Vector2>();
|
||||||
Vector2 screenPos = touchPositionAction.ReadValue<Vector2>();
|
|
||||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||||
lastFrameInteracted = TryDelegateToInteractable(worldPos2D);
|
Debug.Log($"[InputManager] TapMove performed at {worldPos2D}");
|
||||||
if (!lastFrameInteracted)
|
if (!TryDelegateToInteractable(worldPos2D))
|
||||||
{
|
{
|
||||||
pressStartPosition = screenPos;
|
Debug.Log("[InputManager] No interactable found, forwarding tap to default consumer");
|
||||||
pressStartTime = Time.time;
|
defaultConsumer?.OnTap(worldPos2D);
|
||||||
isPressed = true;
|
}
|
||||||
isDragging = false;
|
else
|
||||||
defaultConsumer?.OnHoldStart(worldPos2D);
|
{
|
||||||
|
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);
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||||
if (!lastFrameInteracted)
|
Debug.Log($"[InputManager] HoldMove started at {worldPos2D}");
|
||||||
{
|
defaultConsumer?.OnHoldStart(worldPos2D);
|
||||||
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)
|
/// <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()
|
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);
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
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)
|
private bool TryDelegateToInteractable(Vector2 worldPos)
|
||||||
{
|
{
|
||||||
// Raycast at the world position to find an Interactable
|
|
||||||
Collider2D hit = Physics2D.OverlapPoint(worldPos);
|
Collider2D hit = Physics2D.OverlapPoint(worldPos);
|
||||||
if (hit != null)
|
if (hit != null)
|
||||||
{
|
{
|
||||||
var interactable = hit.GetComponent<Interactable>();
|
var interactable = hit.GetComponent<ITouchInputConsumer>();
|
||||||
if (interactable != null)
|
if (interactable != null)
|
||||||
{
|
{
|
||||||
interactable.OnTap(worldPos);
|
interactable.OnTap(worldPos);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an interactable object that can respond to tap input events.
|
||||||
|
/// </summary>
|
||||||
public class Interactable : MonoBehaviour, ITouchInputConsumer
|
public class Interactable : MonoBehaviour, ITouchInputConsumer
|
||||||
{
|
{
|
||||||
public event Action StartedInteraction;
|
public event Action StartedInteraction;
|
||||||
@@ -13,21 +16,25 @@ public class Interactable : MonoBehaviour, ITouchInputConsumer
|
|||||||
stepBehaviour = GetComponent<ObjectiveStepBehaviour>();
|
stepBehaviour = GetComponent<ObjectiveStepBehaviour>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement new ITouchInputConsumer contract
|
/// <summary>
|
||||||
|
/// Handles tap input. Triggers interaction logic.
|
||||||
|
/// </summary>
|
||||||
public void OnTap(Vector2 worldPosition)
|
public void OnTap(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
Debug.Log($"[Interactable] OnTap at {worldPosition} on {gameObject.name}");
|
Debug.Log($"[Interactable] OnTap at {worldPosition} on {gameObject.name}");
|
||||||
StartedInteraction?.Invoke();
|
StartedInteraction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDragStart(Vector2 worldPosition) { }
|
/// <summary>
|
||||||
public void OnDrag(Vector2 worldPosition) { }
|
/// No hold behavior for interactables.
|
||||||
public void OnDragEnd(Vector2 worldPosition) { }
|
/// </summary>
|
||||||
public void OnHoldStart(Vector2 worldPosition) { /* No hold behavior for interactables */ }
|
public void OnHoldStart(Vector2 worldPosition) { }
|
||||||
public void OnHold(Vector2 worldPosition) { /* No hold behavior for interactables */ }
|
public void OnHoldMove(Vector2 worldPosition) { }
|
||||||
public void OnHoldEnd(Vector2 worldPosition) { /* No hold behavior for interactables */ }
|
public void OnHoldEnd(Vector2 worldPosition) { }
|
||||||
|
|
||||||
// Called when the follower arrives at this interactable
|
/// <summary>
|
||||||
|
/// Called when the follower arrives at this interactable.
|
||||||
|
/// </summary>
|
||||||
public bool OnFollowerArrived(FollowerController follower)
|
public bool OnFollowerArrived(FollowerController follower)
|
||||||
{
|
{
|
||||||
// Check if step is locked here
|
// Check if step is locked here
|
||||||
|
|||||||
@@ -2,67 +2,61 @@
|
|||||||
#if ENABLE_INPUT_SYSTEM
|
#if ENABLE_INPUT_SYSTEM
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
#endif
|
#endif
|
||||||
using Pathfinding; // Add this at the top
|
using Pathfinding;
|
||||||
|
|
||||||
// Basic touch/mouse movement controller suitable for top-down 2D or 3D overworld
|
/// <summary>
|
||||||
// Attach to the player GameObject. Works with or without Rigidbody/Rigidbody2D.
|
/// Handles player movement in response to tap and hold input events.
|
||||||
|
/// Supports both direct and pathfinding movement modes, and provides event/callbacks for arrival/cancellation.
|
||||||
|
/// </summary>
|
||||||
public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||||
{
|
{
|
||||||
Vector3 targetPosition;
|
// --- Movement State ---
|
||||||
bool hasTarget = false;
|
private Vector3 targetPosition;
|
||||||
|
private bool hasTarget = false;
|
||||||
|
private Vector3 directMoveVelocity = Vector3.zero;
|
||||||
|
private bool isHolding = false;
|
||||||
|
private Vector2 lastHoldPosition;
|
||||||
|
private Coroutine pathfindingDragCoroutine;
|
||||||
|
private float pathfindingDragUpdateInterval = 0.1f; // Interval in seconds
|
||||||
|
private bool pendingTap = false; // Track if OnHoldEnd is following a tap (legacy, see below)
|
||||||
|
|
||||||
Rigidbody rb3d;
|
// --- Unity/Component References ---
|
||||||
Rigidbody2D rb2d;
|
private AIPath aiPath;
|
||||||
AIPath aiPath; // Reference to AIPath
|
|
||||||
private Animator animator;
|
private Animator animator;
|
||||||
private Transform artTransform;
|
private Transform artTransform;
|
||||||
|
|
||||||
// For direct movement mode
|
// --- MoveToAndNotify State ---
|
||||||
private Vector3 directMoveVelocity = Vector3.zero;
|
|
||||||
|
|
||||||
public delegate void ArrivedAtTargetHandler();
|
public delegate void ArrivedAtTargetHandler();
|
||||||
public event ArrivedAtTargetHandler OnArrivedAtTarget;
|
public event ArrivedAtTargetHandler OnArrivedAtTarget;
|
||||||
public event System.Action OnMoveToCancelled;
|
public event System.Action OnMoveToCancelled;
|
||||||
private Coroutine moveToCoroutine;
|
private Coroutine moveToCoroutine;
|
||||||
private bool interruptMoveTo = false;
|
private bool interruptMoveTo = false;
|
||||||
|
|
||||||
private bool isDragging = false;
|
|
||||||
private Coroutine pathfindingDragCoroutine; // For pathfinding drag updates
|
|
||||||
private Vector2 lastDragPosition; // Store last drag position
|
|
||||||
private float pathfindingDragUpdateInterval = 0.1f; // Interval in seconds
|
|
||||||
private bool pendingTap = false; // Track if OnHoldEnd is following a tap
|
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
rb3d = GetComponent<Rigidbody>();
|
aiPath = GetComponent<AIPath>();
|
||||||
rb2d = GetComponent<Rigidbody2D>();
|
|
||||||
aiPath = GetComponent<AIPath>(); // Get AIPath component
|
|
||||||
// Find art prefab and animator
|
|
||||||
artTransform = transform.Find("CharacterArt");
|
artTransform = transform.Find("CharacterArt");
|
||||||
if (artTransform != null)
|
if (artTransform != null)
|
||||||
{
|
|
||||||
animator = artTransform.GetComponent<Animator>();
|
animator = artTransform.GetComponent<Animator>();
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
animator = GetComponentInChildren<Animator>();
|
||||||
animator = GetComponentInChildren<Animator>(); // fallback
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
// Initialize target to current position so object doesn't snap
|
|
||||||
targetPosition = transform.position;
|
targetPosition = transform.position;
|
||||||
hasTarget = false;
|
hasTarget = false;
|
||||||
// Register as default consumer in Start, after InputManager is likely initialized
|
|
||||||
InputManager.Instance?.SetDefaultConsumer(this);
|
InputManager.Instance?.SetDefaultConsumer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement new ITouchInputConsumer contract
|
/// <summary>
|
||||||
|
/// Handles tap input. Always uses pathfinding to move to the tapped location.
|
||||||
|
/// Cancels any in-progress MoveToAndNotify.
|
||||||
|
/// </summary>
|
||||||
public void OnTap(Vector2 worldPosition)
|
public void OnTap(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
|
InterruptMoveTo();
|
||||||
Debug.Log($"[PlayerTouchController] OnTap at {worldPosition}");
|
Debug.Log($"[PlayerTouchController] OnTap at {worldPosition}");
|
||||||
pendingTap = true;
|
|
||||||
if (aiPath != null)
|
if (aiPath != null)
|
||||||
{
|
{
|
||||||
aiPath.enabled = true;
|
aiPath.enabled = true;
|
||||||
@@ -70,16 +64,20 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
aiPath.isStopped = false;
|
aiPath.isStopped = false;
|
||||||
SetTargetPosition(worldPosition);
|
SetTargetPosition(worldPosition);
|
||||||
directMoveVelocity = Vector3.zero;
|
directMoveVelocity = Vector3.zero;
|
||||||
isDragging = false;
|
isHolding = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Hold-based tracking ---
|
/// <summary>
|
||||||
|
/// Handles the start of a hold input. Begins tracking the finger and uses the correct movement mode.
|
||||||
|
/// Cancels any in-progress MoveToAndNotify.
|
||||||
|
/// </summary>
|
||||||
public void OnHoldStart(Vector2 worldPosition)
|
public void OnHoldStart(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
pendingTap = false;
|
InterruptMoveTo();
|
||||||
lastDragPosition = worldPosition;
|
Debug.Log($"[PlayerTouchController] OnHoldStart at {worldPosition}");
|
||||||
isDragging = true;
|
lastHoldPosition = worldPosition;
|
||||||
|
isHolding = true;
|
||||||
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding && aiPath != null)
|
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding && aiPath != null)
|
||||||
{
|
{
|
||||||
aiPath.enabled = true;
|
aiPath.enabled = true;
|
||||||
@@ -93,10 +91,13 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnHold(Vector2 worldPosition)
|
/// <summary>
|
||||||
|
/// Handles hold move input. Updates the target position for direct or pathfinding movement.
|
||||||
|
/// </summary>
|
||||||
|
public void OnHoldMove(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
if (!isDragging) return;
|
if (!isHolding) return;
|
||||||
lastDragPosition = worldPosition;
|
lastHoldPosition = worldPosition;
|
||||||
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
||||||
{
|
{
|
||||||
if (aiPath != null && aiPath.enabled) aiPath.enabled = false;
|
if (aiPath != null && aiPath.enabled) aiPath.enabled = false;
|
||||||
@@ -105,9 +106,13 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
// If pathfinding, coroutine will update destination
|
// If pathfinding, coroutine will update destination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the end of a hold input. Stops tracking and disables movement as needed.
|
||||||
|
/// </summary>
|
||||||
public void OnHoldEnd(Vector2 worldPosition)
|
public void OnHoldEnd(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
isDragging = false;
|
Debug.Log($"[PlayerTouchController] OnHoldEnd at {worldPosition}");
|
||||||
|
isHolding = false;
|
||||||
directMoveVelocity = Vector3.zero;
|
directMoveVelocity = Vector3.zero;
|
||||||
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding)
|
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding)
|
||||||
{
|
{
|
||||||
@@ -117,22 +122,17 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
pathfindingDragCoroutine = null;
|
pathfindingDragCoroutine = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Only disable aiPath in direct mode if this was a hold/drag, not a tap
|
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
||||||
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct && !pendingTap)
|
|
||||||
{
|
{
|
||||||
aiPath.enabled = false;
|
aiPath.enabled = false;
|
||||||
}
|
}
|
||||||
pendingTap = false; // Reset after handling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Drag methods are now no-ops for movement tracking ---
|
/// <summary>
|
||||||
public void OnDragStart(Vector2 worldPosition) { }
|
/// Sets the target position for pathfinding movement.
|
||||||
public void OnDrag(Vector2 worldPosition) { }
|
/// </summary>
|
||||||
public void OnDragEnd(Vector2 worldPosition) { }
|
private void SetTargetPosition(Vector2 worldPosition)
|
||||||
|
|
||||||
void SetTargetPosition(Vector2 worldPosition)
|
|
||||||
{
|
{
|
||||||
Debug.Log($"[PlayerTouchController] SetTargetPosition: worldPosition={worldPosition}");
|
|
||||||
targetPosition = new Vector3(worldPosition.x, worldPosition.y, transform.position.z);
|
targetPosition = new Vector3(worldPosition.x, worldPosition.y, transform.position.z);
|
||||||
hasTarget = true;
|
hasTarget = true;
|
||||||
if (aiPath != null)
|
if (aiPath != null)
|
||||||
@@ -141,15 +141,13 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
aiPath.maxSpeed = GameManager.Instance.MoveSpeed;
|
aiPath.maxSpeed = GameManager.Instance.MoveSpeed;
|
||||||
aiPath.canMove = true;
|
aiPath.canMove = true;
|
||||||
aiPath.isStopped = false;
|
aiPath.isStopped = false;
|
||||||
Debug.Log($"[PlayerTouchController] AIPath destination set to {targetPosition}, canMove={aiPath.canMove}, isStopped={aiPath.isStopped}, enabled={aiPath.enabled}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogWarning("AIPath component not found, falling back to direct movement");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveDirectlyTo(Vector2 worldPosition)
|
/// <summary>
|
||||||
|
/// Moves the player directly towards the specified world position.
|
||||||
|
/// </summary>
|
||||||
|
private void MoveDirectlyTo(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
if (aiPath == null) return;
|
if (aiPath == null) return;
|
||||||
Vector3 current = transform.position;
|
Vector3 current = transform.position;
|
||||||
@@ -158,14 +156,10 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
Vector3 direction = toTarget.normalized;
|
Vector3 direction = toTarget.normalized;
|
||||||
float maxSpeed = aiPath.maxSpeed;
|
float maxSpeed = aiPath.maxSpeed;
|
||||||
float acceleration = aiPath.maxAcceleration;
|
float acceleration = aiPath.maxAcceleration;
|
||||||
// Accelerate velocity toward target direction
|
|
||||||
directMoveVelocity = Vector3.MoveTowards(directMoveVelocity, direction * maxSpeed, acceleration * Time.deltaTime);
|
directMoveVelocity = Vector3.MoveTowards(directMoveVelocity, direction * maxSpeed, acceleration * Time.deltaTime);
|
||||||
// Clamp velocity to max speed
|
|
||||||
if (directMoveVelocity.magnitude > maxSpeed)
|
if (directMoveVelocity.magnitude > maxSpeed)
|
||||||
directMoveVelocity = directMoveVelocity.normalized * maxSpeed;
|
directMoveVelocity = directMoveVelocity.normalized * maxSpeed;
|
||||||
// Move the player
|
|
||||||
Vector3 move = directMoveVelocity * Time.deltaTime;
|
Vector3 move = directMoveVelocity * Time.deltaTime;
|
||||||
// Don't overshoot the target
|
|
||||||
if (move.magnitude > toTarget.magnitude)
|
if (move.magnitude > toTarget.magnitude)
|
||||||
move = toTarget;
|
move = toTarget;
|
||||||
transform.position += move;
|
transform.position += move;
|
||||||
@@ -176,7 +170,7 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
if (animator != null && aiPath != null)
|
if (animator != null && aiPath != null)
|
||||||
{
|
{
|
||||||
float normalizedSpeed = 0f;
|
float normalizedSpeed = 0f;
|
||||||
if (isDragging)
|
if (isHolding && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
||||||
{
|
{
|
||||||
normalizedSpeed = directMoveVelocity.magnitude / aiPath.maxSpeed;
|
normalizedSpeed = directMoveVelocity.magnitude / aiPath.maxSpeed;
|
||||||
}
|
}
|
||||||
@@ -188,11 +182,25 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove FixedUpdate and MoveTowardsTarget, as AIPath handles movement
|
/// <summary>
|
||||||
|
/// Coroutine for updating the AIPath destination during pathfinding hold movement.
|
||||||
|
/// </summary>
|
||||||
|
private System.Collections.IEnumerator PathfindingDragUpdateCoroutine()
|
||||||
|
{
|
||||||
|
while (isHolding && aiPath != null)
|
||||||
|
{
|
||||||
|
aiPath.destination = new Vector3(lastHoldPosition.x, lastHoldPosition.y, transform.position.z);
|
||||||
|
yield return new WaitForSeconds(pathfindingDragUpdateInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move to a target position, notify when arrived
|
/// <summary>
|
||||||
|
/// Moves the player to a specific target position and notifies via events when arrived or cancelled.
|
||||||
|
/// This is used by systems like Pickup.cs to orchestrate movement.
|
||||||
|
/// </summary>
|
||||||
public void MoveToAndNotify(Vector3 target)
|
public void MoveToAndNotify(Vector3 target)
|
||||||
{
|
{
|
||||||
|
// Cancel any previous move-to coroutine
|
||||||
if (moveToCoroutine != null)
|
if (moveToCoroutine != null)
|
||||||
{
|
{
|
||||||
StopCoroutine(moveToCoroutine);
|
StopCoroutine(moveToCoroutine);
|
||||||
@@ -201,15 +209,21 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
moveToCoroutine = StartCoroutine(MoveToTargetCoroutine(target));
|
moveToCoroutine = StartCoroutine(MoveToTargetCoroutine(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels any in-progress MoveToAndNotify operation and fires the cancellation event.
|
||||||
|
/// </summary>
|
||||||
public void InterruptMoveTo()
|
public void InterruptMoveTo()
|
||||||
{
|
{
|
||||||
interruptMoveTo = true;
|
interruptMoveTo = true;
|
||||||
isDragging = false;
|
isHolding = false;
|
||||||
directMoveVelocity = Vector3.zero;
|
directMoveVelocity = Vector3.zero;
|
||||||
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct && aiPath != null) aiPath.enabled = false;
|
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct && aiPath != null) aiPath.enabled = false;
|
||||||
OnMoveToCancelled?.Invoke();
|
OnMoveToCancelled?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Coroutine for moving the player to a target position and firing arrival/cancel events.
|
||||||
|
/// </summary>
|
||||||
private System.Collections.IEnumerator MoveToTargetCoroutine(Vector3 target)
|
private System.Collections.IEnumerator MoveToTargetCoroutine(Vector3 target)
|
||||||
{
|
{
|
||||||
hasTarget = true;
|
hasTarget = true;
|
||||||
@@ -238,15 +252,7 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private System.Collections.IEnumerator PathfindingDragUpdateCoroutine()
|
// --- Legacy/Unused fields ---
|
||||||
{
|
// pendingTap: This field is not used in the current input flow. If you are not using tap/hold distinction logic elsewhere, consider removing it.
|
||||||
Debug.Log("[PlayerTouchController] PathfindingDragUpdateCoroutine started");
|
// If you want to remove it, please confirm and I will do so.
|
||||||
while (isDragging && aiPath != null)
|
|
||||||
{
|
|
||||||
aiPath.destination = new Vector3(lastDragPosition.x, lastDragPosition.y, transform.position.z);
|
|
||||||
Debug.Log($"[PlayerTouchController] Updating aiPath.destination to {aiPath.destination}");
|
|
||||||
yield return new WaitForSeconds(pathfindingDragUpdateInterval);
|
|
||||||
}
|
|
||||||
Debug.Log("[PlayerTouchController] PathfindingDragUpdateCoroutine stopped");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user