Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Navmesh agent optimization

Discussion in 'Navigation' started by PhantomProgramming, Mar 25, 2019.

  1. PhantomProgramming

    PhantomProgramming

    Joined:
    Jan 16, 2019
    Posts:
    61
    I've been working with the unity navmesh system and have been trying to get a smooth amount of 1000 navmesh agents all with targets, models, and animations. Sadly my FPS drops insanely low. When I checked the profiler, it said the main issues were scripts and physics.
    Does anybody know how to optimize physics?
    I'm also currently using this as my AI location script.
    Any ideas on how to optimize it?

    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.     public bool moving = false;
    20.     public float extraRotationSpeed;
    21.     public float AI_Delay = 0.1f;
    22.  
    23.  
    24.  
    25.  
    26.  
    27.     // Start is called before the first frame update
    28.     void Start()
    29.     {
    30.         AI_Delay = Random.Range(0.1f, 0.8f);
    31.         myagent.enabled = true;
    32.         sir();
    33.     }
    34.    
    35.  
    36.  
    37.  
    38.     // Update is called once per frame
    39.     void Update()
    40.     {
    41.            
    42.            
    43.  
    44.     }
    45.     void FixedUpdate()
    46.     {
    47.      
    48.         StartCoroutine("DelayEnemy");
    49.        
    50.         //CheckIfInRange();
    51.         myagent.enabled = true;
    52.      
    53.      
    54.     }
    55.  
    56.  
    57.     void sir()
    58.     {
    59.        myTransfom = transform;
    60.        myagent = GetComponent<NavMeshAgent>();
    61.        checkRate = Random.Range(0.2f, 0.5f);
    62.     }
    63.  
    64.     public IEnumerator DelayEnemy() {
    65.  
    66.         yield return new WaitForSeconds(Random.value * AI_Delay);
    67.         if (Time.time > nextCheck && myagent.enabled && myagent.pathPending == false)
    68.         {
    69.             nextCheck = Time.time + checkRate;
    70.  
    71.  
    72.             hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    73.             hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    74.             hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    75.             hitColliders = Physics.OverlapSphere(myTransfom.position, detectionRadius, detectionLayer);
    76.  
    77.             if (hitColliders.Length > 0 && myagent.pathPending == false)
    78.             {
    79.                 myagent.destination = hitColliders[Random.Range(0, hitColliders.Length - 1)].transform.position;
    80.                 //myagent.SetDestination(hitColliders[Random.Range(0, hitColliders.Length - 1)].transform.position);
    81.                 moving = true;
    82.  
    83.             }
    84.             else { moving = false; }
    85.  
    86.  
    87.  
    88.  
    89.  
    90.  
    91.         }
    92.  
    93.     }
    94.  
    95. }
    96.  
     
  2. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    Please provide us with screenshots of your profiler output, we can't really help much without those.
    As for this code, be careful here: you're starting a new DelayEnemy routine per fixedupdate per agent. That is going to add up really quickly. You will probably want to start this coroutine only once per agent and then keep it running indefinitely (explicitly stopping it when the component is disabled or destroyed).
    Simply throw everything in that code in a while(true) loop and you're good to go.
     
  3. PhantomProgramming

    PhantomProgramming

    Joined:
    Jan 16, 2019
    Posts:
    61
    Profilerimage.PNG Should I keep it in the coroutine or move it to update? Right now it's in update.
    I'll provide screenshots later in the day, I don't have time right now.

    EDIT:
    Here's the profiler:
     
    Last edited: Mar 25, 2019
  4. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    You need to do the math on what you are trying to do. Can agents be anywhere? If so then that's potentially 1 million iterations. 1000 agents selecting 1000 agents. Knock of a few if you use something that excludes the agent doing the query. The worst case is your baseline, that's what matters.

    So that's obviously hugely expensive. Even if you spread it out over several seconds it's still going to be a huge hit. You have to partition the data somehow so you are querying for fewer things.

    In a real game you would have some context for what that is. Here I would just make something up, like arbitrary groups.

    Just two groups puts this into more reasonable territory. If you used the C# job system and burst, just doing a naive iteration with a 2d distance check would *probably* work ok. By ok I mean not good enough for an actual game, but good enough as a starting point. On the main thread it's likely to still be problematic.

    OverlapSphere is expensive also. Hugely expensive when you have a high density of colliders. Iterating over a list of all agents would give a far better worst case scenario.