Get NavMesh edges

Hi guys, I’m trying to implement a cover system for my AI. I used to do this by getting the edges of the navmesh (eg. the vertice that stops before the wall/obstacle) and iterate through those until I found an appropriate one.

Is there any way I can get this in the code, I couldn’t find anything related to it in the code.

Thanks, much appreciated.

I’m looking for this as well… am bumping this in hope that someone could answer

This would be very useful. I’m bumping this too.

Hey guys,

Here you can find Rama’s way to get the navmesh edges in code.

Hope this helps :slight_smile:
Elias

For anyone ever stumbling onto this thread - I’ve had the same problem. I don’t know if it was possible to do it this way in 2014, but it’s possible now (4.25 at least). Works rock solid. Here we go:

UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());

INavigationDataInterface* const NavData = NavSys->GetNavDataForActor(*GetOwner());

const ARecastNavMesh* const RecastNavMesh = Cast<const ARecastNavMesh>(NavData);
						
FRecastDebugGeometry NavMeshTileGeo;
NavMeshTileGeo.bGatherNavMeshEdges = true;

TArray<FVector> PointsToGatherTiles = { CurrentLoc, DesiredLoc };
TSet<int32> AddedTileIndices;

for (const FVector& GatherPoint : PointsToGatherTiles)
{
	int32 TileX, TileY;
	RecastNavMesh->GetNavMeshTileXY(GatherPoint, TileX, TileY);

	TArray<int32> TileIndices;
	RecastNavMesh->GetNavMeshTilesAt(TileX, TileY, TileIndices);

	for (int32 i = 0; i < TileIndices.Num(); ++i)
	{
		if (!AddedTileIndices.Contains(TileIndices[i]))
		{
			RecastNavMesh->GetDebugGeometry(NavMeshTileGeo, TileIndices[i]);
			AddedTileIndices.Add(TileIndices[i]);
		}
	}
}

I know “GetDebugGeometry” sounds like it’s not going to be present in a packaged build, but it is and I’m using it currently in our project.

The code is a bit more complex than it could, only because I need edges for two positions - current and destination. This is only really needed when the positions are on different navmesh tile (the squares that are recalculated asynchronously in editor), but in character movement code (where this is from) such situations occur - at some point you do move in a single frame from tile to tile. If you only need edges for a single point, you can remove the “PointsToGatherTiles” variable and the outer loop. You still need the inner loop, because there will be multiple indices for a single tile.

The condition inside the inner loop if (!AddedTileIndices.Contains(TileIndices[i]))makes sure you don’t get the same edge added twice, which would happen when both locations are in the same navmeshtile. If you only use a single point, this will never happen, and as such you may also remove that condition.

The final edge data is stored in NavMeshTileGeo.NavMeshEdges and it is stored in a pair format (each 2 points form an edge) so you need to access it like this:

for (int32 i = 0; i < NavMeshTileGeo.NavMeshEdges.Num(); i += 2)
{
     // Use elements i and i+1. This array always contains even number of entries.
}

I would suggest to look into EQS.

In the given case you could find the highest rated position for the NPCs by running some tests for “NPC visibility to the player”. You could find a good cover this way.

Also, you could run a test that finds the edges of the navmesh by looking for the furthest point from the querier (in this case the NPC) which are still reachable via pathfinding (there is also a test for this).

EQS is still exeprimental (but has been for many years) and you have to activate it via Editor Preferences → General → Experimental → Environment Querying System.

You have to use it in combination with behviour trees to use its full potential, but it is not as complicated as it seems.