Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / NPC / Pathfinding / Agent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class Agent : MonoBehaviour
{
    public float speed = 20f;
    public float turnSpeed = 3f;
    public float turnDistance = 2f;
    public float stoppingDistance = 10f;

    public const float pathRequestUpdateTime = 0.2f;
    public const float pathUpdateMoveThreshold = 0.5f;

    private Path path;
    private Vector2 targetDestination;

    [SerializeField] private bool stop;
    [SerializeField] private Vector2 agentDestination;
    [SerializeField] private Vector2 moveDirection;
    [SerializeField] private Vector2 velocity;

    [SerializeField] private float rDist;
    [SerializeField] private Grid grid;

    public Vector2 TargetDestination => targetDestination;
    public Path Path => path;
    public Grid Grid => grid;
    public bool agentStop { get => stop; set => stop = value; }
    public Vector2 agentVelocity => velocity;

    public float remainingDistance => CalculateRemainingDistance();

    public Vector2 destination
    {
        get => agentDestination; set => agentDestination = value;
    }

    private void Awake()
    {
        agentDestination = Vector2.zero;
        velocity = Vector2.zero;
        stop = agentVelocity.normalized.magnitude == 0 ? true : false;
    }

    private void Start()
    {
        StartCoroutine(UpdatePath());
    }

    private void OnEnable()
    {
        agentDestination = Vector2.zero;
        velocity = Vector2.zero;
        moveDirection = Vector2.zero;
        path = null;
        stop = agentVelocity.normalized.magnitude == 0 ? true : false;
        StartCoroutine(UpdatePath());
    }

    private void Update()
    {
        rDist = CalculateRemainingDistance();
    }

    public void OnPathFound(Vector2[] waypoints, bool pathSuccessful)
    {
        if (pathSuccessful)
        {
            Debug.Log($"WAYPOINTS: {waypoints.Length}");
            path = new Path(waypoints, transform.position, turnDistance, stoppingDistance);
            StopCoroutine("FollowPath");
            StartCoroutine("FollowPath");
        }
    }

    private IEnumerator UpdatePath()
    {
        while (true)
        {
            if (agentDestination != Vector2.zero)
            {
                if (Time.timeSinceLevelLoad < 0.3f)
                {
                    yield return new WaitForSeconds(.3f);
                }
                PathRequestManager.RequestPath(new PathRequest(transform.position, agentDestination, OnPathFound));

                float sqrMoveThreshold = pathUpdateMoveThreshold * pathUpdateMoveThreshold;
                Vector2 destinationOldPos = agentDestination;

                while (agentDestination != Vector2.zero)
                {
                    yield return new WaitForSeconds(pathRequestUpdateTime);

                    if ((agentDestination - destinationOldPos).sqrMagnitude > sqrMoveThreshold)
                    {
                        PathRequestManager.RequestPath(new PathRequest(transform.position, agentDestination, OnPathFound));
                        destinationOldPos = agentDestination;
                    }
                }
            }

            // Wait for a short time before checking the destination again
            yield return new WaitForSeconds(0.2f);
        }
    }

    private float CalculateRemainingDistance()
    {
        if (path == null || path.lookPoints == null || path.lookPoints.Length == 0)
        {
            return 0f;
        }

        float distance = 0f;
        Vector2 currentPosition = transform.position;

        // Calculate distance from current position to the next path point
        if (path.turnBoundaries.Length > 0)
        {
            distance = Vector2.Distance(currentPosition, path.lookPoints[path.turnBoundaries.Length - 1]);
        }

        // Add distances between all remaining path points
        for (int i = path.turnBoundaries.Length - 1; i < path.lookPoints.Length - 1; i++)
        {
            distance += Vector2.Distance(path.lookPoints[i], path.lookPoints[i + 1]);
        }

        return distance;
    }


    private IEnumerator FollowPath()
    {
        bool followingPath = true;
        int pathIndex = 0;
        Vector2 previousPosition = transform.position; // Store the previous position
        float speedPercent = 1f;

        while (followingPath)
        {
            if (stop)
            {
                yield return null;
                continue;
            }

            Vector2 position = new Vector2(transform.position.x, transform.position.y);

            while (path.turnBoundaries[pathIndex].HasCrossedLine(position))
            {
                if (pathIndex == path.finishLineIndex)
                {
                    followingPath = false;
                    break;
                }
                else
                {
                    pathIndex++;
                }
            }

            if (followingPath)
            {
                CalculateRemainingDistance();

                if (pathIndex >= path.slowDownIndex && stoppingDistance > 0)
                {
                    speedPercent = Mathf.Clamp01(path.turnBoundaries[path.finishLineIndex].DistanceFromPoint(position) / stoppingDistance);

                    if (speedPercent < 0.01f)
                    {
                        followingPath = false;
                    }
                }

                Vector2 targetDirection = (path.lookPoints[pathIndex] - position).normalized;
                moveDirection = Vector2.Lerp(moveDirection, targetDirection, Time.deltaTime * turnSpeed);

                // Update position
                position += moveDirection * speed * speedPercent * Time.deltaTime;
                transform.position = new Vector3(position.x, position.y, transform.position.z);

                // Calculate agent velocity
                velocity = (position - previousPosition) / Time.deltaTime; // Velocity is change in position over time
                previousPosition = position; // Update previous position
            }

            yield return null;
        }

        // If not following the path, ensure velocity is zero
        velocity = Vector2.zero;
    }

    public void SetDestination(Vector2 newDestination) => agentDestination = newDestination;

    private void OnDrawGizmos()
    {
        if (path != null)
        {
            path.DrawWithGizmos();
        }

        // Draw a line showing the direction the object is facing
        Gizmos.color = Color.red;
        Vector3 direction = transform.right; // Assuming the character is facing right by default
        Gizmos.DrawLine(transform.position, transform.position + direction * 2);
    }
}