Support Forum

SOLUTION: Flying unit pathfinding!

Hello pathfinding community! I’ve seen several inquiries for flying unit pathfinding, and I’m here to provide you with a script that might help with that.

The debug path line on the left uses a point graph with air nodes, and the debug path line on the right uses a navmesh. I have it set up to dynamically generate a point graph based on the dimensions of my navmeshgraph. This means I don’t ever need to worry about my air unit graph - as long as my ground unit graph is configured correctly, it just works!

Note that this is not a one-size-fits all script! This is a proof-of-concept, and should be altered to accommodate your specific use case.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;

public class FlyingGraph : MonoBehaviour
{
    Vector3 checkNodePosition;
    Vector3 createNodeposition;
    Vector3 navMeshBounds;
    public float nodeDistance = 8f;
    void Start()
    {
        PointGraph pg = AstarPath.active.data.AddGraph(typeof(PointGraph)) as PointGraph;
        // maxDistance accommodates diagonal connections as well. If you don't want diagonal connections, remove the 1.75 multiplier
        pg.maxDistance = nodeDistance * 1.75f;

        navMeshBounds = AstarPath.active.data.navmesh.forcedBoundsSize;

        AstarPath.active.AddWorkItem(new AstarWorkItem(ctx =>
        {
            // Iterate through all 3 dimensions and create nodes
            for (checkNodePosition.x = navMeshBounds.x / 2f; checkNodePosition.x >= -navMeshBounds.x / 2f; checkNodePosition.x -= nodeDistance)
            {
                for (checkNodePosition.y = navMeshBounds.y /2f; checkNodePosition.y >= -navMeshBounds.y /2f; checkNodePosition.y -= nodeDistance)
                {
                    for (checkNodePosition.z = navMeshBounds.z / 2f; checkNodePosition.z >= -navMeshBounds.z / 2f; checkNodePosition.z -= nodeDistance)
                    {
                        // Accommodate weird bounding box offset
                        createNodeposition = new Vector3(checkNodePosition.x,checkNodePosition.y+navMeshBounds.y/2f,checkNodePosition.z);
                        // Make sure the node is not too close to any walls. My wall layer mask is 13
                        if (!Physics.CheckSphere(createNodeposition, nodeDistance / 2f, 1 << 13, QueryTriggerInteraction.Collide))
                        {
                            //Uncomment to see primitive spheres where nodes are created
                            //var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                            //sphere.transform.position = createNodeposition;
                            //Destroy(sphere.GetComponent<Collider>());
                            pg.AddNode((Int3)createNodeposition);
                        }

                    }
                }
            }
            // Connect all the dots!
            pg.ConnectNodes();
        }));
        // Force pathfinding to do the work item we just made
        AstarPath.active.FlushWorkItems();
    }
}

Once you have this graph set up, the seeker component on your ground units should use graph 0, and the seeker component on your flying units should use graph 1.

Important note: nodeDistance is highly dependent on your scene’s dimensions. My scene is only about 100x100 units, and if I set the node distance too low (say like around 3), my scene takes an extra 30+ seconds to load. Performance gains/losses are exponential when you increase/decrease this number.

Another important note: This solution depends on the bounding box of another graph, however you can pretty easily modify the code and set your own dimensions.

Let me know if you have any questions or potential optimizations I can make to this code. Thanks!

1 Like