Search Unity

NavMesh.CalculatePath & NavMeshAgent.CalculatePath return not full path

Discussion in 'Navigation' started by Garrettec, Jul 17, 2015.

  1. Garrettec

    Garrettec

    Joined:
    Nov 26, 2012
    Posts:
    98
    I was performing several NavMesh performance tests and occasionally found out that in case of long paths both mentioned functions (NavMesh.CalculatePath & NavMeshAgent.CalculatePath) return only part of the path. Here is illustration with explanation:

    NavMesh  Playground.png
    this terrain is 2x2 kilometres
    green sphere is start point of the path
    orange sphere in top right corner is destination point
    magenta cube is NavMeshAgent
    green multi-line is the path which NavMesh.CalculatePath returned

    I have tested how will NavMeshAgent behave in such cases:
    When Agent riches the end of that "half path" it stops. To make it move to the destination point I have to call NavMeshAgent.ResetPath and then SetDestination again. After this Agent find the path to the destination and reach it.

    When this happens - path parameters could be completely different (17 corners, 26 corners, 1180 meters, 1934 meters etc.)

    So, is it intentional behaviour or a bug?
    If it is intentional, then why it is not documented? (at least I didn't find anything about such behaviour)
     
    Last edited: Jul 17, 2015
  2. ya-twister

    ya-twister

    Joined:
    Mar 23, 2014
    Posts:
    1
    I also have a problem with that!!!
     
  3. EricNumeri4D

    EricNumeri4D

    Joined:
    Sep 24, 2013
    Posts:
    4
    Hello,

    Same problem here, all NavMeshPath returned by "NavMesh.CalculatePath" have 26 corners max...We (the team) are very stuck with this. Did you find a solution or an answer ?

    Thanks :)
     
  4. EricNumeri4D

    EricNumeri4D

    Joined:
    Sep 24, 2013
    Posts:
    4
    Nouvel élément : En changeant les paramètres du Baking, la longueur change mais une erreur survient toujours, le "NavMesh.CalculatePath" retourne un "PathPartial" en status...Je continue mes recherches.
     
  5. Garrettec

    Garrettec

    Joined:
    Nov 26, 2012
    Posts:
    98
    Hello, Eric

    I din't really search for solution, because it was not a critical problem for us in that moment and I still haven't lost face in Unity answering my question.

    As for your problem... When I contemplated what we can possibly do if we will require to calculate paths through whole location, I have several thoughts in mind:
    - check if your agent is (not moving && !NavMeshAgent.path.status.PathComplete && still not in the destination point)
    if all this is true then call NavMeshAgent.ResetPath and then SetDestination again.
    - another option is to use some kind of checkpoints, and search path between them. Instead of searching for the whole path at once
     
    Mycroft likes this.
  6. Andhil

    Andhil

    Joined:
    Apr 15, 2018
    Posts:
    13
    Hi everybody,
    Same problem here for me in 2019... The Navmesh.CalculatePath() Always return as partial with various number of corners and length … (35 corners max for me …)

    I'm working with a NavmeshSurface component, not with the built-n NavMesh system.

    Someone has an idea ? I need to precompute long navmeshpaths ...

    Thks
     
    Last edited: Sep 30, 2019
  7. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    I had the same problem, checkout this workaround and let me know if you have any questions. You might need to change the constant values depending on the size of your navmesh. For performance reason you might also put this into a coroutine, depending on the size of your terrain.

    Code (CSharp):
    1.  
    2. private const int MAX_PATH_DISTANCE = 30;
    3. private const int MAX_CORNERS = 512;
    4. private const int MAX_TRIES = 3;
    5. private static Vector3[] m_pathCorners = new Vector3[MAX_CORNERS];
    6.  
    7. public static int CalculatePath(Vector3 _start, Vector3 _target, NavMeshQueryFilter _filter, Vector3[] _path, int _startIndex = 0, int _numTries = 0)
    8. {
    9.     if (_numTries > MAX_TRIES || _startIndex >= m_pathCorners.Length)
    10.         return -1;
    11.  
    12.     var startPosOk = GetNavMeshPosition(_start, _filter.areaMask, MAX_PATH_DISTANCE, out Vector3 startPos);
    13.     var endPosOk = GetNavMeshPosition(_target, _filter.areaMask, MAX_PATH_DISTANCE, out Vector3 targetPos);
    14.  
    15.     // this can happen when the navmesh is still being backed or positions are outside the navmesh -> maybe increase MAX_PATH_DISTANCE
    16.     if (!startPosOk || !endPosOk)
    17.         return -1;
    18.  
    19.     NavMesh.CalculatePath(startPos, targetPos, _filter, m_navMeshPath);
    20.  
    21.     switch (m_navMeshPath.status)
    22.     {
    23.         case NavMeshPathStatus.PathPartial:
    24.         {
    25.             var numCorners = m_navMeshPath.GetCornersNonAlloc(m_pathCorners);
    26.             Array.Copy(m_pathCorners, 0, _path, _startIndex, numCorners);
    27.             var lastPos = m_navMeshPath.corners[m_navMeshPath.corners.Length - 1];
    28.             if (Math.Abs(lastPos.x - targetPos.x) < 0.5f && Math.Abs(lastPos.z - targetPos.z) < 0.5f)
    29.                 return _startIndex + numCorners;
    30.  
    31.             return CalculatePath(lastPos, _target, _filter, _path, _startIndex + numCorners, ++_numTries);
    32.         }
    33.         case NavMeshPathStatus.PathComplete:
    34.         {
    35.             var numCorners = m_navMeshPath.GetCornersNonAlloc(m_pathCorners);
    36.             Array.Copy(m_pathCorners, 0, _path, _startIndex, numCorners);
    37.             return _startIndex + numCorners;
    38.         }
    39.         default:
    40.             return -1;
    41.     }
    42. }
    43.  
    44.  
     
    Last edited: Oct 2, 2019
    heartingNinja and wildframemedia like this.
  8. OMGOMGXAXA

    OMGOMGXAXA

    Joined:
    Apr 2, 2016
    Posts:
    10
    It would be nice if someone from Unity would take the time to look at where this maximum distance comes from. And explain to us what it is based on? I've been looking for a way to increase it for 2 days without success, because I don't have access to the source code :/
     
    heartingNinja likes this.
  9. heartingNinja

    heartingNinja

    Joined:
    Mar 26, 2015
    Posts:
    35
    DwinTeimlon post didn't have the code for GetNavMeshPosition so I have added what I used. Also this part NavMesh.CalculatePath(startPos, targetPos, _filter, m_navMeshPath); should be _filter.areaMask or the path seems to not care about area cost.
    Code (CSharp):
    1.  bool GetNavMeshPosition(Vector3 _position, int _areaMask, float _maxDistance, out Vector3 _navMeshPosition)
    2.     {      
    3.         NavMeshHit hit;
    4.  
    5.         // Sample points around the input position to find the nearest valid NavMesh point
    6.         if (NavMesh.SamplePosition(_position, out hit, _maxDistance, _areaMask))
    7.         {
    8.             _navMeshPosition = hit.position;
    9.             return true;
    10.         }
    11.  
    12.         _navMeshPosition = Vector3.zero; // Or some default value
    13.         return false;
    14.     }
    Code (CSharp):
    1. int CalculatePath(Vector3 _start, Vector3 _target, NavMeshQueryFilter _filter, Vector3[] _path, int _startIndex = 0, int _numTries = 0)
    2.     {
    3.         Vector3[] m_pathCorners = new Vector3[MAX_CORNERS];
    4.         NavMeshPath m_navMeshPath = new NavMeshPath(); // You need to initialize this NavMeshPath
    5.  
    6.         if (_numTries > MAX_TRIES || _startIndex >= m_pathCorners.Length)
    7.             return -1;
    8.  
    9.         var startPosOk = GetNavMeshPosition(_start, _filter.areaMask, MAX_PATH_DISTANCE, out Vector3 startPos);
    10.         var endPosOk = GetNavMeshPosition(_target, _filter.areaMask, MAX_PATH_DISTANCE, out Vector3 targetPos);
    11.  
    12.         if (!startPosOk || !endPosOk)
    13.             return -1;
    14.  
    15.         NavMesh.CalculatePath(startPos, targetPos, _filter.areaMask, m_navMeshPath);
    16.  
    17.         switch (m_navMeshPath.status)
    18.         {
    19.             case NavMeshPathStatus.PathPartial:
    20.                 {
    21.                     var numCorners = m_navMeshPath.GetCornersNonAlloc(m_pathCorners);
    22.                     Array.Copy(m_pathCorners, 0, _path, _startIndex, numCorners);
    23.                     var lastPos = m_navMeshPath.corners[m_navMeshPath.corners.Length - 1];
    24.                     if (Mathf.Abs(lastPos.x - targetPos.x) < 0.5f && Mathf.Abs(lastPos.z - targetPos.z) < 0.5f)
    25.                         return _startIndex + numCorners;
    26.  
    27.                     return CalculatePath(lastPos, _target, _filter, _path, _startIndex + numCorners, ++_numTries);
    28.                 }
    29.             case NavMeshPathStatus.PathComplete:
    30.                 {
    31.                     var numCorners = m_navMeshPath.GetCornersNonAlloc(m_pathCorners);
    32.                     Array.Copy(m_pathCorners, 0, _path, _startIndex, numCorners);
    33.                     return _startIndex + numCorners;
    34.                 }
    35.             default:
    36.                 return -1;
    37.         }
    38.     }
    Then how I used it to draw a line render for a long distance.

    Code (CSharp):
    1.  private IEnumerator DrawpathtoCheckpoint()
    2.     {
    3.         WaitForSeconds Wait = new WaitForSeconds(PathUpdateSpeed);    
    4.  
    5.         bool destinationReached = false;
    6.  
    7.         while (nearestOBJ != null && !destinationReached)
    8.         {
    9.             totalDistance = 0;
    10.             NavMeshQueryFilter filter = new NavMeshQueryFilter();
    11.             filter.areaMask = NavMesh.GetAreaFromName("Road") | NavMesh.GetAreaFromName("Terrain");
    12.  
    13.             Vector3[] pathCorners = new Vector3[MAX_CORNERS];
    14.  
    15.             //using the code from DwinTeimlon
    16.             int pathCornerCount = CalculatePath(Player.position, nearestOBJ.transform.position, filter, pathCorners);
    17.  
    18.             if (pathCornerCount > 0)
    19.             {
    20.                 Path.positionCount = pathCornerCount;
    21.  
    22.                 for (int i = 0; i < pathCornerCount; i++)
    23.                 {
    24.                     Path.SetPosition(i, pathCorners[i] + Vector3.up * PathHeightOffset);
    25.  
    26.                     if (i > 0)
    27.                     {
    28.                         totalDistance += Vector3.Distance(pathCorners[i - 1], pathCorners[i]);
    29.                     }
    30.                 }
    31.  
    32.                 if (Vector3.Distance(pathCorners[pathCornerCount - 1], nearestOBJ.transform.position) <= 0.1f)
    33.                 {
    34.                     destinationReached = true;
    35.  
    36.                     // Set the destination to the last point on the calculated path
    37.                     Vector3 lastPoint = pathCorners[pathCornerCount - 1];
    38.                     nearestOBJ.transform.position = lastPoint;
    39.                 }
    40.  
    41.                 // Display the total distance
    42.                 distanceToGoal.text = totalDistance.ToString("F1") + "M";
    43.             }
    44.             else
    45.             {
    46.                 Debug.LogError($"Unable to calculate a path on the NavMesh between {Player.position} and {nearestOBJ.transform.position}.");
    47.             }
    48.  
    49.             yield return Wait;
    50.         }
    51.     }
     
    DwinTeimlon likes this.