- Home /
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!
Answer by andrew-lukasik · Apr 14, 2021 at 01:57 PM
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: Just points on a NavMesh. Nothing special.
5 minutes later..
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[i];
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;
Gizmos.DrawSphere( point , 0.1f );
}
}
}
#endif
}
This looks like exactly what I need. I am working on implementing the code but I have a question. How did you add the points? Is it the first script? I am new to coding so I am not totally sure. Also what objects do I put the scripts on?
Thank you so much
Create an empty gameObject and add NavPoints.cs
component there. In it's original form this script generates points in OnEnable
:
points = SamplePoints( ...
so every time this object is enabled. NavPoints
has also few inspector fields you may want to tweak because, for example, Size field will deter$$anonymous$$e size of a bounding box where NavMesh sampling takes place (this volume is drawn in Scene view as white wire box when selected).
Your answer
Follow this Question
Related Questions
How to detect if NavAgent has reached hit.point? (2) Distance doesn't work? 1 Answer
Raycast feelers not reading in all four directions. 0 Answers
Why Raycast is not working properly? 0 Answers
How can i adjust the hit point vector to point towards the players direction 0 Answers
Why Do Raycasts Ignore NavMesh Agents 4 Answers