Find Random point on Navmesh Surface in Unity3D
Tags: unity3d, gamedev
Published: 2022-11-17 17:40:57

`Useful For: Random Patrolling Enemy or NPC`

In order to find a random point on Navmesh Surface on unity terrain or mesh first we have to bake a Navmesh Surface with a Navmesh-Agent on it, then we find Navmesh point with C#.


## 1. Bake Navmesh Surface
Bake a navmesh surface using unity's built-in navmesh panel which you can find under `Windows >> AI >> Navigation` 
Or [Recommended] you can use Navmesh Surface Component available in new [Navmesh Component Package](https://docs.unity3d.com/Manual/NavMesh-BuildingComponents.html) which you can add using package manager add by name and just write `com.unity.ai.navigation` and click add. 


## 2. Setup Navmesh Agent
After you done baking place a navmesh agent on the surface and attack a C# script which will find a random position for agent to move.


## 3. Finding Navmesh Point
In order to find a random navmesh point on surface We are going to use the method provided by unity which finds the nearest point based on the NavMesh within a specified range.

[Navemsh.SamplePosition](https://docs.unity3d.com/ScriptReference/AI.NavMesh.SamplePosition.html)

```
public static bool SamplePosition(Vector3 sourcePosition, out AI.NavMeshHit hit, float maxDistance, int areaMask);
```

Firstly Declare the necessary data to pass as parameter to get the position

```
NavMeshHit hit;
Vector3 origin = transform.position;
float radius = 4f;
```

Now we find the navmesh point by passing the data

```
NavMesh.SamplePosition(origin, out hit, radius, NavMesh.AllAreas);
```
Finally, In return this method will provide us the nearest Navmesh point in hit

```
Vector3 navmeshPoint = hit.point;
```


## 4. Adding Randomness in point Detection
The method "SamplePosition" will only return us the nearest navmesh point, so it doesn't matter if you provide larger radius.
So, in order to find a random navmesh point we will provide it a random origin point within a range. It will goes like:

```
//Find Random Position within a sphere
Vector3 randomPosition = UnityEngine.Random.insideUnitSphere * roamingPosMaxDistance;
//reset y-axis because we don't need to find it in air
randomPosition.y = 0;
//Add this with the agent position to get random position with agent position as origin
vector3 RandomPos = transform.position + randomPosition;
```

Now we pass this `RandomPos` as origin

```
NavMesh.SamplePosition(RandomPos, out hit, radius, NavMesh.AllAreas);
```
So now each time it calculate a random point from agent and then find the nearest Navmesh point from there... 


## 5. What if there is no navmesh around Random Point..?
Well in that condition we need to repeat the process untill we find a valid point on surface

```
randomPosition = UnityEngine.Random.insideUnitSphere * roamingPosMaxDistance;
randomPosition.y = 0;
RandomPos = transform.position + randomPosition;

//Loop untill find proper point
while(!NavMesh.SamplePosition(RandomPos, out hit, radius, NavMesh.AllAreas)
{
  randomPosition = UnityEngine.Random.insideUnitSphere * roamingPosMaxDistance;
  randomPosition.y = 0;
  RandomPos = transform.position + randomPosition;
}

finalPoint = hit.point;
```

## Final Tip
`Based on Personal Experience`
Run whole process inside a Coroutine or Async[basically on separate thread]. So that if no navmesh surface is available your agent will not just loop the process infinitely and freeze the whole game. For coroutine wait for next frame every time loop execute. So finally it will be like

```
IEnumerator FindRandomPoint()
{
randomPosition = UnityEngine.Random.insideUnitSphere * roamingPosMaxDistance;
randomPosition.y = 0;
RandomPos = transform.position + randomPosition;

//Loop untill find proper point
while(!NavMesh.SamplePosition(RandomPos, out hit, radius, NavMesh.AllAreas)
{
  randomPosition = UnityEngine.Random.insideUnitSphere * roamingPosMaxDistance;
  randomPosition.y = 0;
  RandomPos = transform.position + randomPosition;
  
  yield return new WaitForEndOfFrame();
}

finalPoint = hit.point;
}
```