Introduction

When working with 2D physics in Unity, it often feels like you’re a second-class citizen. The 2D physics API and featureset has been lagging behind 3D for a while, and support for NavMesh pathfinding on the XY is… quite poorly documented. To the point I couldn’t find anyone explaining how to use it, even though the API has supported it since version 5.6!

This tutorial for Unity 2018.1 will be brief in written content, but contains a full educational demo scene that shows how to do the following:

How to generate (and regenerate!) NavMeshes at runtime, on the XY plane

How to handle multiple unit sizes

How to use use your existing 2D Physics colliders as static barriers that block pathfinding (including Polygon2D!)

How to perform pathfinding for multiple unit sizes without having to use a NavMeshAgent (so you can write your own AI code)

How to generate & regenerate NavMeshes at runtime, on the XY plane

This topic is covered mostly by Unity’s own tutorials here which shows how to use NavMeshSurfaces to bake NavMeshes at any orientation at all, and the most important component – the NavMeshSurface – is further explained on the manual here. However, there are several points I’d still like to make.

The NavMeshSurface can be rotated to any orietentation that you like, and combined together in wacky ways – as shown in the Tutorial video. This means that having it on the XY plane is as simple as creating a NavMeshSurface and rotating it until it lays on the XY plane.

The NavMeshSurface supports the baking of NavMeshes that pathfind around either Meshes or 3d Physics components. There is no built-in support for 2D Physics components.

You’ll need one NavMeshSurface for every unit size that you want to support.

Ultimately, once you’ve wrapped your head around the concepts and catches, the baking and re-baking of a NavMesh is as simple as iterating over all of your NavMesh surfaces and call the BuildNavMesh() method. Easy!

How to handle multiple unit sizes

The ability to add and remove different unit sizes can be done at design-time, and can be found in the Navigation window (visible by clicking on the toolbar -> Windows -> Navigation), under the “Agents” tab.

Each entry in the list allows us to specify a different unit size. While there is no ability to remove entries at runtime, we can add new entries (via the NavMesh.CreateSettings() method) and change any of the values for existing entries (the radius, height, step height, etc) at runtime, and also selectively generate NavMeshes for only specific agent types, which allows us to be as dynamic as we want!

The NavMeshSurface is associated with a particular NavMeshAgentTypeID, and internally fetches the stored NavMeshBuildSetting for that AgentTypeID every time you rebake its NavMesh. Unfortunately, there doesn’t appear to be a way of persisting your desired NavMeshBuildSettings back into Unity, so for the purposes of the tutorial example scene, I updated the NavMeshSurface.BuildNavMesh() method to take a NavMeshBuildSettings struct which it will use to rebake the NavMesh.

How to use 2D Physics colliders as static barriers that block pathfinding

While NavMeshSurfaces will happily generate pathfinding ‘walls’ automatically from meshes and 3D Physics colliders that it finds, it lacks automatic support for 2D Physics colliders. If you take a look in the NavMeshSurface code, when you call the BuildNavMesh method it goes off to identify the desired meshes or 3D Physics objects and creates a list of NavMeshBuildSource structs which represent them. For 2d Physics we can take control of the situation by generating our own NavMeshBuildSource structs which we’ll manually feed into the process.

The following code is for a component that you can attach to any GameObject that has a PolygonCollider2D, BoxCollider2D, or CircleCollider2D and it will allow you to generate a NavMeshBuildSource to represent the object.

Internally, NavMeshBuildSources support Box, Sphere, and Capsule shapes, which are direct fits for BoxCollider2D, CircleCollider2D, and CapsuleCollider2D, so creating NavMeshBuildSources for those is an easy task. PolygonCollider2Ds are trickier though – we need to generate a Mesh that represents the points of the PolygonCollider2D. Thankfully the dude runevision over at Unify Community has shared a script that splits a 2D polygon into triangles that we can use to build the Mesh, which makes the process very straight forward – as you can see in the code above.

How to perform pathfinding for multiple unit sizes without having to use a NavMeshAgent

After all that, this part is very straight forward. All you need to know is the AgentTypeID whose radius is the size you want. With that, you can create a NavMeshQueryFilter that specifies this is the AgentTypeID you’re interested in, and pass it along with the other parameters to NavMesh.CalculatePath method.

About

Spacebumfuzzle is a cheap-and-cheerful top-down shooter, designed to be easy to drop in and play without spending countless hours learning rules and mechanics. Grab a gamepad, and go nuts blowing shit up!