In case this is useful to anyone else here is the full code. This is a helper for removing unwanted areas from tiled recast graphs.
using System.Collections.Generic;
using UnityEngine;
namespace Pathfinding
{
public class PathRemoveUnconnected : GraphModifier
{
// Remove unconnected areas in tiled recast graphs that are not relevant
// After graphs are scanned, this will mark all nodes that are not relevant as not walkable and then remove them
// Desired functionality is similar to relevantGraphSurface but without having to place objects in each tile
// Usage: place an empty GameObject inside areas you want to keep and add these to the areaMarkers array
public Transform[] areaMarkers;
Vector3[] connectedPoints;
HashSet<GraphNode> connectedNodes = new HashSet<GraphNode>();
List<GraphNode> toSearch = new List<GraphNode>();
GraphNode nearestNode;
GraphNode searchNode;
int removedNodeCounter;
public override void OnPostScan()
{
Debug.Log("Graphs-->OnPostScan");
// Get connected points from area markers
connectedPoints = new Vector3[areaMarkers.Length];
for (int x = 0; x < areaMarkers.Length; x++) {
connectedPoints[x] = areaMarkers[x].transform.position;
}
// Create a list of the recast graphs in scene
List<RecastGraph> recastGraphs = new List<RecastGraph>();
foreach (RecastGraph r in AstarPath.active.data.FindGraphsOfType(typeof(RecastGraph))) {
recastGraphs.Add(r);
}
if (recastGraphs.Count == 0) return;
// Process each graph
toSearch.Clear();
connectedNodes.Clear();
for (int r = 0; r < recastGraphs.Count; r++) {
// Get the nearest node to each area marker and add to the search
for (int x = 0; x < connectedPoints.Length; x++) {
nearestNode = recastGraphs[r].GetNearest(connectedPoints[x], NNConstraint.Walkable).node;
if (nearestNode != null) {
connectedNodes.Add(nearestNode);
toSearch.Add(nearestNode);
}
}
// Go through all connected nodes and add them to the connected list
while (toSearch.Count > 0) {
searchNode = toSearch[toSearch.Count - 1];
toSearch.RemoveAt(toSearch.Count - 1);
searchNode.GetConnections(n =>
{
if (n.Walkable && !connectedNodes.Contains(n)) {
toSearch.Add(n);
connectedNodes.Add(n);
}
});
}
// Mark unconnected nodes as not walkable
recastGraphs[r].GetNodes(node =>
{
if (!connectedNodes.Contains(node)) {
node.Walkable = false;
node.ClearConnections(true);
}
});
// Update each tile and discard non walkable nodes
removedNodeCounter = 0;
recastGraphs[r].StartBatchTileUpdate();
foreach (var tile in recastGraphs[r].GetTiles()) {
var trianglesToKeep = new List<int>();
for (int i = 0; i < tile.nodes.Length; i++) {
var node = tile.nodes[i];
if (node.Walkable) {
trianglesToKeep.Add(tile.tris[i * 3 + 0]);
trianglesToKeep.Add(tile.tris[i * 3 + 1]);
trianglesToKeep.Add(tile.tris[i * 3 + 2]);
}
else {
removedNodeCounter++;
}
}
var offset = (Int3)new Vector3((tile.x * recastGraphs[r].TileWorldSizeX), 0, (tile.z * recastGraphs[r].TileWorldSizeZ));
Int3[] newVerts = new Int3[tile.verts.Length];
for (int i = 0; i < tile.verts.Length; i++) {
newVerts[i] = tile.vertsInGraphSpace[i] - offset;
}
recastGraphs[r].ReplaceTile(tile.x, tile.z, newVerts, trianglesToKeep.ToArray());
}
recastGraphs[r].EndBatchTileUpdate();
// Info
Debug.Log("Graph #" + r + ", Tile count: " + recastGraphs[r].GetTiles().Length + ", Nodes removed: " + removedNodeCounter);
}
}
}
}