Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Navmesh agents to avoid other agents

Discussion in 'Navigation' started by skinwalker, Oct 16, 2017.

  1. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    311
    Hey,

    I have read a lot of threads on this topic, but didn't find a solution. I am using the NM agent to draw the path and the Character Controller to move with NPC (using the Move method), but the nav mesh agents are not avoiding other nav mesh agents, do you have any idea how to do that?
     
  2. francois85

    francois85

    Joined:
    Aug 11, 2015
    Posts:
    715
    I myself have been trying to find an answer to this question.

    How exactly are you using the Move method?

    A solution I currently have but want to abandon is a custom A* path finding setup. Basically it exposes the f and h costs and allow you to modify it during update. So as my agents move across my nav grid they alter the h and f cost in a given radius. The player agent will then adjust its path according per frame to try and avoid other agents.

    A similar solution with Unity’s nav would be to modify the nave mesh cost with a volume modifier attaches to the other agent and then re bake/ re path per frame. ( I’m a noob at unity navigation so this suggests might not be possible or very expensive)

    Let me know if you find a solution and I’ll let you know what I find. My custom solution is extremely difficult to implement and to maintain hence why I’m looking for something off the shelf.
     
    Levi-Burton likes this.
  3. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
  4. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    311
    I am using something like this, except I forgot that I got rid of the Move function I am using root motion for that

    https://forum.unity.com/threads/using-a-navmeshagent-with-a-charactercontroller.466902/

    My solution for now is to implement a custom AI Behaviour, since my game is open world and there isn't a big chance 2 of the NPCs to walk in the same direction, I just stop one of the NPCs if that happens and disable the navmesh agent and enable navmesh obstacle (Set to carve, only on static = false), the walking NPC avoids the static one and after 3-4 seconds I disable the NM obstacle and enable the NM agent then continue it's walking routine. It's not the best solution, especially for games where you have lots of NPCs in a small area.

    If you have lots of NPCs you can have a charactermanager class that handles figuring out where in proximity other characters are to each other, sadly I don't have the code for that it's just a suggestion a friend of mine uses in his game with lots of NPC walking in a small area.

    I also don't know if the NM agents built in avoidance isn't working for me, because of the character controller or because it's not working at all. My player has a navmesh obstacle component attached and the same NPC avoids the player, but won't avoid other NPCs that only have NM agent.
     
  5. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    THis is very specific to this developers game, i would not recommend OP uses this method. Just use NM obstacles on your agents and itll sort your problem out.
     
  6. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    311
    You can't have NM obstacle and NM agent active at the same time. Adding NavMeshObstacles to my scene I had weird things with agents walking in circles or just walking into a wall over and over.
     
  7. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    Yes but your not using the agent constantly every frame are you? Very inefficient.

    Just use the agent to get a path, store that path, calculate any direction changes along that path.

    Now move the agent along it with the navmesh agent TURNED OFF. and turn the obtacle on.

    Effectively you only use the agent for 1 frame at a time.

    I do this even when not using obstacles as NM agents are very heavy.

    C# job system coming soon will rectify this.

    Also try calculating the directional turns in the path asynchronously for better performance.

    EDIT: to make more clear, effectively you just use the agent to calculate pathing. But you actually do all the moving, and facing direction yourself using the precalculated path. Using this method you can have 10000+ dynamic agents.
     
    Lucid-Amusement and esbenrb like this.
  8. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    311
    Any examples doing that or can you setup a small repo?
     
    GameDevCouple_I likes this.
  9. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    I'm at work at the moment but i can definately setup a small public bitbucket repo when I get home.

    The code for working out turns along a path is generic and can be found on a variety of places, unity specific and not. I think there is even one on the unity wiki (if the wiki is not down again)
     
  10. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    here you go :)

    https://docs.unity3d.com/ScriptReference/AI.NavMeshPath-corners.html

    use that code to work out where all the turns are. Then use basic direction vector math to get direction to first "waypoint" (corner) and then each time you get to the next corner, change direction.

    Very very very simply once you think about it, and means you can turn off the agents except at path generation.

    If your still struggling ill put a repo up this evening when im home :)

    EDIT:
    NavMeshPath.GetCornersNonAlloc for a non allocating version
     
  11. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    311
    Any idea about the performance on large terrains and multiple NPCs? I've heard bad things about having an obstacle that has Carve and is not set to carve only on stationary, basically its always making holes in the navmesh as it's moving.
     
  12. Modus_Pwnens

    Modus_Pwnens

    Joined:
    Oct 24, 2017
    Posts:
    4
    If you are still looking at this Daemonhahn, I would very much like an example of how to implement this. I understand conceptually, but I am too new to Unity to have a good idea of how to go about an implementation. Thanks!
     
    GameDevCouple_I likes this.
  13. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    Hey, I am at work at the moment, but will try and write you a snippet when I am free :)

    EDIT:

    Code (CSharp):
    1.   Public NavMeshPath pathToUse;
    2.     public Queue<Vector3> cornerQueue;
    3.     public Vector3 currentDestination;
    4.     bool hasPath;
    5.     public float currentDistance;
    6.     public float minDistanceArrived;
    7.     Vector3 direction;
    8.     public float moveSpeed;
    9.  
    10.     void /// <summary>
    11.     /// Start is called on the frame when a script is enabled just before
    12.     /// any of the Update methods is called the first time.
    13.     /// </summary>
    14.     void Start()
    15.     {
    16.         SetupPath(pathToUse);
    17.     }
    18.  
    19.     // get the corners and add them to a queue for use to use
    20.     void SetupPath(NavMeshPath path)
    21.     {
    22.         cornerQueue = new Queue<Vector3>();
    23.         foreach(Vector3 corner in path.corners)
    24.         {
    25.             cornersList.Enqueue(corner);
    26.         }
    27.  
    28.         GetNextCorner();
    29.         currentDistance = (transform.position - currentDestination).sqrMagnitude;
    30.         hasPath = true;
    31.     }
    32.  
    33.     // get the next corner, and set direction
    34.     void GetNextCorner()
    35.     {
    36.         if(cornerQueue.Length > 0)
    37.         {
    38. currentDestination = cornerQueue.Dequeue();
    39.         previousCorner = previousCorner;
    40.         direction = transform.position - currentDestination;
    41.          hasPath = true;
    42.         }
    43.         else
    44.         {
    45.             hasPath = false;
    46.         }
    47.     }
    48.  
    49.     // move towards the point
    50.     void MoveAlongPath()
    51.     {
    52.         if(hasPath)
    53.         {
    54.             currentDistance = (transform.position - currentDestination).sqrMagnitude;
    55.  
    56.             if(currentDistance > minDistanceArrived)
    57.             {
    58.            
    59.                 transform.position +=  direction  * moveSpeed * Time.deltaTime;
    60.             }
    61.             else
    62.             {
    63.                 GetNextCorner();
    64.             }
    65.         }
    66.     }
    67.  
    68.  
    69.     // Update the agent
    70.     void Update()
    71.     {
    72.         MoveAlongPath();
    73.     }
    That code is entirely untested but should work with little ammendments!

    You set a minimum arrived distance to set how far a agent goes until it stops. You also need to set the movespeed and provide a navmesh path, but that will do the basics of what I was saying!

    EDIT2: So what ive written will work out the path and move a navmesh agent along a path. Then all you need to do is either raycast from each agent towards their current destination and if these rays hit each other you know that agents will collide!

    Or you could go through that queue (or use a list instead) of waypoints (corners) and fire rays Along each one (or place colliders) and then if theres collisions you know the paths intersect. theres literally so many ways to check intersections between lines, which when you boil it down is what your doing at this stage :)

    That should get you started, message me if you still cant work out the rest.

    EDIT3:
    you could also use the physics api to do casting/ overlap checks, https://docs.unity3d.com/ScriptReference/Physics.html

    EDIT4: https://answers.unity.com/questions/1356936/navmeshpathcornerslength-is-always-0.html this shows a api call that will let you calculate the path without using an actual agent too :)
     
    Last edited: Nov 6, 2017
    StanK_ and obsilp like this.
  14. w4der

    w4der

    Joined:
    Sep 17, 2011
    Posts:
    12
    Hi,
    There are errors in your code.
    Could you help me?
     
  15. StanK_

    StanK_

    Joined:
    Dec 5, 2017
    Posts:
    6
    Hey!
    I've just edited his code for myself, it works:

    Code (CSharp):
    1. private NavMeshPath _path;
    2. private bool _hasPath;
    3. private NavMeshAgent _agent;
    4. private Queue<Vector3> _cornerQueue;
    5. private Vector3 _currentDestination;
    6. private Vector3 _direction;
    7. private float _currentDistance;
    8.  
    9. void OnEnable ()
    10. {
    11.     InitVars();
    12.     CalculateNavMesh();
    13.  
    14.     SetupPath(_path);  
    15. }
    16.  
    17. private void CalculateNavMesh()
    18. {
    19.     _agent.CalculatePath(_targetPoint, _path);
    20. }
    21.  
    22. private void InitVars()
    23. {
    24.     _targetPoint = GameObject.Find("EndPoint").transform.position; // Set target point here
    25.     _agent = GetComponent<NavMeshAgent>();
    26.     _path = new NavMeshPath();
    27. }
    28.  
    29. void SetupPath(NavMeshPath path)
    30. {
    31.     _cornerQueue = new Queue<Vector3>();
    32.     foreach (var corner in path.corners)
    33.     {
    34.         _cornerQueue.Enqueue(corner);
    35.     }
    36.  
    37.     GetNextCorner();
    38.     _hasPath = true;
    39. }
    40.  
    41. private void GetNextCorner()
    42. {
    43.     if (_cornerQueue.Count > 0)
    44.     {
    45.         _currentDestination = _cornerQueue.Dequeue();
    46.         _direction = _currentDestination - transform.position;
    47.         _hasPath = true;
    48.     }
    49.     else
    50.     {
    51.         _hasPath = false;
    52.     }
    53. }
    54.  
    55. void FixedUpdate()
    56. {
    57.     MoveAlongPath();
    58. }
    59.  
    60. private void MoveAlongPath()
    61. {
    62.     if (_hasPath)
    63.     {
    64.         _currentDistance = Calc.GetSqrDistLinear(transform.position, _currentDestination);
    65.  
    66.         if (_currentDistance > 1)
    67.             transform.position += _direction * 0.4f * Time.deltaTime;
    68.         else
    69.             GetNextCorner();
    70.     }
    71. }
     
    CrandellWS and GameDevCouple_I like this.
  16. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    Thanks, yeah i was at work so it was written off top of my head without an IDE or looking at API ;)
     
  17. Modus_Pwnens

    Modus_Pwnens

    Joined:
    Oct 24, 2017
    Posts:
    4

    LOL. Thank you very much for the thorough reply. It has been a while since I posted this question. I actually ended up deep diving into pathing and wrote my own navigation system using A* plus boid flocking.
     
    GameDevCouple_I likes this.
  18. GameDevCouple_I

    GameDevCouple_I

    Joined:
    Oct 5, 2013
    Posts:
    2,126
    Glad you managed to get a solution up and running! :)
     
  19. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    637
    @GameDevCouple_I Isn't the idea of having NavMeshObstacles on agents dangerous when it comes to raycasting on the navmesh? You're going to create a lot of holes throughout the navmesh and the raycasts you will then do to find valid positions on the navmesh will be incorrect, right?
     
  20. vhman

    vhman

    Joined:
    Aug 13, 2018
    Posts:
    97
    Don't do necromancy)))

    https://docs.unity3d.com/Manual/nav-MixingComponents.html

    NavMesh Agent and NavMesh Obstacle
    • Do not mix well!
      • Enabling both will make the agent trying to avoid itself
      • If carving is enabled in addition, the agent tries to constantly remap to the edge of the carved hole, even more erroneous behavior ensues
    • Make sure only one of them are active at any given time
      • Deceased state, you may turn off the agent and turn on the obstacle to force other agents to avoid it
      • Alternatively you can use priorities to make certain agents to be avoided more
     
  21. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    637
    I don't see how that answers my question. Are you saying you think it's correct that it's dangerous? If so, that still doesn't answer the question of how to make agents avoid each other.
    In the 2018 documentation it already said that they don't mix well together, but it might not be true in the specific case GameDevCouple_I mentioned.