Search Unity

Generating a random position on Navmesh.

Discussion in 'Navigation' started by vladimirsmart, Apr 22, 2020.

  1. vladimirsmart

    vladimirsmart

    Joined:
    Nov 27, 2017
    Posts:
    25
    I am making a open-world game where i'll have NPCs wandering around the city. I wonder if there is any way to generate a random position that never is off the Navmesh. Thanks for your help.
     
  2. vhman

    vhman

    Joined:
    Aug 13, 2018
    Posts:
    355
    Hi,

    NavMesh.RayCast, NavMesh.SamplePosition and NavMesh.CalculatePath may help
     
  3. vladimirsmart

    vladimirsmart

    Joined:
    Nov 27, 2017
    Posts:
    25
    Thanks. Do you have any ideas on how to implement this?
     
  4. vhman

    vhman

    Joined:
    Aug 13, 2018
    Posts:
    355
    I'm putting waypoint on a map, then I select random waypoint to follow.
    Pre-made waypoint doesn't mean they cannot be created thought script or any other tools.
     
  5. vladimirsmart

    vladimirsmart

    Joined:
    Nov 27, 2017
    Posts:
    25
    Thanks very much, once again, but i need ideas on what code to use. Would that be okay?
     
  6. vhman

    vhman

    Joined:
    Aug 13, 2018
    Posts:
    355
    Hi, here some pseudocode how to select new waypoint
    Code (CSharp):
    1. private GameObject[] viewpoints;
    2. waypoints = GameObject.FindGameObjectsWithTag("Sidewalk Objective");          
    3. .....
    4. if (waypoint)
    5. {
    6.   var v2tmp = transform.position - waypoint.transform.position;
    7.   if (v2tmp.sqrMagnitude < TargetTolerance)
    8.   {
    9.     waypoint = null;
    10.   }
    11. }
    12. if (!waypoint)
    13. {
    14.   var len = viewpoints.Length-1;
    15.   var pos = UnityEngine.Random.Range(0, len);
    16.   waypoint = viewpoints[pos];
    17.   Agent.SetDestination(waypoint.transform.position);
    18. }
     
  7. clamum

    clamum

    Joined:
    May 14, 2017
    Posts:
    61
    I dunno if it'll help you or not, but in my game I select random locations on my NavMesh and spawn a bunch of people and they walk around to random destinations, when reaching those destinations they choose another random one and keep doing that. It's not perfectly random but it's pretty decent, IMO.

    Here's some relevant code I have. The ObjectRandomizer is just a free Unity asset that allows you to put a bunch of objects in a list and easily grab random ones.

    Code (CSharp):
    1.  
    2. private List<GameObject> spawnedPeople;
    3. public int TotalNumberOfPeopleToSpawn;
    4.  
    5. void Start()
    6.     {
    7.         spawnedPeople = new List<GameObject>();
    8.  
    9.         SpawnAllPeople();
    10.     }
    11.  
    12. private void SpawnAllPeople()
    13.     {
    14.         ObjectRandomizer objectRandomizer = GetComponent<ObjectRandomizer>();
    15.  
    16.         // spawn all people
    17.         for (int personIndex = 0; personIndex < TotalNumberOfPeopleToSpawn; personIndex += 1)
    18.         {
    19.             // get a game location on the board
    20.             Vector3 randomBoardLocation = GetRandomGameBoardLocation();
    21.  
    22.             // spawn a random person prefab at that location
    23.             GameObject spawnedPerson = SpawnPersonAtLocation(randomBoardLocation, objectRandomizer);
    24.  
    25.             //Debug.Log("Spawned " + spawnedPerson.name + " at " + randomBoardLocation);
    26.          
    27.             // add the spawned person to our collection
    28.             spawnedPeople.Add(spawnedPerson);
    29.         }
    30.     }
    31.  
    32.     /// <summary>
    33.     /// Spawns a random person at the given spawn point.
    34.     /// </summary>
    35.     /// <param name="spawnPosition">The position to spawn the person at.</param>
    36.     /// <param name="objectRandomizer">ObjectRandomizer that spawns people.</param>
    37.     /// <returns>The newly spawned person.</returns>
    38.     private GameObject SpawnPersonAtLocation(Vector3 spawnPosition, ObjectRandomizer objectRandomizer)
    39.     {
    40.         // get a random person prefab to spawn
    41.         GameObject randomPersonPrefab = (GameObject)objectRandomizer.RandomObject();
    42.  
    43.         // spawn the person at the current spawn point
    44.         GameObject personGameObject = Instantiate(randomPersonPrefab, spawnPosition, Quaternion.identity);
    45.  
    46.         Vector3 randomDestination = GetRandomGameBoardLocation();
    47.         Person person = personGameObject.GetComponent<Person>();
    48.         person.CurrentDestination = randomDestination;
    49.  
    50.         Debug.Log("Assigned CurrentDestination to " + person.name);
    51.  
    52.         //Debug.Log("Random dest: " + randomDestination);
    53.      
    54.         personGameObject.name = person.name + "_" + spawnPosition.x + "_" + spawnPosition.y + "_" + spawnPosition.z;
    55.         return personGameObject;
    56.     }
    57.  
    58.     /// <summary>
    59.     /// Selects a random point on the game board (NavMesh).
    60.     /// </summary>
    61.     /// <returns>Vector3 of the random location.</returns>
    62.     private Vector3 GetRandomGameBoardLocation()
    63.     {
    64.         NavMeshTriangulation navMeshData = NavMesh.CalculateTriangulation();
    65.  
    66.         int maxIndices = navMeshData.indices.Length - 3;
    67.  
    68.         // pick the first indice of a random triangle in the nav mesh
    69.         int firstVertexSelected = UnityEngine.Random.Range(0, maxIndices);
    70.         int secondVertexSelected = UnityEngine.Random.Range(0, maxIndices);
    71.  
    72.         // spawn on verticies
    73.         Vector3 point = navMeshData.vertices[navMeshData.indices[firstVertexSelected]];
    74.  
    75.         Vector3 firstVertexPosition = navMeshData.vertices[navMeshData.indices[firstVertexSelected]];
    76.         Vector3 secondVertexPosition = navMeshData.vertices[navMeshData.indices[secondVertexSelected]];
    77.  
    78.         // eliminate points that share a similar X or Z position to stop spawining in square grid line formations
    79.         if ((int)firstVertexPosition.x == (int)secondVertexPosition.x || (int)firstVertexPosition.z == (int)secondVertexPosition.z)
    80.         {
    81.             point = GetRandomGameBoardLocation(); // re-roll a position - I'm not happy with this recursion it could be better
    82.         }
    83.         else
    84.         {
    85.             // select a random point on it
    86.             point = Vector3.Lerp(firstVertexPosition, secondVertexPosition, UnityEngine.Random.Range(0.05f, 0.95f));
    87.         }
    88.  
    89.         return point;
    90.     }
     
  8. vladimirsmart

    vladimirsmart

    Joined:
    Nov 27, 2017
    Posts:
    25
    Thanks
     
  9. radoslawpolasik

    radoslawpolasik

    Joined:
    Jul 24, 2018
    Posts:
    2
    Thank you very much, that's exactly what I was looking for!
     
  10. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    Weighting each triangle by their area should more evenly spread the points, but otherwise It won't get much better than using the mesh triangulation data
     
  11. MarkWaldo

    MarkWaldo

    Joined:
    Dec 1, 2019
    Posts:
    12
    I have a large rolling terrain - 5000 by 5000. I have trees with colliders on them and use a NavMesh. I have a deer having a NavMeshAgent that I would like to roam around this terrain to random locations. Since the terrain has various heights, I don't know how to program it to do this. I have included a method that first checks to see if there is a gameobject at a specific random location before I tell it to move there. I don't know if this will work. I know I need to put the movement into the update method using Time.Deltatime along with the running of the animation. I'm just wondering if this routine will work. I would appreciate any modifications you can suggest.
    void MoveTo()
    {
    xPoint = Random.Range(-2600, 2300);
    zPoint = Random.Range(-5700, 700);
    targetPos = new Vector3(xPoint, 0, zPoint);
    Collider[] intersecting = Physics.OverlapSphere(targetPos, 0.01f);
    if (intersecting.Length == 0)
    {
    // can move to this destination
    navMeshAgent.SetDestination(targetPos);
    }
    }