11
\$\begingroup\$

I'm currently making a prototype for a game I plan to develop. As far as I did, I managed to set up the navigation mesh and my navmeshagents.

I would like to display the path they are following when setDestination() is fired.

I did some researches but didn't find anything about it.

EDIT 1 : So I instantiate an empty object with a LineRenderer and I have a line bewteen my agent and the destination. Still I've not all the points when the path has to avoid an obstacle. Furthermore, I wonder if the agent.path does reflect the real path that the agent take as I noticed that it actually follow a "smoothier" path.

Here is the code so far :

GameObject container = new GameObject();
container.transform.parent = agent.gameObject.transform;
LineRenderer ligne = container.AddComponent<LineRenderer>();
ligne.SetColors(Color.white,Color.white);
ligne.SetWidth(0.1f,0.1f);
//Get def material

ligne.gameObject.renderer.material.color = Color.white;
ligne.gameObject.renderer.material.shader = Shader.Find("Sprites/Default");
ligne.gameObject.AddComponent<LineScript>();
ligne.SetVertexCount(agent.path.corners.Length+1);
int i = 0;
foreach(Vector3 v in p.corners)
{
    ligne.SetPosition(i,v);
    //Debug.Log("position agent"+g.transform.position);
    //Debug.Log("position corner = "+v);
    i++;
}
ligne.SetPosition(p.corners.Length,agent.destination);
ligne.gameObject.tag = "ligne";

So How can I get the real coordinates my agent is going to walk throught ?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Do you want to display it in game or just inside the Unity interface? \$\endgroup\$
    – nathan
    Dec 26, 2013 at 17:54
  • \$\begingroup\$ It would be in game. I start to think that maybe I should use calculatePath() and get corners to manually instanciate texutre with dotted or dashed line, and a texture with a cross at the destination ... \$\endgroup\$
    – Sladix
    Dec 27, 2013 at 8:45

4 Answers 4

14
\$\begingroup\$

It's actually really simple. First you put a line renderer component on your nav mesh agent object. If you notice, there is an array called positions. So if you attach the following script to your nav mesh agent, it will create a path between the nav mesh agent's origin and the destination.

var line : LineRenderer; //to hold the line Renderer
var target : Transform; //to hold the transform of the target
var agent : NavMeshAgent; //to hold the agent of this gameObject

function Start(){
    line = GetComponent(LineRenderer); //get the line renderer
    agent = GetComponent(NavMeshAgent); //get the agent
    getPath();
}

function getPath(){
    line.SetPosition(0, transform.position); //set the line's origin

    agent.SetDestination(target.position); //create the path
    yield WaitForEndOfFrame(); //wait for the path to generate

    DrawPath(agent.path);

    agent.Stop();//add this if you don't want to move the agent
}

function DrawPath(path : NavMeshPath){
    if(path.corners.Length < 2) //if the path has 1 or no corners, there is no need
        return;

    line.SetVertexCount(path.corners.Length); //set the array of positions to the amount of corners

    for(var i = 1; i < path.corners.Length; i++){
        line.SetPosition(i, path.corners[i]); //go through each corner and set that to the line renderer's position
    }
}

So that will create a path in the game view.

Hope this helps.

\$\endgroup\$
2
  • \$\begingroup\$ took me a while to realize this was Javascript and remember that Unity supported JS scripts, but this is still valid in C# (except from the function definition syntax) To be noted that as of 2018, LineRenderer supports SetPositions(), so you could just line.SetPositions(path.corners) \$\endgroup\$ Aug 28, 2018 at 16:22
  • \$\begingroup\$ I was doing in C# and figured out that too, just did line.SetPositions(path.corners). Altho I want to draw these line on the mesh, any idea? \$\endgroup\$
    – Matheus
    Feb 19, 2019 at 14:27
2
\$\begingroup\$

Ok I found a solution, Might not be the best but still a solution.

First, instantiate a game object with a line renderer and attach it to the agent :

NavMeshAgent agent = g.GetComponent<NavMeshAgent>();
agent.SetDestination (getDestination(hit.point+offset));
agent.CalculatePath(getDestination(hit.point+offset),agent.path);
GameObject container = new GameObject();
container.transform.position = agent.transform.position;
container.transform.parent = agent.gameObject.transform;

LineRenderer ligne = container.AddComponent<LineRenderer>();
ligne.SetColors(Color.white,Color.white);
ligne.SetWidth(0.1f,0.1f);
//Get def material
ligne.gameObject.renderer.material.color = Color.white;
ligne.gameObject.renderer.material.shader = Shader.Find("Sprites/Default");
ligne.gameObject.AddComponent<LineScript>();
ligne.gameObject.tag = "ligne";
ligne.enabled = false;

And then in the LineScript script :

float dist=this.transform.parent.GetComponent<NavMeshAgent>().remainingDistance;
if (dist != Mathf.Infinity && this.transform.parent.GetComponent<NavMeshAgent>().pathStatus == NavMeshPathStatus.PathComplete && this.transform.parent.GetComponent<NavMeshAgent>().remainingDistance == 0)
        Destroy (gameObject);

this.GetComponent<LineRenderer>().SetVertexCount(this.transform.parent.GetComponent<NavMeshAgent>().path.corners.Length);
int i = 0;
foreach(Vector3 v in this.transform.parent.GetComponent<NavMeshAgent>().path.corners)
{
    this.GetComponent<LineRenderer>().SetPosition(i,v);
    //Debug.Log("position agent"+g.transform.position);
    //Debug.Log("position corner = "+v);
    i++;
}
if (this.GetComponent<LineRenderer> ().enabled == false)
    this.GetComponent<LineRenderer> ().enabled = true;
\$\endgroup\$
1
  • \$\begingroup\$ I still got to handle multiples nav points \$\endgroup\$
    – Sladix
    Jan 9, 2014 at 8:47
2
\$\begingroup\$
using UnityEngine;
using System.Collections;

public class PathUtils : MonoBehaviour {
private NavMeshAgent agent;
private Color c = Color.white;
public void Start() {
    agent = gameObject.GetComponent<NavMeshAgent> ();
}

public void Update() {
    StartCoroutine(DrawPath(agent.path));
}

IEnumerator DrawPath(NavMeshPath path) {
    yield return new WaitForEndOfFrame();
    path = agent.path;
    if (path.corners.Length < 2)
        return;
    switch (path.status) {
    case NavMeshPathStatus.PathComplete:
            c = Color.white;
            break;
    case NavMeshPathStatus.PathInvalid:
            c = Color.red;
            break;
    case NavMeshPathStatus.PathPartial:
            c = Color.yellow;
            break;
    }
            
    Vector3 previousCorner = path.corners[0];
    
    int i = 1;
    while (i < path.corners.Length) {
        Vector3 currentCorner = path.corners[i];
        Debug.DrawLine(previousCorner, currentCorner, c);
        previousCorner = currentCorner;
        i++;
    }
    
}
}

Edit: I had problems achieving this goal because the path.corner numbers were wrong. You have to wait until EndOfFrame otherwise you get an incomplete path.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Short explanation would be great. What does the code do? Also, edit your post to contain all the code in one code block. Thanks \$\endgroup\$
    – Katu
    Jul 18, 2014 at 4:00
  • \$\begingroup\$ Seems more clean that my try ^^ But this will only work in the editor ... I wanted to draw the agent path ingame, and only the valid ones \$\endgroup\$
    – Sladix
    Jul 18, 2014 at 9:34
  • \$\begingroup\$ This actually worked first try once I corrected the typo and missing "agent.path" argument in DrawPath (which I submitted as an edit). Don't see why it would only work in the editor, but it is fine for my needs either way \$\endgroup\$
    – Mike Wise
    Oct 21, 2021 at 10:13
1
\$\begingroup\$

This is a really easy way to render the actual path of NavMeshAgents (but note that these Gizmos are only drawn in the Scene windows, not the actual Game window).

Use OnDrawGizmos!

It's a really useful method you can fill out on any MonoBehaviour, and it gets called automatically, just like Start and Update, if it exists in your script.

private void OnDrawGizmos()
    {
        if (agent.destination != null)
        {
            Gizmos.color = Color.white;
            {
                // Draw lines joining each path corner
                Vector3[] pathCorners = agent.path.corners;
                
                for (int i = 0; i < pathCorners.Length - 1; i++)
                {
                    Gizmos.DrawLine(pathCorners[i], pathCorners[i + 1]);
                }

            }
        }
    }

Just make sure you provide a reference to your agent.

=====

Here's some other examples of things you can do with OnDrawGizmos:

You can do things like:

  • draw a cube marker at the agent's destination point.
  • draw a line from your agent to their destination.
private void OnDrawGizmos()
    {
        if (agent.destination != null)
            {
                Gizmos.color = Color.magenta;
                Gizmos.DrawCube(agent.destination, new Vector3(0.2f, 0.6f, 0.2f));
                Gizmos.DrawLine(agent.destination, transform.position + (Vector3.up * 1.5f));
            }
     }

What I like about it is that it's very quick and easy to add, as you don't need to deal with game objects or components. Just draw these things straight to the scene window without much fluffing around.

But again, if you want these things to appear in Game view, you'll need to do something else!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .