Search Unity

NavMeshManager Performance Issue

Discussion in 'Navigation' started by juffowup, Feb 6, 2015.

  1. juffowup

    juffowup

    Joined:
    May 10, 2014
    Posts:
    7
    I've got 250 game objects using a NavMesh to get around. I noticed something odd in the profiler for the NavMeshManager. There's a step Components.NavMeshAgent.TransformChangedMessage:

    If I put a NavMeshAgent on a simple 3D capsule with no collider this step accounts for around 6% of the scene calculation.

    If I replace the 3D capsule with a human mesh, the framerate tanks and the step above jumps to around 30%. This remains the case even if I disable the human mesh. It can ride around not even being displayed and still has this effect.

    What in the world is going on here? Why would performance tank like this just from switching out the mesh? I expect a bit more time on the render to display the human mesh instead of a capsul. But why is there an impact on the NavMeshManager?
     
  2. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Is the mesh really all you're changing, or does the human character have child transforms (e.g. for bones)? If the latter, you can try turning on the 'Optimise Game Objects' setting in the importer for the character.

    Also, 6% and 30% of what total scene time? 1ms? 10ms? 100ms? If it's 30% of 1ms then I don't think you need to worry...
     
    laurentlavigne likes this.
  3. juffowup

    juffowup

    Joined:
    May 10, 2014
    Posts:
    7
    You nailed it with the superpig, the mesh was the issue. It was fully rigged for bones, and legacy to boot.
     
  4. djarcas

    djarcas

    Joined:
    Nov 15, 2012
    Posts:
    246
    I'm a little confused on this - are you saying that if we have a more complicated animated character, it somehow causes more CPU usage on the AI? Why are these interlinked?
     
  5. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    It's not AI-specific, but in this setup, the AI moves the character. The more complex the character, the long it takes for anything (be it AI, physics, script code, etc) to move it.
     
    radiantboy likes this.
  6. djarcas

    djarcas

    Joined:
    Nov 15, 2012
    Posts:
    246
    Ah, brilliant, thank you for a clear answer.

    Is it more expensive to move things via the navmesh agent, or simply cost I'm not used to seeing? (The thought occurs that I could get the navmesh agent to move only itself around, and simply duplicate its transform to the complex object, but only when on screen)
     
  7. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    It's not inherently more expensive to move things via the navmesh agent, no (beyond the navmesh agent needing to do actual movement calculations - but something has to do those calculations).

    Your suggested approach of having the actual character 'shadow' the Navmesh Agent when on screen is a reasonable one I think. (In fact, I've worked on something for Unity 5.2 that is relevant to this exact scenario...)
     
  8. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    Can you talk more about this thing you're working on in v5.2 @superpig ? I see the v5.2 beta just came out - Is it available in there yet?

    I ask because I've been stress testing the navmesh system recently and I'm running into an issue here where the navmesh Manager is taking up approx. 1.5ms CPU time with 100 agents when they're all completely stationary (2ms if agents request paths every 5sec). I would have expected the Navmesh Manager CPU time drop close to 0ms when there are no active pathing or steering jobs.

    However getting back to the original topic - It would be good to know if we aught to implement a form of agent culling that @djarcas suggests, or whether something in v5.2 intends to resolve the problem.
     
  9. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    The thing in 5.2 I was referring to is the CullingGroup API. If you use it, you would still need to implement agent culling, but you'd be able to implement it on top of a more efficient foundation - just handling the agent spawn/despawn yourself, while letting the engine tell you when you should do it.

    The NavmeshManager still does a few tasks even when agents are not moving, like updating carving status and checking for offmeshlink updates. The navmesh team are making major changes in this area at the moment so I'm not sure it makes a lot of sense for them to invest much time in optimizing the current implementation, but I'll see if we can at least get a bit more information in as to exactly what's taking up the time.
     
  10. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    374
    Interesting stuff. Thanks for the link @superpig. This is available in the v5.2 beta now?
     
  11. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
  12. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    I got a similar issue, only I test on mobile. So if I have understood it correctly, you should copy the transform of the navmesh agent to a more complex object whenever the navmesh is onscreen?

    The problem now is that it takes around 16ms with 100 agents, with obstacle avoidance of low quality.
     
  13. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    @Rusfighter: do you actually have all 100 agents on screen at the same time?
     
  14. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    Max 50, but the other 50 that aren't on screen should walk towards the player and should have navmesh agent enabled...

    The idea of my game is close to the https://www.assetstore.unity3d.com/en/#!/content/40756

    Is there a efficient way for mobile to have some simple pathfinding when offscreen? Or should i just have a empty navmesh object always running when offscreen and enable the complex object (with colliders and rigs) when the navmesh agent moves into screen?
     
  15. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Disabling everything except the NavmeshAgent when the enemy is off-screen ought to help for sure.

    But I think that you can probably make things even cheaper. You don't really need a NavmeshAgent for something that is off-screen; all you really need is a path (which you could copy out of the NavmeshAgent when you deactivate it) and some data tracking exactly whereabouts on that path you are, along with your rate of movement.

    You don't need a NavmeshAgent to compute paths; NavMesh.CalculatePath lets you just ask the Navmesh for paths directly. So you could maybe just use that to make 'points' move towards the player - calculating a fresh path for agents every few frames to deal with the player moving around, but staggering it so you don't recalculate for all 100 agents in one frame - and have those points turn into proper NavMeshAgents when they come into view or within X many meters of the player.
     
    Liam2349 and radiantboy like this.
  16. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    Hmm, i think i understand it by some points. But I dont understand how I can simulate the enemy movement offscreen with the points i get from the NavMesh.CalculatePath.

    For example, if i have 2 points from the path, and lets say my enemy moves by 1 unit per second, and the distance between the 2 points is 10, can i just interpolate with something like Vector3.Lerp(point1, point2, 1/10f) for the first second?
     
  17. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    Okee, found it out! Can handle now 500+ enemys offscreen with 30 fps! Many thanks!

    For people who are interested in script:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class FastPathFinding : MonoBehaviour {
    4.     public float speed; // units/s
    5.     private NavMeshPath path;
    6.     private float previousTime;
    7.     void Awake()
    8.     {
    9.         path = new NavMeshPath();
    10.     }
    11.     public void CalculatePath(Transform target)
    12.     {
    13.         Move(path, Time.timeSinceLevelLoad - previousTime);
    14.         previousTime = Time.timeSinceLevelLoad;
    15.         NavMesh.CalculatePath(transform.position, target.position, NavMesh.AllAreas, path);
    16.     }
    17.  
    18.     void Move(NavMeshPath path, float elapsed)
    19.     {
    20.         if (path == null) return;
    21.         float distanceToTravel = speed * elapsed;
    22.         for (int i = 0; i < path.corners.Length - 1; i++)
    23.         {
    24.             float distance = Vector3.Distance(path.corners[i], path.corners[i + 1]);
    25.             if (distance < distanceToTravel){
    26.                 distanceToTravel -= distance;
    27.                 continue;
    28.             }
    29.             else {
    30.                 Vector3 pos = Vector3.Lerp(path.corners[i], path.corners[i+1], distanceToTravel/distance);
    31.                 transform.position = pos;
    32.                 break;
    33.             }
    34.         }
    35.     }
    36. }
    37.  
     
  18. JBR-games

    JBR-games

    Joined:
    Sep 26, 2012
    Posts:
    708
    @Rusfighter thanks for the script
    working on adding this with the new culling groups

    @superpig
    http://docs.unity3d.com/Manual/CullingGroupAPI.html

    BoundingSphere[] spheres = new BoundingSphere[1000];
    spheres[0] = new BoundingSphere(Vector3.zero, 1f);
    group.SetBoundingSpheres(spheres);
    group.SetBoundingSpheresCount(1); << not sure how to report it but the s in spheres shouldn't be there
     
    superpig likes this.
  19. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    There's a non alloc version of the function to get paths back, I would recommend some form of singleton so you can just generate paths with it willy-nilly, copying the results to arrays per actor so you can keep it tidy and fast on mobile.
     
  20. JBR-games

    JBR-games

    Joined:
    Sep 26, 2012
    Posts:
    708
    Using culling groups with @Rusfighter fast pathfinding Script

    -added multiple spawn points and fixed some issues with pathfinding jumping , by making the previousTime an array so each sphere has its own time . edited script on 10-23-2015

    currently it updates the location of one sphere per frame,it Instantiates a prefab once its visible, and then no longer uses the bounding sphere.
    In later updates ill try to test keeping the sphere so that LODs and visibility can still be calculated with the bounding sphere.

    @hippocoder if you wouldn't mind ,i would love to see how you would better optimize this


    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class JBR_Culling_Groups : MonoBehaviour {
    6.     //attach this script to a game object, out of playable area, do not attach this to your spawn location
    7.  
    8.     public CullingGroup group;
    9.     public BoundingSphere[] spheres;
    10.     public float[] DistanceBands;        //an Array of distances for distance bands (for switch LODs or type of movement)
    11.     private NavMeshPath path;            //navmesh path
    12.     private float distance;             // distance for navmesh calculation
    13.     private float[] previousTime;        //array of time for each sphere
    14.     public string checkTag;                //tag of SpawnedObjects prefabs to check for before spawning
    15.     public GameObject[] spawnedObjects; //array of objects spawned
    16.     public int maxObjects = 50;            // maximun allowed SpawnedObjects prefabs,if at maximum no new spheres will be spawned
    17.  
    18.     public float speed;                 //movement speed of bounding spheres in units/s
    19.     public int maxSpheres = 100;        // Max Array size of all bounding Spheres(this should be set at the max amount you will ever use in game)
    20.     public float sphereRadius = 1.0f;    // radius of bounding sphere
    21.     public int currentSphere = 0;        // current id number of the next sphere to be spawned
    22.     public int moveSphere = 0;            // current id number of the next sphere to be moved on navmesh
    23.     public int totalSpawnedSpheres = 0; // how many spheres have been spawned since start of game
    24.     public Transform target;            // target to move towards
    25.     public Vector3 targetPosition;        // location of target
    26.  
    27.     public bool canSpawn = true;        // can spawner spawn an object
    28.     public bool nextSphere = true;        // this is used to only allow one sphere to update movement per frame
    29.     public GameObject[] SpawnLoc;        // Array of spawn location Transforms
    30.     public int spawnNumb;                // spawn location id number, set to be random
    31.     public float spawnSpeed = 1.0f;        // how fast bounding spheres are spawned
    32.     public GameObject SpawnedObject;    // Prefab to be spawned once bounding sphere is in sight
    33.  
    34.  
    35.  
    36.     void Awake()
    37.     {
    38.         path = new NavMeshPath();
    39.     }
    40.  
    41.  
    42.     // Use this for initialization
    43.     void Start()
    44.     {
    45.         StartCoroutine (SlowUpdate ()); //called only a few times per second
    46.         StartCoroutine (SlowSpawn ()); //spawns spheres at a random spawn location
    47.  
    48.         SpawnLoc = GameObject.FindGameObjectsWithTag ("Spawn");
    49.      
    50.         group = new CullingGroup();
    51.         group.targetCamera = Camera.main;
    52.         spheres = new BoundingSphere[maxSpheres];
    53.         previousTime = new float[maxSpheres];
    54.         for (int i = 0; i < spheres.Length; i++)
    55.         {
    56.         spheres[i].position = (this.transform.position);    // all spheres start off spawned in this gameobjects location, they will not update until moved to a new location
    57.     }
    58.         group.SetBoundingSpheres(spheres);
    59.     //    group.SetBoundingSphereCount(1);
    60.         group.SetBoundingDistances(DistanceBands);
    61.     }
    62.  
    63.  
    64.     // Update is called once per frame
    65.     void Update () {
    66.  
    67.         if (moveSphere >= totalSpawnedSpheres) {
    68.             moveSphere = 0;
    69.         }
    70.         if (nextSphere == true) {
    71.             if (spheres[moveSphere].position != this.transform.position) {     // only check bounding spheres that have been spawned
    72.                 nextSphere = false;
    73.                 CalculatePath (moveSphere);
    74.             }
    75.             else{
    76.                 moveSphere = moveSphere + 1;
    77.             }
    78.         }
    79.     }
    80.     void OnDisable(){
    81.         group.Dispose();
    82.         group = null;
    83.     }
    84.  
    85.     IEnumerator SlowUpdate(){
    86.         yield return new WaitForSeconds(.5f);
    87.         StartCoroutine (SlowUpdate ());
    88.         group.SetDistanceReferencePoint (Camera.main.transform);
    89.         targetPosition = target.position;
    90.         spawnedObjects = GameObject.FindGameObjectsWithTag (checkTag);
    91.  
    92.         if (spawnedObjects.Length >= maxObjects) {
    93.             canSpawn = false;
    94.         }
    95.         else{
    96.             canSpawn = true;
    97.         }
    98.  
    99.  
    100.     }
    101.  
    102.     IEnumerator SlowSpawn(){
    103.         yield return new WaitForSeconds (spawnSpeed);
    104.         StartCoroutine (SlowSpawn ());
    105.  
    106.         if (canSpawn == true) {
    107.             spawnNumb = Random.Range (0, SpawnLoc.Length);    //random number id for spawn location
    108.             if (currentSphere >= spheres.Length) {
    109.                 currentSphere = 0;
    110.             }
    111.             if (spheres [currentSphere].position == this.transform.position) {
    112.                 spheres[currentSphere] = new BoundingSphere (SpawnLoc[spawnNumb].transform.position, sphereRadius);//spawns sphere at spawn location with a radius of (sphereRadius)
    113.     //            Debug.Log ("Location" + spheres[currentSphere].position +" Spawn Location   " + spawnNumb);
    114.                 Debug.Log ("spawn sphere" + currentSphere);
    115.                 if (totalSpawnedSpheres == maxSpheres) {
    116.    
    117.                 }
    118.             }
    119.             currentSphere = currentSphere + 1;
    120.             totalSpawnedSpheres += 1;
    121.             if (totalSpawnedSpheres > maxSpheres) {
    122.                 totalSpawnedSpheres = maxSpheres;
    123.             }
    124.         }
    125.     }
    126.  
    127.     void CalculatePath(int thisSphere)
    128.     {
    129.  
    130.         NavMesh.CalculatePath(spheres[thisSphere].position, targetPosition, NavMesh.AllAreas, path);
    131.         Move(path, Time.timeSinceLevelLoad - previousTime[thisSphere], thisSphere);
    132.         previousTime[thisSphere] = Time.timeSinceLevelLoad;
    133.     }
    134.  
    135.     void Move(NavMeshPath path, float elapsed, int thisSphere)
    136.     {
    137.         if (path == null) return;
    138.         float distanceToTravel = speed * elapsed;
    139.  
    140.         if (path.corners.Length < 1) {
    141.             nextSphere = true;
    142.             moveSphere = moveSphere + 1;
    143.             Debug.Log ("no path yet");
    144.         }
    145.  
    146.         for (int i = 0; i < path.corners.Length - 1; i++)
    147.         {
    148.             distance = Vector3.Distance(path.corners[i], path.corners[i + 1]);
    149.             if (distance < distanceToTravel){
    150.                 distanceToTravel -= distance;
    151.                 continue;
    152.  
    153.             }
    154.             else {
    155.                 Vector3 pos = Vector3.Lerp(path.corners[i], path.corners[i+1], distanceToTravel/distance);
    156.                 spheres[thisSphere].position = pos;
    157.  
    158.                 break;
    159.             }
    160.         }
    161. //        Debug.Log (" Next Sphere" + (moveSphere + 1));
    162.         moveSphere = moveSphere + 1;
    163.         nextSphere = true;
    164.         // tells designer if sphere is visible and if in distance range 0 once per movement
    165.         int sphereDistanceBand = group.GetDistance(thisSphere);
    166.         bool sphereIsVisible = group.IsVisible(thisSphere);
    167.                 if (sphereDistanceBand == 0) {
    168.     //        Debug.Log ("Distance Band Away  " + sphereDistanceBand + " " +spheres[thisSphere]+ " "+ thisSphere);
    169.                 }
    170.                 if (sphereIsVisible == true) {
    171.             //if sphere is visible Instantiate Prefab in its place
    172.             Instantiate(SpawnedObject,spheres[thisSphere].position, SpawnLoc[spawnNumb].transform.rotation);
    173.             DespawnSphere(thisSphere);
    174.     //        Debug.Log ("Can See Sphere  " + sphereIsVisible + " " +spheres[thisSphere]+ " " + thisSphere);
    175.                 }
    176.  
    177.     }
    178.     //if a sphere is no longer needed send message "DespawnSphere" with the sphere id number to this script
    179.     void DespawnSphere(int sphereNumber){
    180.         spheres[sphereNumber].position = (this.transform.position);
    181.         Debug.Log (" Sphere despawned  " + sphereNumber);
    182.     }
    183.  
    184. }
    185.  
    186.  
    187.  
     
    Last edited: Oct 24, 2015
  21. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    I guess you mean this with nonAllocated:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class FastPathFinding : MonoBehaviour {
    4.     public float speed; // units/s
    5.     private NavMeshPath path;
    6.     private float previousTime;
    7.     Vector3[] corners = new Vector3[200];
    8.     private Transform _transform;
    9.  
    10.     void Awake()
    11.     {
    12.         path = new NavMeshPath();
    13.         _transform = transform;
    14.     }
    15.     public void CalculatePath(Transform target)
    16.     {
    17.         Move(path, Time.timeSinceLevelLoad - previousTime);
    18.         previousTime = Time.timeSinceLevelLoad;
    19.         NavMesh.CalculatePath(transform.position, target.position, NavMesh.AllAreas, path);
    20.     }
    21.  
    22.     void Move(NavMeshPath path, float elapsed)
    23.     {
    24.         if (path == null) return;
    25.         float distanceToTravel = speed * elapsed;
    26.         int cornersCount = path.GetCornersNonAlloc(corners);
    27.         for (int i = 0; i < cornersCount - 1; i++)
    28.         {
    29.             float distance = Vector3.Distance(corners[i], corners[i + 1]);
    30.             if (distance < distanceToTravel){
    31.                 distanceToTravel -= distance;
    32.                 continue;
    33.             }
    34.             else {
    35.                 _transform.position = Vector3.Lerp(corners[i], corners[i + 1], distanceToTravel / distance);
    36.                 break;
    37.             }
    38.         }
    39.     }
    40. }
    One limitation: up to 200 corners...

    And with some modification it can easily be made singleton yes.
     
    hippocoder likes this.
  22. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    As a limitation, 200 corners are insane. I have developed an open world game project on the side, and it never used more than 10 corners, even with quite large areas. I guess the real take away is, you'd be surprised how little corners there are in a typical path :)

    When doing manual agent movement you need some form of stuck detection. As far as I know the agents in Unity have this kind of repath capability.
     
  23. JBR-games

    JBR-games

    Joined:
    Sep 26, 2012
    Posts:
    708
    @superpig
    Im using 5.3.1p1 and using culling groups.
    Having an interesting issue. Basically in code if i use this statement the visiblity of the sphere is checked and accurate results are given.
    Code (CSharp):
    1. bool sphereIsVisible = group.IsVisible(0);
    But if i use this even when a sphere should be visible there is no call to the function. But distance bands seem to be called properly.
    Code (CSharp):
    1. group.onStateChanged = StateChangedMethod;
    2.  
    3. private void StateChangedMethod(CullingGroupEvent evt)
    4. {
    5.     if(evt.hasBecomeVisible)
    6.         Debug.LogFormat("Sphere {0} has become visible!", evt.index);
    7.     if(evt.hasBecomeInvisible)
    8.         Debug.LogFormat("Sphere {0} has become invisible!", evt.index);
    9. }
    Not sure if this is a bug or something im doing wrong or my expectations are not correct.
     
  24. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    That's weird. If IsVisible() is giving you the correct results, then that shows that the culling group is being updated correctly on the C++ side of things... maybe something broke with the callback, but I think our tests would catch that. Any chance you could make a tiny project that demonstrates the bug, and submit it for me to take a look at?
     
  25. JBR-games

    JBR-games

    Joined:
    Sep 26, 2012
    Posts:
    708
    @superpig
    Ok i think i found the problem
    Code (CSharp):
    1. group.onStateChanged = StateChangedMethod; //reference a state change of a sphere to function
    2. public void StateChangedMethod(CullingGroupEvent evt) //Called if the state of a sphere has changed
    3.     {
    4.         int evtRef = evt.index;
    5.         if (evt.hasBecomeVisible == true ) { // if state changed to visible
    6. //            Debug.Log ("Sphere has become visible! Maybe********  " + evtRef + " " + Time.timeSinceLevelLoad);
    7.             if(evtRef < maxSpheres){ // make sure we are seeing a sphere that is used on moving objects
    8.                 Debug.Log ("Sphere has become visible!  " + evtRef);
    im calling the group.onStateChanged from two different scripts and its causing the StateChangeMethod not to be called.
    as soon as i comment out the second group.OnStateChanged call in the other script the culling groups work as expected ..
    Code (CSharp):
    1.     jbr_Culling_Groups.group.onStateChanged = CullingGroupsChangeState;
    2.     }
    3.  
    4.     private void CullingGroupsChangeState(CullingGroupEvent evt){
    5.  
    6.         if (evt.index == sphereNumber) {
    7.             if (debugging) {
    8.                 Debug.Log (evt.index + " state change " + sphereNumber);
    9.             }
    10.             if (evt.hasBecomeVisible) {
    11.                 isVisible = true;
    12.                 skinMeshRed.enabled = true;
    13.                 if (debugging) {
    14.                     Debug.Log (this.gameObject.name + " Is Now Visible");
    15.                 }
    16.             }
    17.             if (evt.hasBecomeInvisible) {
    18.                 isVisible = false;
    19.                 skinMeshRed.enabled = false;
    20.                 if (debugging) {
    21.                     Debug.Log (this.gameObject.name + " Is Now InVisible");
    22.                 }
    23.             }
    24.  
    25.         }
    26.     }
    Im assuming its because i cant have two methods = to the group.onStateChange at once.
    += seems to have fixed the issue, I swore at some point i tried that last night but i guess i missed something.
    thanks and sorry to bother you with a missed step in my code.
     
    Last edited: Jan 10, 2016
    pixxelbob likes this.
  26. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    Apologies for reviving this thread, but is one of the few cases where the CullingGroup API is discussed.

    @superpig, reading the docs for the CullingGroup class, there seems to be a missing point in the documentation.

    CullingGroup API uses BoundingSphere for visibility checks, however there is no evident way to bind a BoundingSphere to a mesh, transform or gameobject, in order to update its position as part of the API.

    Are we supossed to manually update the BoundingSphere position (per frame, on Update(), etc) or Im missing something?

    Thanks!
     
  27. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Yes.
     
    Novack likes this.
  28. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    Thank you @superpig for that. Let me ask you the following, obvious question then:

    Is updating the BoundingSphere on Update(), and using CullingGroup something that will give me better performance than invoking 10 times per second something like: GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(Camera), bounds)
     
  29. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    That's not an easy question to answer as it depends on a lot of things. Why don't you profile it and see?
     
    Novack likes this.
  30. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    I thought it was an easy answer given that there is such an API like CullingGroup at all, but I will do the profiling at some point. Thanks.
     
  31. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Well, you're comparing CullingGroup - which is very fast, but does work every frame to give you immediate results - with calling a slower function, but only 10 times per second. Hard for me to say where the tipping point will be. I guess that if you're only running at 10FPS then I'm pretty confident CullingGroup will be faster :)
     
    Novack likes this.
  32. Novack

    Novack

    Joined:
    Oct 28, 2009
    Posts:
    844
    @superpig at the very least, you answer helps me confirm I was in the right direction by asking the question at all. So you're actually helping me deciding towards what I should have done from the begining, that is to profile both options.

    However! After implementing the CullingGroup option my subjective perception is that it works smoother and lighter than with the low frequency GeometryUtility calls... So I will drop the hammer and keep the CullingGroup implementation. As soon as I have time, will get back and do some profiling, and if found any surprises, will report back.

    Thanks for the help!
     
    superpig likes this.
  33. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    Seems like a great script, but it stops working when the navagent is disabled for some reason, which defeats the purpose, any ideas why?
     
  34. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    NavMesh.CalculatePath stops working when I turn off my navagent, should it work? I like your idea so I implemented it, the transform of my npc is updated in a loop using the path corners, this works well and obviously lowers the calls to SetDestination to zero when its applied to everyone. This is great because my npcs were stuttering struggling to get a path as soon as there are like 20 or more. But, I want also to turn off the navagent (although maybe it doesnt matter ? though probably does since the agents fight it when theyre moving off platforms etc. And I see A LOT of synchronizeSimulationToAgentTransforms as described here: https://forum.unity.com/threads/what-is-synchronizesimulationtoagenttransforms.1129505/ ) but when I do they stop still as if the static method calculatePath relies on it. Maybe something changed in unity?
     
    Last edited: Jun 21, 2021
  35. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    Looks like I was wrong, I did a testbed and this approach works fine. Actually im not finding the navagent to be a drag on my performance so I probably dont need to do this anyway. I was also wrong it seems about the setDest causing agents to struggle, this actually happens I think when a part of their path is blocked, probably by another NPC in a thin alley. My test showed 5000 agents using setDest with no problems.
     
  36. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Picking up an old thread looking for optimization paths...

    Is this implementation still relevant in 2021? i.e. is there any potential performance gain from
    - decoupling the NavMeshAgent and the rest of its hierarchy?
    - have that hierachy's transform shadow the NaveMeshAgent transform, using culling groups for further optim?