Search Unity

NavMeshPath.GetCornersNonAlloc() is confusing

Discussion in 'Navigation' started by xVergilx, Aug 23, 2018.

  1. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    There's no way to tell from the start what size of the array is required. And testing via the .GetCornersNonAlloc() aren't convenient, since only the points that are fitted into the array are returned.

    Path with 4 points & array size of 2 will return 2 instead of 4.

    E.g. to resize an array you would have to constantly call it to test if the count was reached.

    Code (CSharp):
    1. _pathPointsCount = _path.GetCornersNonAlloc(_pathPoints);
    2.          
    3. // Resize the points array to fit everything if the array is too small and try to fit the points again
    4. while (_pathPoints.Length <= _pathPointsCount) {
    5.       Array.Resize(ref _pathPoints, _pathPoints.Length * 2);
    6.       _pathPointsCount = _path.GetCornersNonAlloc(_pathPoints);
    7. }
    NavMeshPath is missing something like NavMeshPath.CornersCount to properly test against, without constant interop testing.
     
  2. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Bump, just went googling again, got back to my own old post. Still no API.
     
  3. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    You are supposed to allocate an array large enough to fit the largest possible path. Then just iterate over the values that were set this call using the length returned.

    The whole point of this api is to not have to allocate every call. Resizing up would allocate. Resizing period would hurt performance.
     
  4. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Making it to accept list as a buffer is a better solution than using any array param.

    Just saying.

    Also, giving an actual count of how many there's points would allow to allocate more precise amount of space required.
    Also, allocating large arrays is not a solution. In some edge cases it can backfire badly.
     
    noio likes this.
  5. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Pre allocating is the right solution, and array is the right choice, it reflects the fact that you allocate a set size once.

    What you do think is a better solution that meets the goal? There isn't one short of using unmanaged memory.
     
  6. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    A couple of solutions:
    1. Make a list buffer instead to allocate exactly how much memory is needed.
    This will also make it less a pain in the ass to add additional points somewhere along the path, and apply modifiers.

    This is what Graphics API did with .DrawMeshInstanced by the way.

    2. Or expose count api to determine initial size, so it can be more or less accurately allocated (E.g. *2 or similar).
    3. Unsafe code. Because why not?
    4. Moving to managed side. Because DOTS.

    Unity's NavMesh API is way beyond obsolete state.
    It's so user-friendly that almost screams "A*" somewhere along the lines of manual.
     
    Last edited: Apr 15, 2019
  7. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Other api's work with different data and different needs. The cost of insert/remove might not be a big deal with a render list where maybe it only changes occasionally. It could easily be the more performant option then creating a new list.

    With the rendering stuff the list is the concern of the api. With pathfinding what you want to add to it for your own needs is not. If Unity had some api that spit out a list of things rendered, that it determined, I'm pretty sure it would be an array.
     
  8. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Graphics API supports both list and array overloads. They've initially had only array, and added list later on.
    I'm using that for drawing lots of decal lists. That do change often, and it works out great.

    Yeah, whatever.

    Problem is, if that data structure helped them to be more performant I'd be okay with it.
    But I think laziness and lack of resources is the actual cause. Because it isn't even as performant as similar solutions on the store. There's to this day not even an overload to submit a path for calculation in an async way.
    (And no, using NavMeshAgent is a workaround, not a solution)

    Keep defending it, I don't care at this point.

    You've helped me remind how many workarounds it requires for Unity's Navigation to actually work.
    So, I think I'll just bite the bullet and transition this project to A* Pathfinding as well.
     
  9. hottabych

    hottabych

    Joined:
    Apr 18, 2015
    Posts:
    107
    This method confused me too.
    Okay, it returns an array of Vector3's and... what to do next? I can't assign it to corners property directly because it is read-only. Does it copies result to corners automatically? Or does it reference to this array in some way and use it instead of corners property? Manual lacks of information.
    My goal is to calculate waypoints in async Task and to assign it to the new path in a main thread. Seems like this method is designed for parallel execution, but it's unclear how to use it properly.
    NavMeshPath also has a mysterious constructor that is not documented too.
     
    Last edited: May 12, 2019
  10. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Just create an array that can hold all points like Vector3[1000] and feed it to the method.
    It will fill points from the previously calculated path and return how many points it has filled.

    Unfortunately, you can't use it in non-main thread, because you can't by design.
    And to caculate path you need to use Path API. This one just retrieves the result from the Path class.

    So, if you want to do async path calculation, look into NavMeshAgent - turn on, calculate path, turn off - workaround.
    (Or just dump it, and use A* Pathfinding Project instead, its way better than Unity's Navigation)
     
    hottabych likes this.
  11. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    232
    Opinions on the API in general aside, I agree with @xVergilx that being able to get
    CornerCount
    is a no-brainer.

    Regardless of whether you want to allocate only once, or re-allocate later, it's essential to know whether you got all the resulting points or not. Who is to say whether
    Vector3[1000]
    is even enough?! What if you then get 1000 points back? Re-allocate just to be sure? Getting the corner count in advance at least lets the developer make this decision.