Search Unity

Navmesh Modifier or Navmesh Modifier Volume at run time

Discussion in 'Navigation' started by Ardinius, Aug 10, 2017.

  1. Ardinius

    Ardinius

    Joined:
    Oct 4, 2015
    Posts:
    57
    For example:
    You have a maze, and the player can place traps at run time, the traps are navigatable but you want the AI to attempt to avoid them of course, so i would assume to add a Navmesh Modifier volume to the traps with a high number.

    However is it possible to add a NavMesh Modifier / Volume at run time?
    The documentation seems to state "The NavMesh Modifier Volume also affects the NavMesh generation process, meaning the NavMesh has to be updated to reflect any changes to NavMesh Modifier Volumes."

    I have a local Nav Mesh Builder in the scene, and have attempted adding Nav Mesh Modifier volume and Nav mesh modifier script to the trap object but this doesn't seem to be effecting the navmesh in anyway.
     
  2. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    NavMeshModifer Volumes are currently not processed by the LocalNavMeshBuilder

    First of all change the NavMeshSourceTag to this:

    Code (CSharp):
    1. [DefaultExecutionOrder(-200)]
    2. public class NavMeshSourceTag : MonoBehaviour
    3. {
    4.     public static List<MeshFilter> Meshes = new List<MeshFilter>();
    5.     public static List<NavMeshModifierVolume> VolumeModifiers = new List<NavMeshModifierVolume>();
    6.     public static int AgentTypeId;
    7.  
    8.     //----------------------------------------------------------------------------------------
    9.     private void OnEnable()
    10.     {
    11.         var volumes = GetComponents<NavMeshModifierVolume>();
    12.         if(volumes != null)
    13.             VolumeModifiers.AddRange(volumes);
    14.  
    15.         var modifier = GetComponent<NavMeshModifier>();
    16.         if ((modifier != null) && (!modifier.AffectsAgentType(AgentTypeId) || (modifier.ignoreFromBuild) && modifier.AffectsAgentType(AgentTypeId)))
    17.             return;
    18.  
    19.         var meshes = GetComponentsInChildren<MeshFilter>();
    20.         if (meshes != null && meshes.Length > 0)
    21.             Meshes.AddRange(meshes);
    22.     }
    23.  
    24.     //----------------------------------------------------------------------------------------
    25.     private void OnDisable()
    26.     {
    27.         var volumes = GetComponents<NavMeshModifierVolume>();
    28.         if (volumes != null)
    29.         {
    30.             for (int index = 0; index < volumes.Length; index++)
    31.                 VolumeModifiers.Remove(volumes[index]);
    32.         }
    33.  
    34.         var modifier = GetComponent<NavMeshModifier>();
    35.         if((modifier != null) && (modifier.ignoreFromBuild))
    36.             return;
    37.  
    38.         var mesh = GetComponent<MeshFilter>();
    39.         if(mesh != null)
    40.             Meshes.Remove(mesh);
    41.     }
    42.  
    43.     //----------------------------------------------------------------------------------------
    44.     public static void CollectMeshes(ref List<NavMeshBuildSource> _sources)
    45.     {
    46.         _sources.Clear();
    47.         for (var i = 0; i < Meshes.Count; ++i)
    48.         {
    49.             var mf = Meshes[i];
    50.  
    51.             if (mf == null)
    52.                 continue;
    53.  
    54.             var m = mf.sharedMesh;
    55.             if (m == null)
    56.                 continue;
    57.  
    58.             var s = new NavMeshBuildSource();
    59.             s.shape = NavMeshBuildSourceShape.Mesh;
    60.             s.sourceObject = m;
    61.             s.transform = mf.transform.localToWorldMatrix;
    62.             var modifier = mf.GetComponent<NavMeshModifier>();
    63.             s.area = modifier && modifier.overrideArea ? modifier.area : 0;
    64.             _sources.Add(s);
    65.         }
    66.     }
    67.  
    68.     //----------------------------------------------------------------------------------------
    69.     public static void CollectModifierVolumes(int _layerMask, ref List<NavMeshBuildSource> _sources)
    70.     {
    71.         foreach (var m in VolumeModifiers)
    72.         {
    73.             if ((_layerMask & (1 << m.gameObject.layer)) == 0)
    74.                 continue;
    75.             if (!m.AffectsAgentType(AgentTypeId))
    76.                 continue;
    77.  
    78.             var mcenter = m.transform.TransformPoint(m.center);
    79.             var scale = m.transform.lossyScale;
    80.             var msize = new Vector3(m.size.x * Mathf.Abs(scale.x), m.size.y * Mathf.Abs(scale.y), m.size.z * Mathf.Abs(scale.z));
    81.  
    82.             var src = new NavMeshBuildSource();
    83.             src.shape = NavMeshBuildSourceShape.ModifierBox;
    84.             src.transform = Matrix4x4.TRS(mcenter, m.transform.rotation, Vector3.one);
    85.             src.size = msize;
    86.             src.area = m.area;
    87.             _sources.Add(src);
    88.         }
    89.     }
    90. }
    91.  
    Change the LocalNavMeshBuilder accordingly:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.AI;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using NavMeshBuilder = UnityEngine.AI.NavMeshBuilder;
    6.  
    7. // Build and update a localized navmesh from the sources marked by NavMeshSourceTag
    8. [DefaultExecutionOrder(-102)]
    9. public class LocalNavMeshBuilder : MonoBehaviour
    10. {
    11.     // The center of the build
    12.     public Transform m_Tracked;
    13.     public int AgentID;
    14.  
    15.     // The size of the build bounds
    16.     public Vector3 m_Size = new Vector3(80.0f, 20.0f, 80.0f);
    17.  
    18.     NavMeshData m_NavMesh;
    19.     AsyncOperation m_Operation;
    20.     NavMeshDataInstance m_Instance;
    21.     List<NavMeshBuildSource> m_Sources = new List<NavMeshBuildSource>();
    22.  
    23.     IEnumerator Start()
    24.     {
    25.         while (true)
    26.         {
    27.             UpdateNavMesh(true);
    28.             yield return m_Operation;
    29.         }
    30.     }
    31.  
    32.     void OnEnable()
    33.     {
    34.         // Construct and add navmesh
    35.         m_NavMesh = new NavMeshData();
    36.         m_Instance = NavMesh.AddNavMeshData(m_NavMesh);
    37.         if (m_Tracked == null)
    38.             m_Tracked = transform;
    39.         UpdateNavMesh(false);
    40.     }
    41.  
    42.     void OnDisable()
    43.     {
    44.         // Unload navmesh and clear handle
    45.         m_Instance.Remove();
    46.     }
    47.  
    48.     void UpdateNavMesh(bool asyncUpdate = false)
    49.     {
    50.         NavMeshSourceTag.CollectMeshes(ref m_Sources);
    51.         NavMeshSourceTag.CollectModifierVolumes(LayerMask.GetMask("Default"), ref m_Sources);
    52.  
    53.         var defaultBuildSettings = NavMesh.GetSettingsByID(AgentID);
    54.         var bounds = QuantizedBounds();
    55.  
    56.         if (asyncUpdate)
    57.             m_Operation = NavMeshBuilder.UpdateNavMeshDataAsync(m_NavMesh, defaultBuildSettings, m_Sources, bounds);
    58.         else
    59.             NavMeshBuilder.UpdateNavMeshData(m_NavMesh, defaultBuildSettings, m_Sources, bounds);
    60.     }
    61.  
    62.     static Vector3 Quantize(Vector3 v, Vector3 quant)
    63.     {
    64.         float x = quant.x * Mathf.Floor(v.x / quant.x);
    65.         float y = quant.y * Mathf.Floor(v.y / quant.y);
    66.         float z = quant.z * Mathf.Floor(v.z / quant.z);
    67.         return new Vector3(x, y, z);
    68.     }
    69.  
    70.     Bounds QuantizedBounds()
    71.     {
    72.         // Quantize the bounds to update only when theres a 10% change in size
    73.         var center = m_Tracked ? m_Tracked.position : transform.position;
    74.         return new Bounds(Quantize(center, 0.1f * m_Size), m_Size);
    75.     }
    76.  
    77.     void OnDrawGizmosSelected()
    78.     {
    79.         if (m_NavMesh)
    80.         {
    81.             Gizmos.color = Color.green;
    82.             Gizmos.DrawWireCube(m_NavMesh.sourceBounds.center, m_NavMesh.sourceBounds.size);
    83.         }
    84.  
    85.         Gizmos.color = Color.yellow;
    86.         var bounds = QuantizedBounds();
    87.         Gizmos.DrawWireCube(bounds.center, bounds.size);
    88.  
    89.         Gizmos.color = Color.green;
    90.         var center = m_Tracked ? m_Tracked.position : transform.position;
    91.         Gizmos.DrawWireCube(center, m_Size);
    92.     }
    93. }
    94.  
    Please change in LocalNavMeshBuilder
    Code (csharp):
    1. NavMeshSourceTag.CollectModifierVolumes(LayerMask.GetMask("Default"), ref m_Sources);
    to whatever Layermask you need to.

    Hope that helps.
     
    funkyCoty, Wrymnn, BillyMFT and 2 others like this.
  3. Ardinius

    Ardinius

    Joined:
    Oct 4, 2015
    Posts:
    57
    DwinTeimlon,
    I don't know how to thank you enough!
    I have been looking through these forums only in the last couple of days for help, and you appear all over the place offering help to others.
    It never even occured to me to attempt what you did there, its working perfectly, thank you again!
     
    DwinTeimlon likes this.
  4. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    Sure not problem at all. I had to solve that for my project anyway. No big deal really :). Always happy to help.
     
  5. christougher

    christougher

    Joined:
    Mar 6, 2015
    Posts:
    558
    YAAAAAAASSSSSSSSSSS.
    You, sir, are my hero.
    I've asked about how to do this a number of times.
     
  6. SSL7

    SSL7

    Joined:
    Mar 23, 2016
    Posts:
    349
    @DwinTeimlon Hello, just found this post after searching a lot about runtime navmesh on unity 5.6. My problem is that I have the localnavmeshbuilder on my player and navmeshsourcetag on my terrain, everything works fine on terrain (mountains are inaccessible etc.) but everything else is ignored, trees, houses etc, which are fine when I bake navmesh in editor, now they are just being ignored.

    Do I miss something?

    thank you in advance
     
  7. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    Did you add a navmeshsourcetag to your trees and houses?
     
  8. SSL7

    SSL7

    Joined:
    Mar 23, 2016
    Posts:
    349
    I did add a sourcetag to the whole terrain and then on houses and didnt work (havent tried on trees yet). the script I use is navmeshsourcetag of unity git, do I have to use your modified one here?

    Thank you for your reply @DwinTeimlon
     
  9. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    Depends. If you use navmesh modifiers only it's ok, but if you use navmesh modifier volumes you need to use my modified scripts above.
     
  10. Roixo

    Roixo

    Joined:
    Nov 30, 2017
    Posts:
    16
  11. DanielOckay

    DanielOckay

    Joined:
    Sep 22, 2017
    Posts:
    2
    I've been tinkering with what you provided some, should it work correctly on terrains?
    I'm using the LocalNavMeshBuilder and have the NavMeshSourceTag on all of my terrain tiles. I've tried a few things but doesn't seem to make a difference. all I can figure is that it's because I'm using Terrains instead of meshes.
     
  12. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    Could you post a screenshot of your terrain tiles in the editor and explain exactly what is not working. Not sure what you want to achieve.
     
  13. DanielOckay

    DanielOckay

    Joined:
    Sep 22, 2017
    Posts:
    2




    So I have a terrain comprised of a series of smaller terrains (more easily seen in second image), they all have the NavMeshSourceTag script on them, the LocalNavMeshBuilder on the capsule then works its magic, this works fine. The purple box is a NavMeshModifierVolume, however it has no effect on the navmesh(I've tried moving away and back to make sure it rebakes after placing it.
    I've tried the changes you suggested, but still had the same results. it's worth noting all of my terrain is actually "Terrain" which I'm guessing might be the issue since it seems to be treated slightly differently than a normal mesh.
    I'm not 100% sure this is the solution I want to use for my goals, but I wanted it as an option since it seems like it should work. I was planning to be able to create and assign some modifier volumes to various places programmatically that would adjust the cost appropriately for that area.
    it's not really a perfect solution for what I want, but also I don't really see why it isn't working.
    as I understood it, the modifiervolume box should have the adjusted cost value.

    worth noting, everything was generated at runtime and the modifiervolume was then added into the scene. second picture is when I enabled a NavMeshSurface script on that tile and disabled the NavMeshSourceTag script. not really applicable but also helps show how the tiles are layed out better than the first one does.
     
  14. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    You can use the NavMeshSurface.UpdateNavMesh:

    Code (CSharp):
    1. var surface = GameObject.FindObjectOfType<NavMeshSurface>();
    2.  
    3. surface.UpdateNavMesh (surface.navMeshData);
     
  15. BillyMFT

    BillyMFT

    Joined:
    Mar 14, 2013
    Posts:
    178
    Your post was very helpful. Thanks!
     
  16. Wrymnn

    Wrymnn

    Joined:
    Sep 24, 2014
    Posts:
    384
    Holy S***, nice. Was looking for this for days....
     
  17. Zante

    Zante

    Joined:
    Mar 29, 2008
    Posts:
    429
    You have saved me weeks of frustration and bewilderment. Thank you.
     
    DwinTeimlon likes this.
  18. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    Hi @DwinTeimlon. I am trying to get this to work also and the solution does not work for Terrain trees. I have added NavMeshModifierVolume and NavMeshSourceTag to the tree prefabs, but the OnEnable never gets called for these terrain trees ever. Do you know of any solution for using NaveMeshModifierVolume on terrain trees?

    [Edit]
    I think this is simply because prefabs are not preserved when used as terrain trees and so the NavMeshSourceTag and NavMeshModifierVolume scripts won't be on the trees.
     
    Last edited: Nov 29, 2021
  19. macube

    macube

    Joined:
    Jul 16, 2017
    Posts:
    39
    Small Update from me: it works with Terrains now :)
    ( replace the NavMeshSourceTag code )

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.AI;
    3. using System.Collections.Generic;
    4.  
    5. namespace Unity.AI.Navigation.Samples
    6. {
    7.     /// <summary>
    8.     /// Tagging component for use with the LocalNavMeshBuilder
    9.     /// Supports mesh-filter and terrain - can be extended to physics and/or primitives
    10.     /// </summary>
    11.     [DefaultExecutionOrder(-200)]
    12.     public class NavMeshSourceTag : MonoBehaviour
    13.     {
    14.         public static List<MeshFilter> Meshes = new List<MeshFilter>();
    15.         public static List<Terrain> m_Terrains = new List<Terrain>();
    16.  
    17.         public static List<NavMeshModifierVolume> VolumeModifiers = new List<NavMeshModifierVolume>();
    18.         public static int AgentTypeId;
    19.  
    20.         //----------------------------------------------------------------------------------------
    21.         private void OnEnable()
    22.         {
    23.             var volumes = GetComponents<NavMeshModifierVolume>();
    24.             if (volumes != null)
    25.                 VolumeModifiers.AddRange(volumes);
    26.  
    27.             var modifier = GetComponent<NavMeshModifier>();
    28.             if ((modifier != null) && (!modifier.AffectsAgentType(AgentTypeId) || (modifier.ignoreFromBuild) && modifier.AffectsAgentType(AgentTypeId)))
    29.                 return;
    30.  
    31.             var meshes = GetComponentsInChildren<MeshFilter>();
    32.             if (meshes != null && meshes.Length > 0)
    33.                 Meshes.AddRange(meshes);
    34.  
    35.             var t = GetComponentsInChildren<Terrain>();
    36.             if (t != null && t.Length > 0)
    37.                 m_Terrains.AddRange(t);
    38.         }
    39.  
    40.         //----------------------------------------------------------------------------------------
    41.         private void OnDisable()
    42.         {
    43.             var volumes = GetComponents<NavMeshModifierVolume>();
    44.             if (volumes != null)
    45.             {
    46.                 for (int index = 0; index < volumes.Length; index++)
    47.                     VolumeModifiers.Remove(volumes[index]);
    48.             }
    49.  
    50.             var modifier = GetComponent<NavMeshModifier>();
    51.             if ((modifier != null) && (modifier.ignoreFromBuild))
    52.                 return;
    53.  
    54.             var mesh = GetComponent<MeshFilter>();
    55.             if (mesh != null)
    56.                 Meshes.Remove(mesh);
    57.  
    58.             var t = GetComponent<Terrain>();
    59.             if (t != null)
    60.             {
    61.                 m_Terrains.Remove(t);
    62.             }
    63.         }
    64.  
    65.         //----------------------------------------------------------------------------------------
    66.         public static void CollectMeshes(ref List<NavMeshBuildSource> _sources)
    67.         {
    68.             _sources.Clear();
    69.             for (var i = 0; i < Meshes.Count; ++i)
    70.             {
    71.                 var mf = Meshes[i];
    72.  
    73.                 if (mf == null)
    74.                     continue;
    75.  
    76.                 var m = mf.sharedMesh;
    77.                 if (m == null)
    78.                     continue;
    79.  
    80.                 var s = new NavMeshBuildSource();
    81.                 s.shape = NavMeshBuildSourceShape.Mesh;
    82.                 s.sourceObject = m;
    83.                 s.transform = mf.transform.localToWorldMatrix;
    84.                 var modifier = mf.GetComponent<NavMeshModifier>();
    85.                 s.area = modifier && modifier.overrideArea ? modifier.area : 0;
    86.                 _sources.Add(s);
    87.             }
    88.  
    89.             for (var i = 0; i < m_Terrains.Count; ++i)
    90.             {
    91.                 var t = m_Terrains[i];
    92.                 if (t == null)
    93.                     continue;
    94.  
    95.                 var s = new NavMeshBuildSource();
    96.                 s.shape = NavMeshBuildSourceShape.Terrain;
    97.                 s.sourceObject = t.terrainData;
    98.                 // Terrain system only supports translation - so we pass translation only to back-end
    99.                 s.transform = Matrix4x4.TRS(t.transform.position, Quaternion.identity, Vector3.one);
    100.                 var modifier = t.GetComponent<NavMeshModifier>();
    101.                 s.area = modifier && modifier.overrideArea ? modifier.area : 0;
    102.                 _sources.Add(s);
    103.             }
    104.  
    105.         }
    106.  
    107.         //----------------------------------------------------------------------------------------
    108.         public static void CollectModifierVolumes(int _layerMask, ref List<NavMeshBuildSource> _sources)
    109.         {
    110.             foreach (var m in VolumeModifiers)
    111.             {
    112.                 if ((_layerMask & (1 << m.gameObject.layer)) == 0)
    113.                     continue;
    114.                 if (!m.AffectsAgentType(AgentTypeId))
    115.                     continue;
    116.  
    117.                 var mcenter = m.transform.TransformPoint(m.center);
    118.                 var scale = m.transform.lossyScale;
    119.                 var msize = new Vector3(m.size.x * Mathf.Abs(scale.x), m.size.y * Mathf.Abs(scale.y), m.size.z * Mathf.Abs(scale.z));
    120.  
    121.                 var src = new NavMeshBuildSource();
    122.                 src.shape = NavMeshBuildSourceShape.ModifierBox;
    123.                 src.transform = Matrix4x4.TRS(mcenter, m.transform.rotation, Vector3.one);
    124.                 src.size = msize;
    125.                 src.area = m.area;
    126.                 _sources.Add(src);
    127.             }
    128.         }
    129.     }
    130. }
     
    Last edited: Jan 5, 2022
  20. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    What do you mean it works with terrains now? It always worked with terrains. What exactly did you change?
     
  21. macube

    macube

    Joined:
    Jul 16, 2017
    Posts:
    39
    the code above get only all meshes ..

    public static List<MeshFilter> Meshes = new List<MeshFilter>();

    .. but no terrains. I'm add this ..

    public static List<Terrain> m_Terrains = new List<Terrain>();

    .. and the rest of the code for terrains (see the changed collectMeshes function). For me, it works now.
     
  22. Tion-Gaming

    Tion-Gaming

    Joined:
    Jan 30, 2015
    Posts:
    28
    Do you happen to know if only removing the comonents mesh from the list in OnDisable is intentional? In OnEnable it uses
    Code (CSharp):
    1.  
    2.  
    3.             var meshes = GetComponentsInChildren<MeshFilter>();
    4.             if (meshes != null && meshes.Length > 0)
    5.                 Meshes.AddRange(meshes);
    6.  
    but in OnDisable it only uses:
    Code (CSharp):
    1.  
    2.  
    3.             var mesh = GetComponent<MeshFilter>();
    4.             if (mesh != null)
    5.                 Meshes.Remove(mesh);
    6.  
    That looks wrong to me, but I havent had much experience with navmesh comps yet so i may be wrong. I think that would result in none of the children of a component having their mesh filter removed from the list when you disable the component and I am not sure if there is a way to remove them after that.