What is the correct API for unloading a graph at runtime?

As discussed here I’m implementing a game design that requires me to additively load and unload sections of navmesh at runtime.

I’ve worked out most of the issues with loading Recast graphs additively, but now it turns out that unloading them is just as troublesome.

Calling AstarData.RemoveGraph(graph) causes an assert down the line because they graph is immediately removed from the graphs list, but as the graph is destroyed via SafeDestroy(), OnDestory() executes in the future and it tries to use its graphindex (which has already been invalidated.)

Just calling SafeOnDestroy() isn’t sufficient because that never removes the graph from the list, resulting in all sorts of problems.

Should the nulling of the graphs[graphIndex] entry be handled by the graph’s own OnDestroy() rather than from within RemoveGraph()?

And is there a process that culls empty entries from the end of the graphs array? at the moment I think that even if I could get the above to work then the graphs array would grow every time I additively loaded an then unload a graph.

In the fine spirit of working it out for myself I’ve managed to come up with the following which seems to work, even is reaching in to the Astar Library and fiddling with its internal data structures makes my skin crawl:


 //remove our graphs

        AstarPath.RegisterSafeUpdate(() => {
            if (m_Graphs != null) {
                foreach (Pathfinding.NavGraph graph in m_Graphs) {
                    uint index = graph.graphIndex;
                    graph.OnDestroy();
                    AstarPath.active.astarData.graphs[index] = null;
                }
            }

            //cull any trailing nulls from the list of graphs
            List<Pathfinding.NavGraph> graphs = new List<Pathfinding.NavGraph>();
            bool added = false;
            bool culled = false;
            for (int i =  AstarPath.active.graphs.Length-1; i >=0;i--) {
                Pathfinding.NavGraph graph = AstarPath.active.graphs[i];
                if (graph != null || added) {
                    graphs.Insert(0, graph);
                    added = true;
                } else {
                    culled = true;
                }
            }
            if (culled) {
                AstarPath.active.graphs = graphs.ToArray();
            }

        });

Hi

Sorry for the late answer.
Yeah… unloading and loading graphs might not be the best code in the library…

What I really should do is to stop the pathfinding threads, then completely remove the graph (SafeOnDestroy is too brittle and breaks when you don’t do exactly as intended) instead of as now call SafeOnDestroy and remove the graph immediately.
I will try to take a look at it before the next version.

Thanks for the feedback, and thanks for such a great library.

Hi

Just uploaded 3.6.1 beta. It includes better support for removing graphs during runtime.
There might be cases I have missed, but hopefully it will work.

1 Like