2D NavMesh PathFinding

I am working on 2D game, My 2D project need use pathfinding. I search manual from official, and I found the NavMesh Pathfinding of unity support 3D game only… there must be mesh renderer. It cant work on 2D because there is no mesh renderer, unless you add MeshRenderer component add to GameObject,But it conflict with 2D SpriteRenderer…

So… I use the vertex coordinates of Collider2D attached to gameobject to create mesh, eg:

private MeshTransform CreateEdge2dMesh (EdgeCollider2D e2d)
    {
        if (e2d == null) {
            return null;
        }
        Vector2[] points = e2d.points;
        Vector2 offset = e2d.offset;
        Bounds bounds = e2d.bounds;
        Vector3 cv = new Vector3 (bounds.center.x, bounds.center.y, 0);

        Vector3[] vertices = new Vector3[points.Length + 1];
        Vector2[] uvs = new Vector2[vertices.Length];
        int[] triangles = new int[(points.Length - 1) * TRIANGLES_COUNT];
        vertices [points.Length] = cv;
        uvs [points.Length] = Vector2.zero;
        for (int i = 0; i < points.Length; i++) {
            Vector2 pt = e2d.transform.TransformPoint (points [i]);
            vertices [i] = new Vector3 (pt.x + offset.x, pt.y + offset.y, 0);
            uvs [i] = Vector2.zero;
            if (i < points.Length - 1) {
                triangles [i * TRIANGLES_COUNT] = i;
                triangles [(i * TRIANGLES_COUNT) + 1] = i + 1;
                triangles [(i * TRIANGLES_COUNT) + 2] = points.Length;
            }
        }

        Mesh mesh = new Mesh ();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.uv = uvs;
        mesh.RecalculateNormals ();
        MeshTransform mt = new MeshTransform ();
        mt.mesh = mesh;
        mt.transform = e2d.transform;

        return mt;
    }

Then I call the function eg:

public void AddMesh (MeshTransform mt)
    {
        if (mt == null) {
            return;
        }

        NavMeshBuildSource nmbs = new NavMeshBuildSource ();
        nmbs.shape = NavMeshBuildSourceShape.Mesh;
        nmbs.transform = mt.transform.localToWorldMatrix;
        nmbs.area = 0;
        nmbs.sourceObject = mt.mesh;

        m_NavMeshSourceList.Add (nmbs);
    }

and call NavMeshBuilder.UpdateNavMeshData…
I add the NavMeshAgent to my player, but it doesn’t work.
Console print:
Failed to create agent because it is not close enough to the NavMesh

I think the NavMesh PathFinding system need the mesh vertex information to create or bake the walkable/diswalkable area, I create mesh which sharp is exactly match the collider, and use this to create/bake walkable area…

Do I went about it in the wrong way?..

thx…

1 Like

You can’t use navmesh in 2d right now. If you absolutely cannot use a grid for A*, check this out:

http://www.jgallant.com/nodal-pathfinding-in-unity-2d-with-a-in-non-grid-based-games/

1 Like

Thank you, I think the navmesh need the mesh vertex information to bake walkable area, so I create mesh and send the mesh information to navmesh system of unity, no matter what unity pathfinding algorithm is It need something like mesh to generate nodemap or orther else assist struct, the algorithm just calculate out the walk path data and return base on the mesh information we provided… But I found it cant work… Is there something wrong, or the thread go to a wrong way?

Apparently the problem with navmesh in 2d is the coordinates. 2d uses x,y and navmesh uses x,z. People with a lot of time on their hands have figured out how to switch the coordinates around. Check this out:

https://noobtuts.com/unity/navigation2d

1 Like

PolyNav is another asset with strong reviews:

1 Like

I am sorry but this is completely incorrect. Navmesh can be used in 2D since 2017. Use a navmesh surface component, and youll be able to create a navmesh on anything.

https://docs.unity3d.com/Manual/class-NavMeshSurface.html

Have you gotten this to work? I don’t think it works with sprites due to coordinate mismatch

1 Like

Apparently, it’s theoretically possible for this to work, however, it’s not feasible. You will need to rebuild your scene in 3d and rotate all game objects to face xz instead of xy. You will also need 3d colliders instead of 2d. I’ve ran some tests and with some tinkering it maybe is possible, but, I strongly recommend using A*!!

3 Likes

thx you so mush, you best! I will try A*.

I dont think it work… I have tried…

Works fine for me?

What did you do to set it up?

Just to confirm before sinking in hours upon hours trying to do the impossible.
You made navmesh pathfinding work just on sprites in a 2D unity project (started as a 2D project)?

1 Like

Not without some workarounds. From my experience I had to do this:

  • As mentioned, you need to get the navmeshplane component from unity’s git. This will make it possible to bake on any plane, no matter the orientation. So we can use XY plane.

  • 2D Colliders doesn’t bake by default. I found a script from a blog that made it possible on runtime. If you want to bake in editor I would need to use a 3D collider.

  • The navmesh agent would rotate itself to ground to the plane, this would be a problem because it would rotate the sprite/image as well with it. Work around for this was to just put the agent component on an child object and use it with script. So you set it to not move or rotate, but still give it a path - you can then use the desiredVelocity and use those values to move the 2D object.

I think that solved all the problems. But it’s a lot of workaround, so you would need to get used to setting your objects up in this way.

Hope that helped. Just saying it’s possible.
If anyone has an easier way I would love to hear about it!

2 Likes

Hi.

I here to confirm that NavMesh totally works in 2D. I implemented NavMeshSurface2d for tilemap in top down shooter as proof of concept.

You need:

  1. https://docs.unity3d.com/Manual/class-NavMeshSurface.html just because it has base implementation
  2. Empty object rotated respectively to Tilemap (90;0;0) with NavMeshSurface
  3. Implement source collector for tiles, because NavMeshBuilder.CollectSources will not work
  4. Use X and Z axis for NavMeshBuildSource()

so with something like this

    var src = new NavMeshBuildSource();
    src.transform = Matrix4x4.Translate(tilemap.GetCellCenterWorld(vec3int));
    src.shape = NavMeshBuildSourceShape.Box;
    src.size = Vector3.one;
    sources.Add(src);

You will able to bake tilemap. With NavMesh API you can implement any complex shapes you want.

Also totally check this “Runtime NavMesh Generation”

2 Likes

here some POC code, if somebody interested.

It will generate NavMesh from first TileMap with TilemapColider2d.

3795436–318604–NavMeshSurface2d.cs (19.2 KB)
3851014–325753–NavMeshSurfaceEditor2d.cs (19.8 KB)

3 Likes

Wish I found this before I implemented my own A* pathing into my 2D game, though I guess now I have something more efficient and optimisable.

Hi,

Could you please help me out with a basic Unity project where you set up these? I’m stuck with it. I have a simple project but I’m generating the Tilemap in runtime. To be honest I don’t know where to put the 90,0,0 rotated empty game object, but I’ve set a navmeshsurface2d component to the Tilemap and the ‘Collect Objects’ is set to Grid. ‘Use Geometry’ is set to Render Meshes but I tried also with Physics Collider doesn’t change a thing.
Sorry I’m completely lost. I’m a bit new to Unity but otherwise an experienced programmer.

Hi,

This kind a proof of concept for Top Down games, so it is not a production code. So in any way you need to learn it by yourself.
But here some hints:

  1. If you generating Tilemap at runtime, so NavMeshSurface2d.BuildNavMesh() should be called after it is done (check the videos I linked).
  2. Empty Gameobject should be placed in Scene root, with NavMeshSurface2d component added and configured. Press right click on its transform, and input rotation x:90 y:0 z:0
  3. In NavMeshSurface2d select Collection Object to Grid.

So the points of code you are interested are “CollectObjects2d.Grid” (enum that I added)

First - word bounds calculation:
L:351 Bounds CalculateWorldBounds(List sources)
Second - collect source objects, that are tiles:
L:241 List CollectSources()

My implementation looks for TilemapCollider2D component, by iteration through its tiles as unwalkable. That’s all folks.

You can proxy with navmeshAgent, or use static methods CalculatePath. Just be sure that agent touches navmesh, or it will throw exception that agent is too far.

Ahahaha

after couple of updates it does not work)

After couple of updates code above does not work)

So here is an update, it has better production value, but still its just a POC. Maybe I will put in on GitHub

I updated bounds calculation

if (m_CollectObjects == CollectObjects2d.Grid)
 
            {
 
                var grid = FindObjectOfType<Grid>();
 
                var colider = grid.GetComponentInChildren<CompositeCollider2D>();
 
                var bounds = GetWorldBounds(worldToLocal , colider.bounds);
 
                bounds.Expand(0.1f);
 
                return bounds;
 
            }

And Source Collection, that will substract unwalkable areas
It is mandatory to have CompositCollider2d, as it used to get bounds.
Create root object, rotate x 90, in navmesh2d select “Grid” and “Unwalkable”.

3851014–325753–NavMeshSurfaceEditor2d.cs (19.8 KB)
3851014–325756–NavMeshSurface2d.cs (19.8 KB)

1 Like