Navmesh Generation

Hi,

I want to generate random levels for my fps game. The idea is shown in the really bad picture I attached.

The player is stationary in the middle, boxes are randomly created around the player facing random angles. Enemies are created and they run around the player.

This is the hard part, I know about Navmesh and how to use it, but I wanted to know If I can generate a nevmesh modifier in the red areas?

The idea is that those areas are “Cover” and that enemies will run form cover to cover. Cover is any area that is out of sight of the player. Would I use raycasts in a circle around the player? how can I add the area to a modifier?

Any help is appreciated, I have no idea where to begin so anything is helpful.

Thank you!

I suggest you take a pragmatic approach and simplify this as much as possible to reduce need for any intense math here.
_
First, lets start with that you probably don’t really need a Mesh or NavMesh exactly but a pool of points that all met some specific criteria. That criteria, for example, being:

  • Point must be reachable from a nav mesh
  • Point must represent a location of some kind of safe cover against given (player) position
    _
    So let’s try that:
    ![points on a NavMesh][1]
    Just points on a NavMesh. Nothing special.

5 minutes later…

![NavMesh safe cover locations][2]
_
Optimization

To optimize this I advise you not to raycast all of these points every frame like I did but only once to generate a companion hash map <player_spatial_hash key,int[] point indices> instead. Idea here being that you create a lookup table for every (reasonable) player position on scene start and just lookup point pools with zero additional raycasts after that.
_

// NavPoints.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Unity.Mathematics;
[ExecuteInEditMode]
public class NavPoints : MonoBehaviour
{
	[SerializeField] float3 _size = new float3{ x=20 , y=10 , z=20 };
	[SerializeField] float3 _step = new float3{ x=0.5f , y=1 , z=0.5f };
	public static half3[] points = new half3[0];
	#if UNITY_EDITOR
	void OnDrawGizmosSelected () => Gizmos.DrawWireCube( transform.position , _size );
	#endif
	void OnEnable ()
	{
		points = SamplePoints( new Bounds{ center=transform.position , size=_size } , _step , 1<<0 );
	}
	public static half3[] SamplePoints ( Bounds bounds , float3 step , int areaMask )
	{
		int3 numSteps = (int3)( (float3)bounds.size / step );
		float3 start = (float3)bounds.min + step*0.5f;
		float sampleDist = (float) math.max(math.max( step.x , step.y ) , step.z );
		var set = new HashSet<half3>();
		for( int y=0 ; y<numSteps.y ; y++ )
		for( int z=0 ; z<numSteps.z ; z++ )
		for( int x=0 ; x<numSteps.x ; x++ )
		{
			float3 vec = start + step*new int3{ x=x , y=y , z=z };
			if( NavMesh.SamplePosition( sourcePosition:vec , hit:out var hit , maxDistance:sampleDist , areaMask:areaMask ) )
				set.Add( (half3) (float3) hit.position );
		}
		var results = new half3[ set.Count ];
		set.CopyTo( results );
		Debug.Log($"{nameof(NavPoints)}: {results.Length} points sampled ({(results.Length*6)/1000f:0.0} MB)");
		return results;
	}
}

_
// PlayerHeadLocationTester.cs
using UnityEngine;
using Unity.Mathematics;
public class PlayerHeadLocationTester : MonoBehaviour
{
#if UNITY_EDITOR
void OnDrawGizmos ()
{
RaycastHit hits = new RaycastHit[1];
float3 origin = transform.position;
for( int i=0 ; i<NavPoints.points.Length ; i++ )
{
float3 point = NavPoints.points*;*

  •  	float3 dir = point - origin;*
    
  •  	if( Physics.RaycastNonAlloc( origin , dir , hits , math.length(dir) , 1<<0 )!=0 )*
    
  •  	{*
    
  •  		var hit = hits[0];*
    

Gizmos.color = Color.blue;

  •  	    Gizmos.DrawSphere( point , 0.1f );*
    
  •  	}*
    
  •  	else*
    
  •  	{*
    

Gizmos.color = Color.yellow;