Skip to content

Instantly share code, notes, and snippets.

@IJEMIN
Created August 20, 2018 14:08
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save IJEMIN/f2510a85b1aaf3517da1af7a6f9f1ed3 to your computer and use it in GitHub Desktop.
Save IJEMIN/f2510a85b1aaf3517da1af7a6f9f1ed3 to your computer and use it in GitHub Desktop.
Get Random Position on NavMesh in Unity
using UnityEngine.AI;
using UnityEngine;
public static class NavMeshUtil {
// Get Random Point on a Navmesh surface
public static Vector3 GetRandomPoint(Vector3 center, float maxDistance) {
// Get Random Point inside Sphere which position is center, radius is maxDistance
Vector3 randomPos = Random.insideUnitSphere * maxDistance + center;
NavMeshHit hit; // NavMesh Sampling Info Container
// from randomPos find a nearest point on NavMesh surface in range of maxDistance
NavMesh.SamplePosition(randomPos, out hit, maxDistance, NavMesh.AllAreas);
return hit.position;
}
}
@calypsoresende
Copy link

Vector3 returning (infinity,infinity,infinity)

@NilsMoller
Copy link

Considering this is the second result when googling around, I will leave my experiences here.

There are a few problems here.

  • The NavMesh#SamplePosition returns a boolean. True if it found a point, false if not. This is not being checked. If the sampling fails, the code will break. Using the return value of SamplePosition in an if statement will remove the errors from that.
  • Navmesh#SamplePosition gets any position on the navmesh. If there are two disconnect areas and the sample distance is far enough, it will find a position on the navmesh which your agent might not be able to get to. See the image (sampling from camera position).
    image

To get around the second problem, I would calculate the path before setting the destination to make sure it is reachable.
I have not tested this, but I'm sure you can figure something out along these lines.

bool foundPosition = NavMesh.SamplePosition(
    origin + Random.insideUnitSphere * maxRange,
    out navMeshHit,
    maxRange,
    NavMesh.AllAreas
);

if (!foundPosition)
    // throw exception, return false, retry, some other way to handle failed sample

NavMeshPath path = new NavMeshPath();
this.navmeshAgent.CalculatePath(navMeshHit.position, path);
canReachPoint = path.status == NavMeshPathStatus.PathComplete;

@IJEMIN
Copy link
Author

IJEMIN commented Nov 12, 2021

Considering this is the second result when googling around, I will leave my experiences here.

There are a few problems here.

  • The NavMesh#SamplePosition returns a boolean. True if it found a point, false if not. This is not being checked. If the sampling fails, the code will break. Using the return value of SamplePosition in an if statement will remove the errors from that.
  • Navmesh#SamplePosition gets any position on the navmesh. If there are two disconnect areas and the sample distance is far enough, it will find a position on the navmesh which your agent might not be able to get to. See the image (sampling from camera position).
    image

To get around the second problem, I would calculate the path before setting the destination to make sure it is reachable. I have not tested this, but I'm sure you can figure something out along these lines.

bool foundPosition = NavMesh.SamplePosition(
    origin + Random.insideUnitSphere * maxRange,
    out navMeshHit,
    maxRange,
    NavMesh.AllAreas
);

if (!foundPosition)
    // throw exception, return false, retry, some other way to handle failed sample

NavMeshPath path = new NavMeshPath();
this.navmeshAgent.CalculatePath(navMeshHit.position, path);
canReachPoint = path.status == NavMeshPathStatus.PathComplete;

thanks! :D I will update codes

@localhosted
Copy link

If you don't mind having the "random" points in a grid, you can pregenerate points according to the navmesh's bounds and generate points using Mathf.PerlinNoise

Afterwards you can exclude all points that aren't found by NavMesh.SamplePosition and then choose a random valid point with Random.Range(0, pointList.Count)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment