Search Unity

NavMeshAgent locating more than one target [Issue]

Discussion in 'Navigation' started by PhantomProgramming, Jan 23, 2019.

  1. PhantomProgramming

    PhantomProgramming

    Joined:
    Jan 16, 2019
    Posts:
    61
    I have two different teams of knights fighting each other using navmesh, what I need to figure out is how to add more than one target to my array, here's my AILocate script, please use C# when explaining.

    Thanks, PhantomProgramming



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.AI;
    5.  
    6.  
    7.  
    8. public class AILocate : MonoBehaviour
    9. {
    10.  
    11.  
    12.     public LayerMask detectionLayer;
    13.     private Transform myTransfom;
    14.     public NavMeshAgent myagent;
    15.     private Collider[] hitColliders;
    16.         private float checkRate;
    17.         private float nextCheck;
    18.         public float detectionRadius = 180;
    19.  
    20.  
    21.  
    22.  
    23.  
    24.  
    25.  
    26.     // Start is called before the first frame update
    27.     void Start()
    28.     {
    29.             sir();
    30.     }
    31.  
    32.     // Update is called once per frame
    33.     void Update()
    34.     {
    35.             CheckIfInRange();
    36.             myagent.enabled = true;
    37.        
    38.     }
    39.  
    40.         void sir()
    41.         {
    42.             myTransfom = transform;
    43.         myagent = GetComponent<NavMeshAgent>();
    44.             checkRate = Random.Range(0.8f, 1.2f);
    45.         }
    46.  
    47.         void CheckIfInRange()
    48.         {
    49.             if (Time.time > nextCheck && myagent.enabled == true)
    50.             {
    51.                 nextCheck = Time.time + checkRate;
    52.  
    53.  
    54.                 hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    55.              
    56.  
    57.  
    58.             if (hitColliders.Length > 0)
    59.                 {
    60.                     myagent.SetDestination(hitColliders[0].transform.position);
    61.                 }
    62.  
    63.             }
    64.         }
    65.  
    66.  
    67. }
    68.  
     
  2. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    You are setting multiple targets to the array already. Unless you mean you want multiple destinations? In that case I'll have to disappoint you, Unity's NavMeshAgents can have only 1 destination at a time. You would have to write your own logic to handle going from target to target in a sequence.
    On a sidenote, I believe this script is going to give you a lot of annoying behaviour. First of all you're continuously enabling your agent, that's unnecessary. Just enable your agents on startup.
    More importantly, if you keep checking for new targets all the time your agent will probably change its target while still going to another target. It would be a lot better to only check for new goals when your agent does not already have one.
    To do this, I would simply save the target collider in a variable once you find the one you want to move to.
    Then, calculate a path to this target and update this path every few ticks to ensure you'll eventually end up there in case of a moving target (basically chasing someone).
    Then when you reach the target (you can check this with agent.RemainingDistance), do whatever you want (fighting, shaking hands), and THEN look for a new target (potentially excluding the target you already had).
     
  3. PhantomProgramming

    PhantomProgramming

    Joined:
    Jan 16, 2019
    Posts:
    61
    Hmm, could I make the target more random? As of right now, two armies are converging on the same enemy everytime their new target get's made. Or could I do the closest target?

    Thanks for the tip on the enabling though. ;)
     
  4. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    Right now you always set your destination as the 1st collider you find within your sphere. I am not entirely sure how the resulting array from OverlapSphere is sorted, but they might be sorted along the order of your objects in the scene hierarchy.
    To work around this, either just pick a random number between 0 and the length of the array of colliders, or iterate through the array and check the distance between the agent and the collider, remember the shortest distance and the corresponding index, and then set that one to be the target.
     
  5. PhantomProgramming

    PhantomProgramming

    Joined:
    Jan 16, 2019
    Posts:
    61
    Okay, so I tried this, but I can't tell if there was any change, does this look right?

    Code (CSharp):
    1. void CheckIfInRange()
    2.         {
    3.             if (Time.time > nextCheck && myagent.enabled == true)
    4.             {
    5.                 nextCheck = Time.time + checkRate;
    6.  
    7.  
    8.                 hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    9.              
    10.  
    11.  
    12.                 if (hitColliders.Length > 0)
    13.                 {
    14.                 myagent.SetDestination(hitColliders[Random.Range(0, hitColliders.Length - 1)].transform.position);
    15.                     moving = true;
    16.  
    17.             }
    18.             else { moving = false; }
    19.  
    20.             }
    21.         }
     
  6. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    That should normally result in your agents picking different targets, yes.
    So how exactly do the agents behave now? Please provide a little more detail, perhaps a short video would help as well.
     
  7. PhantomProgramming

    PhantomProgramming

    Joined:
    Jan 16, 2019
    Posts:
    61
    So, they seem to have a couple that chase a different target which is good, but not many as I would like, should I just make more of these:

    Code (CSharp):
    1. hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    Or would that cause memory leak?
     
  8. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    No that's not going to do anything, nor cause memory leaks (C# does garbage collection, that specifically makes creating leaks impossible in that language because all deallocations will eventually get collected and cleaned up). Making more of those will simply result in getting the same list over and over again, provided you don't change anything like the sphere's radius or center.
    You might simply consider making a bigger radius for your sphere and see if that works.
    Keep in mind that you're still continuously updating the agents' targets, that might actually make your agents seem to walk to the same target since they're swapping all the time, which could potentially cause clumping behaviour. Try and call the checkinrange method only once first.
     
  9. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    To reduce memory leaks you could also use
    Physics.OverlapSphereNonAlloc 
    instead.

    Also in regards to your problem. I am currently making a rts game and I have tried different solutions to your problem. This is not a trivial problem to solve. If you want to make two armies fighting look good you have a lot of work ahead of you.

    What I did to work around the flaws of the unity pathfinding system (a proper way of surrounding a target e.g. like in starcraft does not work with Unity navmesh) is that I introduced attack offsets for any enemy on the field. The bigger the unit the more attack offsets it has. This does also work for buildings. Attackers could claim those offsets and release them if they are actually fighting somebody else. This also helps to mitigate the problem of 20 units attacking a single unit at the same time. If they did not find a proper target, they would look for a random position at the target they orginally wanted to attack and walk there and wait for a target they can go to.

    The problem is that those attackers claming the position also need to get to that target, which is sometimes hard in crowded situations. So if they are not getting to the position they claimed in a certain amount of time they release it again and search for something closer/better.

    You should also let the other unit know that it is currently attacked by somebody, so it can react accordingly and not just run towards the position it was actually looking for.

    What I additionaly have in place is a radar system, as I have a lot of units those radarsystems are only avaiable to a bunch of units which normally stick together. With a radar system in place you can make a lot of easy adjustments what a unit should attack or not.