Big Input refactor
This commit is contained in:
@@ -2,67 +2,61 @@
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
using Pathfinding; // Add this at the top
|
||||
using Pathfinding;
|
||||
|
||||
// Basic touch/mouse movement controller suitable for top-down 2D or 3D overworld
|
||||
// Attach to the player GameObject. Works with or without Rigidbody/Rigidbody2D.
|
||||
/// <summary>
|
||||
/// 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
|
||||
{
|
||||
Vector3 targetPosition;
|
||||
bool hasTarget = false;
|
||||
// --- Movement State ---
|
||||
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;
|
||||
Rigidbody2D rb2d;
|
||||
AIPath aiPath; // Reference to AIPath
|
||||
// --- Unity/Component References ---
|
||||
private AIPath aiPath;
|
||||
private Animator animator;
|
||||
private Transform artTransform;
|
||||
|
||||
// For direct movement mode
|
||||
private Vector3 directMoveVelocity = Vector3.zero;
|
||||
|
||||
// --- MoveToAndNotify State ---
|
||||
public delegate void ArrivedAtTargetHandler();
|
||||
public event ArrivedAtTargetHandler OnArrivedAtTarget;
|
||||
public event System.Action OnMoveToCancelled;
|
||||
private Coroutine moveToCoroutine;
|
||||
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()
|
||||
{
|
||||
rb3d = GetComponent<Rigidbody>();
|
||||
rb2d = GetComponent<Rigidbody2D>();
|
||||
aiPath = GetComponent<AIPath>(); // Get AIPath component
|
||||
// Find art prefab and animator
|
||||
aiPath = GetComponent<AIPath>();
|
||||
artTransform = transform.Find("CharacterArt");
|
||||
if (artTransform != null)
|
||||
{
|
||||
animator = artTransform.GetComponent<Animator>();
|
||||
}
|
||||
else
|
||||
{
|
||||
animator = GetComponentInChildren<Animator>(); // fallback
|
||||
}
|
||||
animator = GetComponentInChildren<Animator>();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Initialize target to current position so object doesn't snap
|
||||
targetPosition = transform.position;
|
||||
hasTarget = false;
|
||||
// Register as default consumer in Start, after InputManager is likely initialized
|
||||
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)
|
||||
{
|
||||
InterruptMoveTo();
|
||||
Debug.Log($"[PlayerTouchController] OnTap at {worldPosition}");
|
||||
pendingTap = true;
|
||||
if (aiPath != null)
|
||||
{
|
||||
aiPath.enabled = true;
|
||||
@@ -70,16 +64,20 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
aiPath.isStopped = false;
|
||||
SetTargetPosition(worldPosition);
|
||||
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)
|
||||
{
|
||||
pendingTap = false;
|
||||
lastDragPosition = worldPosition;
|
||||
isDragging = true;
|
||||
InterruptMoveTo();
|
||||
Debug.Log($"[PlayerTouchController] OnHoldStart at {worldPosition}");
|
||||
lastHoldPosition = worldPosition;
|
||||
isHolding = true;
|
||||
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding && aiPath != null)
|
||||
{
|
||||
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;
|
||||
lastDragPosition = worldPosition;
|
||||
if (!isHolding) return;
|
||||
lastHoldPosition = worldPosition;
|
||||
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
||||
{
|
||||
if (aiPath != null && aiPath.enabled) aiPath.enabled = false;
|
||||
@@ -105,9 +106,13 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
// 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)
|
||||
{
|
||||
isDragging = false;
|
||||
Debug.Log($"[PlayerTouchController] OnHoldEnd at {worldPosition}");
|
||||
isHolding = false;
|
||||
directMoveVelocity = Vector3.zero;
|
||||
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding)
|
||||
{
|
||||
@@ -117,22 +122,17 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
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 && !pendingTap)
|
||||
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
||||
{
|
||||
aiPath.enabled = false;
|
||||
}
|
||||
pendingTap = false; // Reset after handling
|
||||
}
|
||||
|
||||
// --- Drag methods are now no-ops for movement tracking ---
|
||||
public void OnDragStart(Vector2 worldPosition) { }
|
||||
public void OnDrag(Vector2 worldPosition) { }
|
||||
public void OnDragEnd(Vector2 worldPosition) { }
|
||||
|
||||
void SetTargetPosition(Vector2 worldPosition)
|
||||
/// <summary>
|
||||
/// Sets the target position for pathfinding movement.
|
||||
/// </summary>
|
||||
private void SetTargetPosition(Vector2 worldPosition)
|
||||
{
|
||||
Debug.Log($"[PlayerTouchController] SetTargetPosition: worldPosition={worldPosition}");
|
||||
targetPosition = new Vector3(worldPosition.x, worldPosition.y, transform.position.z);
|
||||
hasTarget = true;
|
||||
if (aiPath != null)
|
||||
@@ -141,15 +141,13 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
aiPath.maxSpeed = GameManager.Instance.MoveSpeed;
|
||||
aiPath.canMove = true;
|
||||
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;
|
||||
Vector3 current = transform.position;
|
||||
@@ -158,14 +156,10 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
Vector3 direction = toTarget.normalized;
|
||||
float maxSpeed = aiPath.maxSpeed;
|
||||
float acceleration = aiPath.maxAcceleration;
|
||||
// Accelerate velocity toward target direction
|
||||
directMoveVelocity = Vector3.MoveTowards(directMoveVelocity, direction * maxSpeed, acceleration * Time.deltaTime);
|
||||
// Clamp velocity to max speed
|
||||
if (directMoveVelocity.magnitude > maxSpeed)
|
||||
directMoveVelocity = directMoveVelocity.normalized * maxSpeed;
|
||||
// Move the player
|
||||
Vector3 move = directMoveVelocity * Time.deltaTime;
|
||||
// Don't overshoot the target
|
||||
if (move.magnitude > toTarget.magnitude)
|
||||
move = toTarget;
|
||||
transform.position += move;
|
||||
@@ -176,7 +170,7 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
if (animator != null && aiPath != null)
|
||||
{
|
||||
float normalizedSpeed = 0f;
|
||||
if (isDragging)
|
||||
if (isHolding && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Cancel any previous move-to coroutine
|
||||
if (moveToCoroutine != null)
|
||||
{
|
||||
StopCoroutine(moveToCoroutine);
|
||||
@@ -201,15 +209,21 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
moveToCoroutine = StartCoroutine(MoveToTargetCoroutine(target));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels any in-progress MoveToAndNotify operation and fires the cancellation event.
|
||||
/// </summary>
|
||||
public void InterruptMoveTo()
|
||||
{
|
||||
interruptMoveTo = true;
|
||||
isDragging = false;
|
||||
isHolding = false;
|
||||
directMoveVelocity = Vector3.zero;
|
||||
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct && aiPath != null) aiPath.enabled = false;
|
||||
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)
|
||||
{
|
||||
hasTarget = true;
|
||||
@@ -238,15 +252,7 @@ public class PlayerTouchController : MonoBehaviour, ITouchInputConsumer
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator PathfindingDragUpdateCoroutine()
|
||||
{
|
||||
Debug.Log("[PlayerTouchController] PathfindingDragUpdateCoroutine started");
|
||||
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");
|
||||
}
|
||||
// --- 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.
|
||||
// If you want to remove it, please confirm and I will do so.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user