Big script cleanup. Remove the examples from Ropes' external package

This commit is contained in:
Michal Pikulski
2025-09-06 21:01:54 +02:00
parent 045bd7966e
commit d3c6b838b4
134 changed files with 719 additions and 26298 deletions

View File

@@ -0,0 +1,9 @@
using UnityEngine;
public interface ITouchInputConsumer
{
void OnTap(Vector2 position);
void OnHoldStart(Vector2 position);
void OnHoldMove(Vector2 position);
void OnHoldEnd(Vector2 position);
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 84aa53c8b48d4c46a2d10611f4987550
timeCreated: 1756729216

View File

@@ -0,0 +1,160 @@
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;
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 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;
}
}
/// <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.
/// </summary>
private bool TryDelegateToInteractable(Vector2 worldPos)
{
Collider2D hit = Physics2D.OverlapPoint(worldPos);
if (hit != null)
{
var interactable = hit.GetComponent<ITouchInputConsumer>();
if (interactable != null)
{
interactable.OnTap(worldPos);
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eaca18daca6d4bfd8026afb6dd5f8c61
timeCreated: 1756729225

View File

@@ -0,0 +1,261 @@
using UnityEngine;
using Pathfinding;
namespace Input
{
/// <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
{
// --- Movement State ---
private Vector3 targetPosition;
private Vector3 directMoveVelocity; // default is Vector3.zero
private bool isHolding;
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)
// --- Unity/Component References ---
private AIPath aiPath;
// Note: String-based property lookup is flagged as inefficient, but is common in Unity for dynamic children.
private Animator animator;
private Transform artTransform;
// --- MoveToAndNotify State ---
public delegate void ArrivedAtTargetHandler();
private Coroutine moveToCoroutine;
public event ArrivedAtTargetHandler OnArrivedAtTarget;
public event System.Action OnMoveToCancelled;
private bool interruptMoveTo;
void Awake()
{
aiPath = GetComponent<AIPath>();
artTransform = transform.Find("CharacterArt");
if (artTransform != null)
animator = artTransform.GetComponent<Animator>();
else
animator = GetComponentInChildren<Animator>();
}
void Start()
{
InputManager.Instance?.SetDefaultConsumer(this);
}
/// <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}");
if (aiPath != null)
{
aiPath.enabled = true;
aiPath.canMove = true;
aiPath.isStopped = false;
SetTargetPosition(worldPosition);
directMoveVelocity = Vector3.zero;
isHolding = false;
}
}
/// <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)
{
InterruptMoveTo();
Debug.Log($"[PlayerTouchController] OnHoldStart at {worldPosition}");
lastHoldPosition = worldPosition;
isHolding = true;
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Pathfinding &&
aiPath != null)
{
aiPath.enabled = true;
if (pathfindingDragCoroutine != null) StopCoroutine(pathfindingDragCoroutine);
pathfindingDragCoroutine = StartCoroutine(PathfindingDragUpdateCoroutine());
}
else // Direct movement
{
if (aiPath != null) aiPath.enabled = false;
directMoveVelocity = Vector3.zero;
}
}
/// <summary>
/// Handles hold move input. Updates the target position for direct or pathfinding movement.
/// </summary>
public void OnHoldMove(Vector2 worldPosition)
{
if (!isHolding) return;
lastHoldPosition = worldPosition;
if (GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
{
if (aiPath != null && aiPath.enabled) aiPath.enabled = false;
MoveDirectlyTo(worldPosition);
}
// 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)
{
Debug.Log($"[PlayerTouchController] OnHoldEnd at {worldPosition}");
isHolding = false;
directMoveVelocity = Vector3.zero;
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode ==
GameSettings.HoldMovementMode.Pathfinding)
{
if (pathfindingDragCoroutine != null)
{
StopCoroutine(pathfindingDragCoroutine);
pathfindingDragCoroutine = null;
}
}
if (aiPath != null && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
{
aiPath.enabled = false;
}
}
/// <summary>
/// Sets the target position for pathfinding movement.
/// </summary>
private void SetTargetPosition(Vector2 worldPosition)
{
if (aiPath != null)
{
aiPath.destination = worldPosition;
aiPath.maxSpeed = GameManager.Instance.MoveSpeed;
aiPath.canMove = true;
aiPath.isStopped = false;
}
}
/// <summary>
/// Moves the player directly towards the specified world position.
/// </summary>
private void MoveDirectlyTo(Vector2 worldPosition)
{
if (aiPath == null) return;
Vector3 current = transform.position;
Vector3 target = new Vector3(worldPosition.x, worldPosition.y, current.z);
Vector3 toTarget = (target - current);
Vector3 direction = toTarget.normalized;
float maxSpeed = aiPath.maxSpeed;
float acceleration = aiPath.maxAcceleration;
directMoveVelocity =
Vector3.MoveTowards(directMoveVelocity, direction * maxSpeed, acceleration * Time.deltaTime);
if (directMoveVelocity.magnitude > maxSpeed)
directMoveVelocity = directMoveVelocity.normalized * maxSpeed;
Vector3 move = directMoveVelocity * Time.deltaTime;
if (move.magnitude > toTarget.magnitude)
move = toTarget;
transform.position += move;
}
void Update()
{
if (animator != null && aiPath != null)
{
float normalizedSpeed = 0f;
if (isHolding && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
{
normalizedSpeed = directMoveVelocity.magnitude / aiPath.maxSpeed;
}
else if (aiPath.enabled)
{
normalizedSpeed = aiPath.velocity.magnitude / aiPath.maxSpeed;
}
animator.SetFloat("Speed", Mathf.Clamp01(normalizedSpeed));
}
}
/// <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);
}
}
/// <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);
}
interruptMoveTo = false;
moveToCoroutine = StartCoroutine(MoveToTargetCoroutine(target));
}
/// <summary>
/// Cancels any in-progress MoveToAndNotify operation and fires the cancellation event.
/// </summary>
public void InterruptMoveTo()
{
interruptMoveTo = true;
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)
{
if (aiPath != null)
{
aiPath.destination = target;
aiPath.maxSpeed = GameManager.Instance.MoveSpeed;
}
while (!interruptMoveTo)
{
Vector2 current2D = new Vector2(transform.position.x, transform.position.y);
Vector2 target2D = new Vector2(target.x, target.y);
float dist = Vector2.Distance(current2D, target2D);
if (dist <= GameManager.Instance.StopDistance + 0.2f)
{
break;
}
yield return null;
}
moveToCoroutine = null;
if (!interruptMoveTo)
{
OnArrivedAtTarget?.Invoke();
}
}
// --- 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.
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2ecb5bf6d8f447368687404e1b24278d
timeCreated: 1756719186