One of the most convenient new features added to the Unity game engine is the NavMesh system. These built-in components allow for automatic pathfinding with minimal configuration in a scene. They also offer an impressive amount of customization out-of-the-box, such as dealing with more complex geometries and adding multiple custom agent sizes and step heights.
The NavMesh components are also highly performant, as this video from Brackeys shows. They even outperform standard A* implementations and other popular packages from the Asset Store.
However, many game developers want to visibly draw the path that an agent is taking. Unfortunately, this feature currently lacks built-in support from Unity. I’m going to demonstrate some ways you can do it manually, but depending on your use case, it may require some extra effort.
As a quick aside, this tutorial assumes that you have already grabbed the updated NavMesh components from the GitHub repository. Make sure you have them imported into your project (note that these are still in beta).
Setup
As an example, I’ll be using a simple tower defense game I created where waves spawn at a fixed point and head to a fixed target. All of the enemies that spawn are NavMeshAgents. The player can build towers anywhere on the map. This means the path for enemies is redrawn constantly, so I have to bake my NavMesh on the fly whenever a new tower is built.
The methods discussed below will work for any type of game, whether it is a tower defense, RTS, or something else that requires displaying the path.
The first thing you’ll want to do is to create a line renderer component on a game object in your scene. Possible options for this include a manager object or whatever contains the script that controls your spawning, but the choice is ultimately yours.
Additionally, I created a simple DrawNavMeshPath
script that adds the points in the path to the line render. For simplicity’s sake, the script just contains a static list of points that other scripts can edit, but you’ll probably want to refactor that into something better. The code for DrawNavMeshPath
is:
public static Vector3[] path = new Vector3[0];
private LineRenderer lr;
void Start()
{
lr = GetComponent<LineRenderer>();
}
void Update()
{
if (path != null && path.Length > 1)
{
lr.positionCount = path.Length;
for (int i = 0; i < path.Length; i++)
{
lr.SetPosition(i, path[i]);
}
}
}
Method 1: Draw the Path for Every Agent
Now that we have our line renderer set up, it’s time to draw our path. This first method is the simplest one, and, as the name implies, it involves drawing out the path for every NavMeshAgent currently in your scene. If you only ever have a handful of agents simultaneously in your game, or you want every path to be visible, this could be a good method for you.
This method will require a bit of refactoring of our DrawNavMeshPath
class since it will have to draw multiple paths simultaneously instead of just one, but you can do this by simply adding this script as a component to each enemy (or unit that is a NavMeshAgent). You’ll also have to add a line renderer component to each unit rather than having only one on a separate game object.
Assuming that you’ve refactored it accordingly and that the script that controls your enemy has access to your draw script, all you need to do to get this to work is to make a call like:
DrawNavMeshPath.path = agent.path.corners;
In this case, “agent” is the script that controls your unit and “corners” is the name of the array on NavMeshAgent that holds the actual points in the path. (Also, make sure you’ve actually called agent.setDestination(yourTargetDestination)
before this–no path will be calculated unless your agent has a destination or has a destination and is paused).
Method 2: Draw the Path Once for a Group of Agents
This particular method is the use case that I originally had for drawing an agent’s path, and it’s a bit more involved than the previous one. It involves drawing a single path, and it works well if you have a game like a tower defense where all of your enemies should be moving on the same path. It could also work in a variety of other games, such as in an RTS when a player is moving a group of units and you want to display a sort-of “average” path that all of them will follow.
Issues
There were two implementations I tried that didn’t work. The first was drawing the path of the first enemy that spawned in a wave immediately upon its creation. The problem with this, though, is that agent.setDestination
is an asynchronous call, so there’s no guarantee at any time that you’ll actually have the path. It almost definitely won’t work if you call it within the same function or frame where you spawn your enemy.
This is one of the more frustrating parts of the current implementation of NavMesh within Unity. There’s no way to synchronously access the path, or to force the engine to generate a new one.
Another method I tried that didn’t work was to have a static object, like the portal that spawned my enemies, and attach a NavMesh agent to that class. I then tried to set the destination for the portal and immediately pause it by setting the isStopped
property to false
. The issue with this, however, is that an agent does not generate paths while it is paused, so trying to access the path would only return an empty list.
Actual implementation
My solution was to have my wave spawner keep track of the previously spawned enemy. When it tries to spawn the next enemy in a wave, it also re-draws the path using the one that the previously spawned agent generated.
This works because I have a buffer of around a quarter-second between enemy spawns, and that’s usually enough frames for the path to have been calculated. (If not, I just keep the path that was previously drawn.) It’s a bit of a hack, but it works well for a game as simple as mine.
There are certainly some downsides to this implementation. It’s somewhat wasteful, since it involves constantly redrawing the path even when it might not change. It also means that if something (whether that be from a unit spawning or a different move command, etc.) blocks your current path before your next path redraw, all of your agents will re-calculate their routes, and there will be a few moments where the displayed path is no longer accurate.
This wasn’t an issue for me since I prevented players from building on tiles that the agents currently included in their path. However, it could be a more serious problem in a game like an RTS where that isn’t an option.
Conclusion
Ultimately, the point is that while Unity’s current implementation of NavMesh is quite optimized and good for simpler use cases, it still lacks some of the more powerful or complex features that some games may require. Drawing paths is an example of this, and each game will need a different blend of hacking and toying with Unity’s system in order to find something that works.
There are definitely ways to mold Unity to meet your needs, but if effectively displaying a path is something your game relies on, it may be better to roll your own navigation system or find a more robust one available in the Asset Store.