This commit is contained in:
2025-09-09 14:23:57 +02:00
7 changed files with 296 additions and 76 deletions

View File

@@ -16,6 +16,9 @@ namespace Input
private Vector2 lastHoldPosition;
private Coroutine pathfindingDragCoroutine;
private float pathfindingDragUpdateInterval = 0.1f; // Interval in seconds
[Header("Collision Simulation")]
public LayerMask obstacleMask;
public float colliderRadius = 0.5f;
// --- Unity/Component References ---
private AIPath aiPath;
@@ -23,6 +26,7 @@ namespace Input
// Note: String-based property lookup is flagged as inefficient, but is common in Unity for dynamic children.
private Animator animator;
private Transform artTransform;
private SpriteRenderer spriteRenderer;
// --- MoveToAndNotify State ---
public delegate void ArrivedAtTargetHandler();
@@ -39,6 +43,11 @@ namespace Input
animator = artTransform.GetComponent<Animator>();
else
animator = GetComponentInChildren<Animator>();
// Cache SpriteRenderer for flipping
if (artTransform != null)
spriteRenderer = artTransform.GetComponent<SpriteRenderer>();
if (spriteRenderer == null)
spriteRenderer = GetComponentInChildren<SpriteRenderer>();
}
void Start()
@@ -147,21 +156,64 @@ namespace Input
/// </summary>
private void MoveDirectlyTo(Vector2 worldPosition)
{
if (aiPath == null) return;
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);
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;
}
// --- Collision simulation ---
Vector3 adjustedVelocity = AdjustVelocityForObstacles(current, directMoveVelocity);
Vector3 adjustedMove = adjustedVelocity * Time.deltaTime;
if (adjustedMove.magnitude > toTarget.magnitude)
{
adjustedMove = toTarget;
}
transform.position += adjustedMove;
}
/// <summary>
/// Simulates collision with obstacles by raycasting in the direction of velocity and projecting the velocity if a collision is detected.
/// </summary>
/// <param name="position">Player's current position.</param>
/// <param name="velocity">Intended velocity for this frame.</param>
/// <returns>Adjusted velocity after collision simulation.</returns>
private Vector3 AdjustVelocityForObstacles(Vector3 position, Vector3 velocity)
{
if (velocity.sqrMagnitude < 0.0001f)
return velocity;
float moveDistance = velocity.magnitude * Time.deltaTime;
Vector2 origin = new Vector2(position.x, position.y);
Vector2 dir = velocity.normalized;
float rayLength = colliderRadius + moveDistance;
RaycastHit2D hit = Physics2D.Raycast(origin, dir, rayLength, obstacleMask);
Debug.DrawLine(origin, origin + dir * rayLength, Color.red, 0.1f);
if (hit.collider != null)
{
// Draw normal and tangent for debug
Debug.DrawLine(hit.point, hit.point + hit.normal, Color.green, 0.2f);
Vector2 tangent = new Vector2(-hit.normal.y, hit.normal.x);
Debug.DrawLine(hit.point, hit.point + tangent, Color.blue, 0.2f);
// Project velocity onto tangent to simulate sliding
float slideAmount = Vector2.Dot(velocity, tangent);
Vector3 slideVelocity = tangent * slideAmount;
return slideVelocity;
}
return velocity;
}
void Update()
@@ -169,16 +221,30 @@ namespace Input
if (animator != null && aiPath != null)
{
float normalizedSpeed = 0f;
Vector3 velocity = Vector3.zero;
if (isHolding && GameManager.Instance.DefaultHoldMovementMode == GameSettings.HoldMovementMode.Direct)
{
normalizedSpeed = directMoveVelocity.magnitude / aiPath.maxSpeed;
velocity = directMoveVelocity;
}
else if (aiPath.enabled)
{
normalizedSpeed = aiPath.velocity.magnitude / aiPath.maxSpeed;
velocity = aiPath.velocity;
}
animator.SetFloat("Speed", Mathf.Clamp01(normalizedSpeed));
SetSpriteFlip(velocity);
}
}
private void SetSpriteFlip(Vector3 velocity)
{
if (spriteRenderer != null && velocity.sqrMagnitude > 0.001f)
{
if (velocity.x > 0.01f)
spriteRenderer.flipX = false;
else if (velocity.x < -0.01f)
spriteRenderer.flipX = true;
}
}

View File

@@ -68,4 +68,9 @@ public class Interactable : MonoBehaviour, ITouchInputConsumer
}
return anySuccess;
}
public void CompleteInteraction(bool success)
{
InteractionComplete?.Invoke(success);
}
}

View File

@@ -0,0 +1,35 @@
using UnityEngine;
using System;
/// <summary>
/// MonoBehaviour that immediately completes an interaction when started.
/// </summary>
public class OneClickInteraction : MonoBehaviour
{
private Interactable interactable;
void Awake()
{
interactable = GetComponent<Interactable>();
if (interactable != null)
{
interactable.StartedInteraction += OnStartedInteraction;
}
}
void OnDestroy()
{
if (interactable != null)
{
interactable.StartedInteraction -= OnStartedInteraction;
}
}
private void OnStartedInteraction()
{
if (interactable != null)
{
interactable.CompleteInteraction(true);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 833a4ccef651449e973e623d9107bef5
timeCreated: 1757417586

View File

@@ -29,6 +29,7 @@ public class FollowerController : MonoBehaviour
private float _currentSpeed = 0f;
private Animator _animator;
private Transform _artTransform;
private SpriteRenderer spriteRenderer;
private PickupItemData _currentlyHeldItem;
public PickupItemData CurrentlyHeldItem => _currentlyHeldItem;
@@ -83,10 +84,12 @@ public class FollowerController : MonoBehaviour
if (_artTransform != null)
{
_animator = _artTransform.GetComponent<Animator>();
spriteRenderer = _artTransform.GetComponent<SpriteRenderer>();
}
else
{
_animator = GetComponentInChildren<Animator>(); // fallback
spriteRenderer = GetComponentInChildren<SpriteRenderer>();
}
}
@@ -181,6 +184,14 @@ public class FollowerController : MonoBehaviour
_currentSpeed = Mathf.Max(_currentSpeed, minSpeed);
}
Vector3 dir = (_targetPoint - transform.position).normalized;
// Sprite flipping based on movement direction
if (spriteRenderer != null && dir.sqrMagnitude > 0.001f)
{
if (dir.x > 0.01f)
spriteRenderer.flipX = false;
else if (dir.x < -0.01f)
spriteRenderer.flipX = true;
}
transform.position += dir * _currentSpeed * Time.deltaTime;
}
else
@@ -202,6 +213,14 @@ public class FollowerController : MonoBehaviour
else if (_aiPath != null)
{
normalizedSpeed = _aiPath.velocity.magnitude / _followerMaxSpeed;
// Sprite flipping for pathfinding mode
if (spriteRenderer != null && _aiPath.velocity.sqrMagnitude > 0.001f)
{
if (_aiPath.velocity.x > 0.01f)
spriteRenderer.flipX = false;
else if (_aiPath.velocity.x < -0.01f)
spriteRenderer.flipX = true;
}
}
_animator.SetFloat("Speed", Mathf.Clamp01(normalizedSpeed));
}
@@ -438,3 +457,5 @@ public class FollowerController : MonoBehaviour
}
}
}