Search code examples
c#unity-game-engineartificial-intelligencepath-finding

Dynamic Pathfinding A* Unity3D C#


I'm trying to use A* pathfinding in Unity3D, and it works if the target remains stationary. However, if the target moves, the units will still follow the same path. Find the code for each unit below (attached to the seeker objects):

using UnityEngine;
using System.Collections;

public class Unit : MonoBehaviour {

    public Transform target;
    float speed = 20;
    Vector3[] path;
    int targetIndex;

void Start() {
    PathRequestManager.RequestPath(transform.position,target.position, OnPathFound);
}

public void OnPathFound(Vector3[] newPath, bool pathSuccessful) {
    if (pathSuccessful) {
        path = newPath;
        StopCoroutine("FollowPath");
        StartCoroutine("FollowPath");
    }
}

IEnumerator FollowPath() {
    Vector3 currentWaypoint = path[0];

    while (true) {
        if (transform.position == currentWaypoint) {
            targetIndex ++;
            if (targetIndex >= path.Length) {
                yield break;
            }
            currentWaypoint = path[targetIndex];
        }


        transform.position = Vector3.MoveTowards(transform.position,currentWaypoint,speed * Time.deltaTime);
        yield return null;

    }
}

public void OnDrawGizmos() {
    if (path != null) {
        for (int i = targetIndex; i < path.Length; i ++) {
            Gizmos.color = Color.black;
            Gizmos.DrawCube(path[i], Vector3.one);

            if (i == targetIndex) {
                Gizmos.DrawLine(transform.position, path[i]);
            }
            else {
                Gizmos.DrawLine(path[i-1],path[i]);
            }
        }
    }
}

}

now if I try to use it for dynamic pathfinding (target moving around, and paths updating) by changing void Start() to void Update(), this will not work. The response will cause weird unit behaviour, moving forth and back all the time or not completing the path etc. Now I don't know the exact cause for this, might be because of coroutines? Anyway, how could I alter the code so that correct dynamic pathfinding will be the result?

Thanks in advance!

P.S. all remaining source code is to be found here: http://bit.ly/pathfindingSource


Solution

  • I've "fixed" the problem, with a for now good enough solution.

    First of all, I make the pathfinding dynamic by requesting a new path in every waypoint. Second of all, the pathfinding keeps on going even after reaching the target, by some logic which checks every frame whether the units are far enough again to repeat the coroutine. If so, the old coroutine is stopped and a new coroutine is started.

    Here is the code:

    using UnityEngine;
    using System.Collections;
    
    public class Unit : MonoBehaviour {
    
    public Transform target;
    float speed = 20;
    Vector3[] path;
    int targetIndex;
    
    bool  newRequestReady;
    
    void Start() {
        PathRequestManager.RequestPath(transform.position,target.position, OnPathFound);
    
        newRequestReady = false;
    }
    
    void Update(){
        float distance = Vector3.Distance (transform.position, target.position);
        Debug.Log(distance);
    
        if(distance < 5){
            StopCoroutine("FollowPath");
            newRequestReady = true;
        }
        else if(distance >=10 && newRequestReady){
            PathRequestManager.RequestPath(transform.position,target.position, OnPathFound);
            StopCoroutine("FollowPath");
            newRequestReady = false;
    
        }
    }
    
    public void OnPathFound(Vector3[] newPath, bool pathSuccessful) {
        if (pathSuccessful) {
            path = newPath;
            StopCoroutine("FollowPath");
            StartCoroutine("FollowPath");
        }
    }
    
    IEnumerator FollowPath() {
        Vector3 currentWaypoint = path[0];
    
        while (true) {
    
            if (transform.position == currentWaypoint) {
                PathRequestManager.RequestPath(transform.position,target.position,OnPathFound);
                targetIndex=0;
    
                targetIndex ++;
                //Debug.Log(currentWaypoint);
    
                if (targetIndex >= path.Length) {
                    targetIndex =0;
                    path = new Vector3[0];
                //yield break;
                }
                currentWaypoint = path[targetIndex];
            }
                transform.position = Vector3.MoveTowards(transform.position,currentWaypoint,speed * Time.deltaTime);
    
            yield return null;
        }
    }
    
    
    }