Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice

Question Runtime Implementation

Discussion in 'Navigation' started by GreedyVox, Jan 21, 2024.

  1. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    Trying to implement the NavMeshSurface without blocking the main rendering thread at runtime, for a procedural voxel world. Currently when rebuilding terrain chunks, and if the surface is complex, the UpdateNavMesh takes between 200ms to 800ms, which is ok, as long as it doesn't block the main rendering thread, however with my implementation, it seems to be blocking, with a slight jerk when updating the surface.

    Is my implementation correct? Or is this the only method for updating the NavMeshSurface? Or should I have a NavMeshSurface per chunk instead of the whole world?

    Code (CSharp):
    1.  
    2. public bool UpdateNavMeshSurface() => m_ToggleUpdate && !(m_ToggleUpdate = false);
    3. public void UpdateNavMeshSurface(Action call = null)
    4. => StartCoroutine(UpdateNavMeshSurface(m_NavMeshSurface, call));
    5. protected IEnumerator UpdateNavMeshSurface(NavMeshSurface surface, Action call = null)
    6. {
    7.     m_LastUpdate = surface?.UpdateNavMesh(surface?.navMeshData);
    8.     if (m_LastUpdate != null)
    9.         yield return new WaitUntil(() => m_LastUpdate.isDone);
    10.     call?.Invoke();
    11. }
     
    Last edited: Jan 21, 2024
  2. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    After some testing, with trying to do NavMeshSurface per chunk, which was way worse than the above, think it just all about optimising the setting to reduce the UpdateNavMesh build time.

    I did notice if I can keep the build time to around 200ms, everything seems to be smooth, 500+ms can notice the UpdateNavMesh build, causing a slight jerkiness for about a one or two rendering frames.
     
  3. elvirawhitlock

    elvirawhitlock

    Joined:
    Jan 23, 2024
    Posts:
    1
    Here's a simplified example of how you might structure the code using Unity's Job System. Note that using Unity's Job System requires the Burst compiler, which is available starting from Unity 2018.1:

    csharp
    using UnityEngine;
    using UnityEngine.AI;
    using Unity.Jobs;
    using Unity.Collections;
    using Unity.Burst;
    super mario bros wonder
    public class YourClass : MonoBehaviour
    {
    private NavMeshSurface m_NavMeshSurface;
    private bool m_ToggleUpdate;
    private JobHandle m_LastUpdate;

    public bool UpdateNavMeshSurface() => m_ToggleUpdate && !(m_ToggleUpdate = false);

    private void Update()
    {
    if (UpdateNavMeshSurface())
    {
    NavMeshJob job = new NavMeshJob
    {
    surface = m_NavMeshSurface,
    };

    m_LastUpdate = job.Schedule();
    }
    }

    [BurstCompile]
    private struct NavMeshJob : IJob
    {
    public NavMeshSurface surface;

    public void Execute()
    {
    surface?.UpdateNavMesh(surface?.navMeshData);
    }
    }

    private void LateUpdate()
    {
    m_LastUpdate.Complete();
    }
    }
     
  4. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    Thanks for your help, unfortunately, NavMeshSurface is not a value type. Job structs may not contain any reference types.

    Would have been a great solution otherwise!
     
  5. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    I found the issue, it was a small mistake made implementing the functions, I forgot to handle the call back actions.
    Code (CSharp):
    1. () => { call?.Invoke(); }
    Once implemented correctly everything worked like it should. Now building the NavMeshSurface is done in the back threads and once finished invokes the action, so now when rebuilding terrain chunks the NavMeshSurface updates without blocking the main thread, the way it was designed. :cool:

    Code (CSharp):
    1. public bool UpdateNavMeshSurface() => m_ToggleUpdate && !(m_ToggleUpdate = false);
    2. public void UpdateNavMeshSurface(Action call = null)
    3. => StartCoroutine(UpdateNavMeshSurface(m_NavMeshSurface, () => { call?.Invoke(); }));
    4. protected IEnumerator UpdateNavMeshSurface(NavMeshSurface surface, Action call = null)
    5. {
    6.     m_LastUpdate = surface?.UpdateNavMesh(surface?.navMeshData);
    7.     if (m_LastUpdate != null)
    8.         yield return new WaitUntil(() => m_LastUpdate.isDone);
    9.     call?.Invoke();
    10. }
     
    WeiWuDe likes this.
  6. Baldor

    Baldor

    Joined:
    Jul 24, 2018
    Posts:
    5
    Helped me alot. Thanks for sharing :D
     
  7. GreedyVox

    GreedyVox

    Joined:
    Nov 18, 2015
    Posts:
    33
    Aweosme! Glad it helped! :)