Search Unity

Does the component navigation work with terrain trees yet?

Discussion in 'Navigation' started by Tyrathect, Aug 14, 2017.

  1. Tyrathect

    Tyrathect

    Joined:
    Jul 10, 2012
    Posts:
    38
    It seemed pretty decent, but it has (had?) two serious flaws:
    1. It won't work if you have multiple scene files loaded. It throws errors.
    2. It doesn't work with trees on the terrain. It just ignores them.
     
  2. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    I can't comment on the multiple scene issue, but have you tried giving your tree prefabs NavMeshModifierVolumes?
     
  3. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    It does not work with trees out of the box like the built in navmesh system. Please comment on the issue on github so we can finally have this fixed https://github.com/Unity-Technologies/NavMeshComponents/issues/30

    If you know any other solution please let me know, I haven't tried giving the trees NavMeshModifierVolumes and I cannot test it right now, if someone can do that would be awesome.
     
  4. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    Have you tried giving your tree prefabs NavMeshModifierVolumes ? It is not working for me :(
     
  5. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    How do you bake/update your navmeshes? With NavMeshSurface or LocalNavMeshBuilder?
     
  6. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    I have a navmesh surface component in the scene and I click the Bake button from the navmesh surface script component.

    If I use Navigation > Bake, I am not really happy with the results, I see some parts of the terrain not baked properly, where the navmesh surface bake does it just right.
     
  7. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    Well, that just doesn't work. The "obsolete" bake in the Unity UI does not work with NavMeshModifiers and NavMeshModifierVolumes. You need to use the bake function of the NavMeshSurface from the new Navgiation components.

    Can you post a screenshot, of the terrain so we can see why it does not bake properly?
     
  8. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    It has gaps that are not present when I bake from the NM Surface component, so I want to stick with that. The problem is that it does not detect unity's terrain trees :(
     
  9. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    AFAIK, there is no solution for this if you stick using the old navmesh bake function.
     
  10. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    Well I am not using the old bake function and by old I am refering to the built in thats in the Navigation window. I am using the one from the navmesh surface game object.
     
  11. Naalei

    Naalei

    Joined:
    Jan 15, 2018
    Posts:
    1
    Hi,
    I ran into the same issue with terrain tree and NavMeshSurface and wasn't able to find a solution on google !
    So I tried to patch NavMeshSurface script myself and I'm quite happy with the result !

    The code is still missing a lot of things like using the real size of the tree (you can use treePrototypes to retrieve the tree prefab), colliders and details meshes, but it is suited to my needs !
    It is just an example on how to implement tree baking in NavMeshSurface.

    Replace NavMeshSurface.CollectSource() with this method.
    (Added code is between /***** Terrain trees *****/ and /***** End ofTerrain trees *****/)

    There is still an issue if your terrain is not set to position 0, 0, 0
    The fakeGameObject.transform.position should take into account terrain transform (position, rotation and scale)


    Hope it can help some people !
    (and sorry for my bad english !)

    Code (CSharp):
    1.  
    2.         List<NavMeshBuildSource> CollectSources()
    3.         {
    4.             var sources = new List<NavMeshBuildSource>();
    5.             var markups = new List<NavMeshBuildMarkup>();
    6.  
    7.             List<NavMeshModifier> modifiers;
    8.             if (m_CollectObjects == CollectObjects.Children)
    9.             {
    10.                 modifiers = new List<NavMeshModifier>(GetComponentsInChildren<NavMeshModifier>());
    11.                 modifiers.RemoveAll(x => !x.isActiveAndEnabled);
    12.             }
    13.             else
    14.             {
    15.                 modifiers = NavMeshModifier.activeModifiers;
    16.             }
    17.  
    18.             foreach (var m in modifiers)
    19.             {
    20.                 if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
    21.                     continue;
    22.                 if (!m.AffectsAgentType(m_AgentTypeID))
    23.                     continue;
    24.                 var markup = new NavMeshBuildMarkup();
    25.                 markup.root = m.transform;
    26.                 markup.overrideArea = m.overrideArea;
    27.                 markup.area = m.area;
    28.                 markup.ignoreFromBuild = m.ignoreFromBuild;
    29.                 markups.Add(markup);
    30.             }
    31.          
    32.             if (m_CollectObjects == CollectObjects.All)
    33.             {
    34.                 NavMeshBuilder.CollectSources(null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    35.             }
    36.             else if (m_CollectObjects == CollectObjects.Children)
    37.             {
    38.                 NavMeshBuilder.CollectSources(transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    39.             }
    40.             else if (m_CollectObjects == CollectObjects.Volume)
    41.             {
    42.                 Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
    43.                 var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
    44.                 NavMeshBuilder.CollectSources(worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    45.             }
    46.  
    47.             /***** Terrain trees *****/
    48.  
    49.             var fakeGameObject = new GameObject();
    50.             // Required to have a Transform instance to call localToWorldMatrix (I'm fairly bad at mathematics so maybe there is an other solution !)
    51.  
    52.             var size = Terrain.activeTerrain.terrainData.size;
    53.             // Do not use Terrain.activeTerrain but maybe add a properties or detect terrains inside the bounding box of NavMeshSurface
    54.             // Then simply loop through all selected terrains
    55.             foreach (var tree in Terrain.activeTerrain.terrainData.treeInstances)
    56.             {
    57.                 var prototype = Terrain.activeTerrain.terrainData.treePrototypes[tree.prototypeIndex];
    58.                 // Use prototype.prefab to reach the prefab of the tree
    59.                 // not used in this example
    60.  
    61.                 fakeGameObject.transform.position = new Vector3(tree.position.x * size.x, tree.position.y * size.y, tree.position.z * size.z) ;
    62.                 fakeGameObject.transform.rotation = Quaternion.AngleAxis(tree.rotation, Vector3.up);
    63.                 fakeGameObject.transform.localScale = new Vector3(1, 1, 1);
    64.  
    65.                 var src = new NavMeshBuildSource();
    66.                 src.transform = fakeGameObject.transform.localToWorldMatrix;
    67.                 src.shape = NavMeshBuildSourceShape.Box; // update this to your convenience
    68.                 src.size = new Vector3(1f, 1f, 1f); // update this according to tree heightScale / widthScale and the prefab size.
    69.                 sources.Add(src);
    70.             }
    71.             DestroyImmediate(fakeGameObject);
    72.  
    73.             /***** End ofTerrain trees *****/
    74.  
    75.             if (m_IgnoreNavMeshAgent)
    76.                 sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshAgent>() != null));
    77.  
    78.             if (m_IgnoreNavMeshObstacle)
    79.                 sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshObstacle>() != null));
    80.  
    81.             AppendModifierVolumes(ref sources);
    82.  
    83.             return sources;
    84.         }
     
    Last edited: Jan 15, 2018
  12. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    Thanks for the workaround, I haven't tested it, because I found another solution - exporting the tree colliders in the scene and rebaking the navmesh. Hopefully your solution will help to someone!
     
  13. WhiteWolfProject

    WhiteWolfProject

    Joined:
    Jan 29, 2017
    Posts:
    4
    Thanks ! Its work fine.

    I add terran position:

    public Terrain mTerrain;
    ....
    Vector3 pos = mTerrain.transform.position;
    ...
    fakeGameObject.transform.position = new Vector3(tree.position.x * size.x + pos.x,
    tree.position.y * size.y + pos.y,
    tree.position.z * size.z + pos.z);
     
    BrandStone likes this.
  14. Buka

    Buka

    Joined:
    Feb 22, 2013
    Posts:
    48
    This is still not working, tried to use NavMesh components but they ignore terrain trees for some reason if I bake with NavMeshSurface. Can somebody provide a solution? Tried above and still the same, if I place trees manually it works but from terrain trees, it does not work at all. Please help somebody
     
  15. skinwalker

    skinwalker

    Joined:
    Apr 10, 2015
    Posts:
    509
    I dont have other solution than buying A* project and using it, thats what I ended up doing.
     
  16. Buka

    Buka

    Joined:
    Feb 22, 2013
    Posts:
    48
    We are making MMO (for a while now) and these navigation problems are so annoying. I Was hoping that they will just fix that baking problem with trees and we could just use that. You can see on this example image (some terrain where I tried to bake things properly) I do get weird path around trees if default Unity Navigation Bake is used, looks really weird in the game when players are running around. NavMeshSurface has option to bake colliders only so when I try to use that i do get better results but only if trees are placed by hand, not terrain trees sadly... Would be great to not go and use A* at this point since we based on NavMeshAgent our combat, network, navigation...But if we have to switch as @skinwalker suggested then we will probably.
     

    Attached Files:

    GGodis and ModLunar like this.
  17. HedwigDoets

    HedwigDoets

    Joined:
    Dec 7, 2016
    Posts:
    2

    Hi! I fixed the thing where if the terrain is not on zero it doesn't work.
    Here's the whole thing independent of terrain position:
    Code (CSharp):
    1.  List<NavMeshBuildSource> CollectSources()
    2.         {
    3.             var sources = new List<NavMeshBuildSource>();
    4.             var markups = new List<NavMeshBuildMarkup>();
    5.  
    6.             List<NavMeshModifier> modifiers;
    7.             if (m_CollectObjects == CollectObjects.Children)
    8.             {
    9.                 modifiers = new List<NavMeshModifier>(GetComponentsInChildren<NavMeshModifier>());
    10.                 modifiers.RemoveAll(x => !x.isActiveAndEnabled);
    11.             }
    12.             else
    13.             {
    14.                 modifiers = NavMeshModifier.activeModifiers;
    15.             }
    16.  
    17.             foreach (var m in modifiers)
    18.             {
    19.                 if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
    20.                     continue;
    21.                 if (!m.AffectsAgentType(m_AgentTypeID))
    22.                     continue;
    23.                 var markup = new NavMeshBuildMarkup();
    24.                 markup.root = m.transform;
    25.                 markup.overrideArea = m.overrideArea;
    26.                 markup.area = m.area;
    27.                 markup.ignoreFromBuild = m.ignoreFromBuild;
    28.                 markups.Add(markup);
    29.             }
    30.  
    31.             if (m_CollectObjects == CollectObjects.All)
    32.             {
    33.                 NavMeshBuilder.CollectSources(null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    34.             }
    35.             else if (m_CollectObjects == CollectObjects.Children)
    36.             {
    37.                 NavMeshBuilder.CollectSources(transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    38.             }
    39.             else if (m_CollectObjects == CollectObjects.Volume)
    40.             {
    41.                 Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
    42.                 var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
    43.                 NavMeshBuilder.CollectSources(worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    44.             }
    45.  
    46.             /***** Terrain trees *****/
    47.  
    48.             var fakeGameObject = new GameObject();
    49.             // Required to have a Transform instance to call localToWorldMatrix (I'm fairly bad at mathematics so maybe there is an other solution !)
    50.  
    51.             var size = Terrain.activeTerrain.terrainData.size;
    52.             // Do not use Terrain.activeTerrain but maybe add a properties or detect terrains inside the bounding box of NavMeshSurface
    53.             // Then simply loop through all selected terrains
    54.             foreach (var tree in Terrain.activeTerrain.terrainData.treeInstances)
    55.             {
    56.                 var prototype = Terrain.activeTerrain.terrainData.treePrototypes[tree.prototypeIndex];
    57.                 // Use prototype.prefab to reach the prefab of the tree
    58.                 // not used in this example
    59.  
    60.                 fakeGameObject.transform.position = new Vector3(tree.position.x * size.x, tree.position.y * size.y, tree.position.z * size.z);
    61.                 fakeGameObject.transform.position += Terrain.activeTerrain.GetPosition();
    62.                 fakeGameObject.transform.rotation = Quaternion.AngleAxis(tree.rotation, Vector3.up);
    63.                 fakeGameObject.transform.localScale = new Vector3(1, 1, 1);
    64.  
    65.                 var src = new NavMeshBuildSource();
    66.                 src.transform = fakeGameObject.transform.localToWorldMatrix;
    67.                 src.shape = NavMeshBuildSourceShape.Box; // update this to your convenience
    68.                 src.size = new Vector3(1f, 1f, 1f); // update this according to tree heightScale / widthScale and the prefab size.
    69.                 sources.Add(src);
    70.             }
    71.             DestroyImmediate(fakeGameObject);
    72.  
    73.             /***** End ofTerrain trees *****/
    74.  
    75.             if (m_IgnoreNavMeshAgent)
    76.                 sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshAgent>() != null));
    77.  
    78.             if (m_IgnoreNavMeshObstacle)
    79.                 sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshObstacle>() != null));
    80.  
    81.             AppendModifierVolumes(ref sources);
    82.  
    83.             return sources;
    84.         }
     
    Swearsoft and ModLunar like this.
  18. Acreates

    Acreates

    Joined:
    Dec 12, 2016
    Posts:
    41
    For us non engineers. I can't find "NavMeshSurface.CollectSource()" has something changed? Can someone provide the entire script here?

    (Really annoying that the nav mesh doesn't bake Trees)
     
  19. HeavyArt

    HeavyArt

    Joined:
    Mar 5, 2015
    Posts:
    1
    Works great if change fakeGameObject.transform.localScale from Vector3(1, 1, 1) to Vector3(2, 10, 2);

    Code (CSharp):
    1.      
    2.         List<NavMeshBuildSource> CollectSources()
    3.         {
    4.             var sources = new List<NavMeshBuildSource>();
    5.             var markups = new List<NavMeshBuildMarkup>();
    6.  
    7.             List<NavMeshModifier> modifiers;
    8.             if (m_CollectObjects == CollectObjects.Children)
    9.             {
    10.                 modifiers = new List<NavMeshModifier>(GetComponentsInChildren<NavMeshModifier>());
    11.                 modifiers.RemoveAll(x => !x.isActiveAndEnabled);
    12.             }
    13.             else
    14.             {
    15.                 modifiers = NavMeshModifier.activeModifiers;
    16.             }
    17.  
    18.             foreach (var m in modifiers)
    19.             {
    20.                 if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
    21.                     continue;
    22.                 if (!m.AffectsAgentType(m_AgentTypeID))
    23.                     continue;
    24.                 var markup = new NavMeshBuildMarkup();
    25.                 markup.root = m.transform;
    26.                 markup.overrideArea = m.overrideArea;
    27.                 markup.area = m.area;
    28.                 markup.ignoreFromBuild = m.ignoreFromBuild;
    29.                 markups.Add(markup);
    30.             }
    31.  
    32.             if (m_CollectObjects == CollectObjects.All)
    33.             {
    34.                 NavMeshBuilder.CollectSources(null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    35.             }
    36.             else if (m_CollectObjects == CollectObjects.Children)
    37.             {
    38.                 NavMeshBuilder.CollectSources(transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    39.             }
    40.             else if (m_CollectObjects == CollectObjects.Volume)
    41.             {
    42.                 Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
    43.                 var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
    44.                 NavMeshBuilder.CollectSources(worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
    45.             }
    46.  
    47.             /***** Terrain trees *****/
    48.  
    49.             var fakeGameObject = new GameObject();
    50.             // Required to have a Transform instance to call localToWorldMatrix (I'm fairly bad at mathematics so maybe there is an other solution !)
    51.  
    52.             var size = Terrain.activeTerrain.terrainData.size;
    53.             // Do not use Terrain.activeTerrain but maybe add a properties or detect terrains inside the bounding box of NavMeshSurface
    54.             // Then simply loop through all selected terrains
    55.             foreach (var tree in Terrain.activeTerrain.terrainData.treeInstances)
    56.             {
    57.                 var prototype = Terrain.activeTerrain.terrainData.treePrototypes[tree.prototypeIndex];
    58.                 // Use prototype.prefab to reach the prefab of the tree
    59.                 // not used in this example
    60.  
    61.                 fakeGameObject.transform.position = new Vector3(tree.position.x * size.x, tree.position.y * size.y, tree.position.z * size.z);
    62.                 fakeGameObject.transform.position += Terrain.activeTerrain.GetPosition();
    63.                 fakeGameObject.transform.rotation = Quaternion.AngleAxis(tree.rotation, Vector3.up);
    64.                 fakeGameObject.transform.localScale = new Vector3(2, 10, 2);
    65.  
    66.                 var src = new NavMeshBuildSource();
    67.                 src.transform = fakeGameObject.transform.localToWorldMatrix;
    68.                 src.shape = NavMeshBuildSourceShape.Box; // update this to your convenience
    69.                 src.size = new Vector3(1f, 1f, 1f); // update this according to tree heightScale / widthScale and the prefab size.
    70.                 sources.Add(src);
    71.             }
    72.             DestroyImmediate(fakeGameObject);
    73.  
    74.             /***** End ofTerrain trees *****/
    75.  
    76.             if (m_IgnoreNavMeshAgent)
    77.                 sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshAgent>() != null));
    78.  
    79.             if (m_IgnoreNavMeshObstacle)
    80.                 sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshObstacle>() != null));
    81.  
    82.             AppendModifierVolumes(ref sources);
    83.  
    84.             return sources;
    85.         }
    86.  
     
    Last edited: Oct 23, 2018
    cmart likes this.
  20. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Thank you so much, that was a lot to get me started on the solution I needed. I made some adjustments, including optimizations with getting arrays from the Unity API multiple times, and avoiding the need to create GameObjects. I used Matrix4x4.TRS(...) instead to calculate the localToWorld matrix, even though I don't know how to calculate that mathematically, myself yet ;) haha.


    My version (below) adds support for trees with NavMeshObstacles on their prefab's root GameObject. Specifically, if it DOES find a NavMeshObstacle on the prefab's root GameObject, it will add that as a source to the list, and use the center and sizing of the obstacle.

    I left my commented-out Debug.Log(...) calls in case you wanted to easily uncomment them and see what's going on.

    I can only hope that Unity includes something this vital for the NavMeshComponents. If I get time, I may request it to them, and say "hey, it works already! Here, .. please!!!" haha. Goodluck, and hope this helps :)

    Code (CSharp):
    1. // --- --- --- Terrain trees --- --- ---
    2.  
    3. Terrain terrain = Terrain.activeTerrain;
    4. TerrainData terrainData = terrain.terrainData;
    5. Vector3 size = Terrain.activeTerrain.terrainData.size;
    6. Vector3 terrainPos = terrain.GetPosition();
    7. //Debug.Log("terrainPos = " + terrainPos);
    8. int treesArea = NavMesh.GetAreaFromName("Not Walkable");
    9. if (treesArea < 0) {
    10.     Debug.LogError("Unrecognized area name! The default area will be used instead.");
    11.     treesArea = 0;
    12. }
    13.  
    14. TreePrototype[] treePrototypes = terrainData.treePrototypes;
    15. TreeInstance[] treeInstances = terrainData.treeInstances;
    16. for (int i = 0; i < treeInstances.Length; i++) {
    17.     //treeInstances[i] is the current ACTUAL tree we're going over.
    18.     //the tree prototype is the "template" used by this tree.
    19.     TreePrototype prototype = treePrototypes[treeInstances[i].prototypeIndex];
    20.     GameObject prefab = prototype.prefab;
    21.     NavMeshObstacle obstacle = prefab.GetComponent<NavMeshObstacle>();
    22.     if (obstacle == null)
    23.         continue;
    24.  
    25.     //Debug.Log("treeInstances[" + i + "] info:\n" + treeInstances[i].position + " " + treeInstances[i].rotation + " " + treeInstances[i].widthScale + " " + treeInstances[i].heightScale);
    26.     Vector3 worldTreePos = terrainPos + Vector3.Scale(treeInstances[i].position, size)
    27.         + obstacle.center;
    28.     Quaternion worldTreeRot = Quaternion.Euler(0, treeInstances[i].rotation * Mathf.Rad2Deg, 0);
    29.     Vector3 worldTreeScale = new Vector3(treeInstances[i].widthScale, treeInstances[i].heightScale, treeInstances[i].widthScale);
    30.     //Debug.Log("CREATED MATRIX FOR TRS:\nworldTreePos = " + worldTreePos + "\nworldTreeRot = " + worldTreeRot + "\nworldTreeScale = " + worldTreeScale);
    31.    
    32.     NavMeshBuildSource src = new NavMeshBuildSource();
    33.     src.transform = Matrix4x4.TRS(worldTreePos, worldTreeRot, worldTreeScale);
    34.  
    35.     switch (obstacle.shape) {
    36.         case NavMeshObstacleShape.Capsule:
    37.             src.shape = NavMeshBuildSourceShape.Capsule;
    38.  
    39.             //Unity 2019.2.0f1: BUG!! navMeshObstacle.height returns exactly HALF of the actual height of the obstacle.
    40.             //Use the size property instead.
    41.             src.size = obstacle.size;
    42.             break;
    43.         case NavMeshObstacleShape.Box:
    44.             src.shape = NavMeshBuildSourceShape.Box;
    45.             src.size = obstacle.size;
    46.             break;
    47.         default:
    48.             Debug.LogError("Unsupported type of " + typeof(NavMeshObstacleShape).Name
    49.                 + " for the building of the " + typeof(NavMeshSurface).Name + "! (" + obstacle.shape + ")");
    50.             break;
    51.     }
    52.     src.size = Vector3.Scale(src.size, prefab.transform.localScale);
    53.     //Debug.Log("src.size = " + src.size);
    54.     src.area = treesArea;
    55.     sources.Add(src);
    56. }
    57.  
    58. // --- --- --- End of Terrain trees --- --- ---


    [EDIT]: This code should go in the NavMeshSurface.CollectSources() method. Exactly where may depend on the version of the NavMeshComponents repo that you cloned, I used the 2019.1 branch. I put it right before the lines that use these "ignore" options:

    Code (CSharp):
    1. if (m_IgnoreNavMeshAgent)
    2.     sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshAgent>() != null));
    3.  
    4. if (m_IgnoreNavMeshObstacle)
    5.     sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshObstacle>() != null));
    This should be near the end of the method where it returns the sources.
     
    Last edited: Sep 4, 2019
  21. id0

    id0

    Joined:
    Nov 23, 2012
    Posts:
    455
    Not working for me at all. Trees not baking. I replace the code, add obstacle... What am I doing wrong?
     
  22. AsicsoN

    AsicsoN

    Joined:
    May 28, 2019
    Posts:
    5
    Thanks a lot ModLunar! Works in hdrp 2019.2.12 by placing code in same location you did.
     
    AnanasikDeveloper and ModLunar like this.
  23. Zephyros0

    Zephyros0

    Joined:
    Feb 8, 2020
    Posts:
    1
    it's too late, but maybe you have multiple terrains. this solution works only for active terrain (main)
     
    Swearsoft likes this.
  24. Swearsoft

    Swearsoft

    Joined:
    Mar 19, 2009
    Posts:
    1,632
    Ok Guys, I fixed the script to work with multiple terrains

    Code (CSharp):
    1.  
    2. // --- --- --- Terrain trees --- --- ---
    3.  
    4.             Terrain[] terrains = GameObject.FindObjectsOfType<Terrain>();
    5.  
    6.             if(terrains.Length > 0 )
    7.             {
    8.                 foreach (Terrain item in terrains)
    9.                 {
    10.                     Terrain terrain = item.GetComponent<Terrain>();
    11.                     TerrainData terrainData = terrain.terrainData;
    12.                     Vector3 size = Terrain.activeTerrain.terrainData.size;
    13.                     Vector3 terrainPos = terrain.GetPosition();
    14.                     //Debug.Log("terrainPos = " + terrainPos);
    15.                     int treesArea = NavMesh.GetAreaFromName("Not Walkable");
    16.                     if (treesArea < 0)
    17.                     {
    18.                         Debug.LogError("Unrecognized area name! The default area will be used instead.");
    19.                         treesArea = 0;
    20.                     }
    21.  
    22.                     TreePrototype[] treePrototypes = terrainData.treePrototypes;
    23.                     TreeInstance[] treeInstances = terrainData.treeInstances;
    24.                     for (int i = 0; i < treeInstances.Length; i++)
    25.                     {
    26.                         //treeInstances[i] is the current ACTUAL tree we're going over.
    27.                         //the tree prototype is the "template" used by this tree.
    28.                         TreePrototype prototype = treePrototypes[treeInstances[i].prototypeIndex];
    29.                         GameObject prefab = prototype.prefab;
    30.                         NavMeshObstacle obstacle = prefab.GetComponent<NavMeshObstacle>();
    31.                         if (obstacle == null)
    32.                             continue;
    33.  
    34.                         //Debug.Log("treeInstances[" + i + "] info:\n" + treeInstances[i].position + " " + treeInstances[i].rotation + " " + treeInstances[i].widthScale + " " + treeInstances[i].heightScale);
    35.                         Vector3 worldTreePos = terrainPos + Vector3.Scale(treeInstances[i].position, size)
    36.                             + obstacle.center;
    37.                         Quaternion worldTreeRot = Quaternion.Euler(0, treeInstances[i].rotation * Mathf.Rad2Deg, 0);
    38.                         Vector3 worldTreeScale = new Vector3(treeInstances[i].widthScale, treeInstances[i].heightScale, treeInstances[i].widthScale);
    39.                         //Debug.Log("CREATED MATRIX FOR TRS:\nworldTreePos = " + worldTreePos + "\nworldTreeRot = " + worldTreeRot + "\nworldTreeScale = " + worldTreeScale);
    40.  
    41.                         NavMeshBuildSource src = new NavMeshBuildSource();
    42.                         src.transform = Matrix4x4.TRS(worldTreePos, worldTreeRot, worldTreeScale);
    43.  
    44.                         switch (obstacle.shape)
    45.                         {
    46.                             case NavMeshObstacleShape.Capsule:
    47.                                 src.shape = NavMeshBuildSourceShape.Capsule;
    48.  
    49.                                 //Unity 2019.2.0f1: BUG!! navMeshObstacle.height returns exactly HALF of the actual height of the obstacle.
    50.                                 //Use the size property instead.
    51.                                 src.size = obstacle.size;
    52.                                 break;
    53.                             case NavMeshObstacleShape.Box:
    54.                                 src.shape = NavMeshBuildSourceShape.Box;
    55.                                 src.size = obstacle.size;
    56.                                 break;
    57.                             default:
    58.                                 Debug.LogError("Unsupported type of " + typeof(NavMeshObstacleShape).Name
    59.                                     + " for the building of the " + typeof(NavMeshSurface).Name + "! (" + obstacle.shape + ")");
    60.                                 break;
    61.                         }
    62.                         src.size = Vector3.Scale(src.size, prefab.transform.localScale);
    63.                         //Debug.Log("src.size = " + src.size);
    64.                         src.area = treesArea;
    65.                         sources.Add(src);
    66.                     }
    67.                 }
    68.             }        
    69.  
    70.             // --- --- --- End of Terrain trees --- --- ---
    71.  
    Insert bfore "if (m_IgnoreNavMeshAgent)"
     
    Last edited: Aug 28, 2020
    ModLunar likes this.
  25. Swearsoft

    Swearsoft

    Joined:
    Mar 19, 2009
    Posts:
    1,632
    fixed it for multiple terrains
     
  26. whereisthemonkey

    whereisthemonkey

    Joined:
    Apr 21, 2019
    Posts:
    9
  27. Trumped

    Trumped

    Joined:
    Jul 3, 2017
    Posts:
    5
    I had this same problem came up with a temporary fix, hopefully its not too CPU taxing I only really needed to do it for a small area where the trees are all the same size.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class navMeshTreeBuilding : MonoBehaviour
    6. {
    7.     public GameObject navmeshobstacle;
    8.     public Transform terrain;
    9.  
    10.     private void Start()
    11.     {
    12.         foreach (var tree in Terrain.activeTerrain.terrainData.treeInstances)
    13.         {
    14.             GameObject obj = Instantiate(navmeshobstacle, transform.position, transform.rotation, Terrain.activeTerrain.transform);
    15.             var size = Terrain.activeTerrain.terrainData.size;
    16.             obj.transform.position = new Vector3(tree.position.x * size.x + terrain.transform.position.x, tree.position.y * size.y + terrain.transform.position.y, tree.position.z * size.z + terrain.transform.position.z);
    17.         }
    18.     }
    19. }
     
    akuno likes this.
  28. Dieee

    Dieee

    Joined:
    Apr 25, 2013
    Posts:
    6
    Is there Unity news for this?
     
  29. benthroop

    benthroop

    Joined:
    Jan 5, 2007
    Posts:
    262
    This is the most ridiculous issue Unity. I can't believe I have to solve this.
     
    jumisko likes this.
  30. jmfp92

    jmfp92

    Joined:
    Dec 15, 2017
    Posts:
    11
    I actually think I came up with a fairly efficient solution to this problem. I sort of did what @Trumped did just slightly different. It is INCREDIBLY inefficient at start but once it's finished running it's like it was never even there. This worked for me perfectly, hope this helps someone

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.AI;
    5.  
    6. public class TerrainTrees : MonoBehaviour
    7. {
    8.     [SerializeField]
    9.     Terrain terrain;
    10.     [SerializeField]
    11.     NavMeshSurface surface;
    12.     [SerializeField]
    13.     float volumeSizeOffset = 2f;
    14.  
    15.     private void Start()
    16.     {
    17.         TerrainData data = terrain.terrainData;
    18.         //making a list of gameobjects so we can delete the extra objects that are generated considering
    19.         //most of us will have thousands of trees on our terrains
    20.         List<GameObject> trees = new List<GameObject>();
    21.         //loop through each tree in the terrain, create a gameobject and place it in the same position as the tree
    22.         //then, give that object a NavMeshModifierVolume component of the specified size
    23.         //then rebake the navmesh and delete all of the objects that were generated. The navmesh will remain baked with
    24.         //the unwalkable areas leftover
    25.         foreach (TreeInstance item in data.treeInstances)
    26.         {
    27.             //creating an empty gameobject to use as a "prefab"
    28.             GameObject empty = new GameObject();
    29.             //instantiating the empty object at the correct position
    30.             GameObject fakeTree = Instantiate(empty, transform.position, Quaternion.identity, terrain.transform);
    31.             //getting the size of the terrain
    32.             var size = data.size;
    33.             fakeTree.transform.position = new Vector3(item.position.x * size.x + terrain.transform.position.x, item.position.y * size.y + terrain.transform.position.y, item.position.z * size.z + terrain.transform.position.z);
    34.             NavMeshModifierVolume mod = fakeTree.AddComponent<NavMeshModifierVolume>();
    35.             mod.size = new Vector3(volumeSizeOffset, 3, volumeSizeOffset);
    36.             //making volume not walkable
    37.             mod.area = 1;
    38.             trees.Add(fakeTree);
    39.             trees.Add(empty);
    40.         }
    41.         //rebuilding navmesh
    42.         surface.BuildNavMesh();
    43.         //destroying the thousands of gameobjects that were created for each tree
    44.         foreach (GameObject tree in trees)
    45.         {
    46.             Destroy(tree);
    47.         }
    48.     }
    49. }
     
    id0 likes this.
  31. RPKMN1

    RPKMN1

    Joined:
    Feb 13, 2017
    Posts:
    36
    that works so long as you never plan on updating your navmesh again.
     
  32. jorn818

    jorn818

    Joined:
    May 12, 2013
    Posts:
    100
    I made a version that uses NavMeshObstacle instead of ModifierVolume.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.AI;
    6.  
    7. public class TerrainTrees : MonoBehaviour
    8. {
    9.     [SerializeField]
    10.     Terrain terrain;
    11.     [SerializeField]
    12.     public NavMeshSurface surface;
    13.     [SerializeField]
    14.     float volumeSizeOffset = 0.3f;
    15.     GameObject TerrainObject;
    16.     private void Start()
    17.     {
    18.         //TerrainObject = this.gameObject;
    19.         //surface = TerrainObject.GetComponent<NavMeshSurface>();
    20.         terrain = GetComponentInChildren<Terrain>();
    21.        
    22.         TerrainData data = terrain.terrainData;
    23.        
    24.         //making a list of gameobjects so we can delete the extra objects that are generated considering
    25.         //most of us will have thousands of trees on our terrains
    26.         List<GameObject> trees = new List<GameObject>();
    27.         //loop through each tree in the terrain, create a gameobject and place it in the same position as the tree
    28.         //then, give that object a NavMeshObstacle component of the specified size
    29.         //then rebake the navmesh and delete all of the objects that were generated. The navmesh will remain baked with
    30.         //the unwalkable areas leftover
    31.         foreach (TreeInstance item in data.treeInstances)
    32.         {
    33.             //creating an empty gameobject to use as a "prefab"
    34.             GameObject empty = new GameObject();
    35.             //instantiating the empty object at the correct position
    36.             GameObject fakeTree = Instantiate(empty, transform.position, Quaternion.identity, terrain.transform);
    37.             //getting the size of the terrain
    38.             var size = data.size;
    39.             fakeTree.transform.position = new Vector3(item.position.x * size.x + terrain.transform.position.x, item.position.y * size.y + terrain.transform.position.y, item.position.z * size.z + terrain.transform.position.z);
    40.             NavMeshObstacle mod = fakeTree.AddComponent<NavMeshObstacle>();
    41.             mod.size = new Vector3(volumeSizeOffset, 3, volumeSizeOffset);
    42.             //making volume not walkable
    43.             //mod.area = 1;
    44.             mod.carving = true;
    45.             trees.Add(fakeTree);
    46.             trees.Add(empty);
    47.         }
    48.         //rebuilding navmesh
    49.         surface.BuildNavMesh();
    50.         //destroying the thousands of gameobjects that were created for each tree
    51.         foreach (GameObject tree in trees)
    52.         {
    53.             Destroy(tree);
    54.         }
    55.     }
    56. }
    [/QUOTE]
     
  33. RPKMN1

    RPKMN1

    Joined:
    Feb 13, 2017
    Posts:
    36
    this doesn't solve the fundamental issue of not being able to ever update your navmeshsurface again at runtime or you lose all that carving since you've destroyed all the game objects that contained the navMeshObstacles.

    Unity just needs to fix this. it's ridiculous.
     
    ModLunar likes this.
  34. flowerMaster

    flowerMaster

    Joined:
    Apr 27, 2013
    Posts:
    33
    Maybe one of the least crappy work arounds is to quickly delete all the trees, bake the navmesh, undo your changes to bring back the trees (undoing the changes won't undo the new navmesh), done. But honestly, Unity never ceases to amaze me with lame things like this.
     
  35. Inxentas

    Inxentas

    Joined:
    Jan 15, 2020
    Posts:
    278
    I have resorted to only using actual terrain trees when the player won't interact with them (and they are part of a background or whatever). For actual trees I stared using fully prepped prefabs (with a NavMeshObstacle and LOD component) using PolyBrush. I got sick of the restrictive nature of terrain trees and it's not like I need every tree to be super efficient. YMMV in large forest scenes but for the odd tree I want to place it's a perfect solution.

    If it is a shrub you can walk through, I would use terrain trees. If it uses any kind of physics, I use prefabs.
     
  36. spilat12

    spilat12

    Joined:
    Feb 10, 2015
    Posts:
    38
    1. Backup the scene
    2. Erase trees
    3. Bake navmesh
    4. Press Ctrl+Z a few times to bring back the trees, this won't affect the navmesh you baked
    5. Done!
     
  37. RPKMN1

    RPKMN1

    Joined:
    Feb 13, 2017
    Posts:
    36
    I don't mean to be rude, but as mentioned a couple times in this thread, we're talking about the NavMeshSurface component, whose entire point for existing is to allow for runtime regeneration of the navmesh.

    Even if we weren't talking about that, having to do something so janky is kind of ridiculous in a first-class engine.
     
    jumisko likes this.
  38. jumisko

    jumisko

    Joined:
    Feb 18, 2020
    Posts:
    13
    This is still an issue. I can't use the painting of terrain trees at all and have them be obstacles. I've tried using layers and even adding components to the prefabs, but they only work if I drag and drop the prefab onto the terrain manually. There should be no need for additional coding for this - it should just work to use the terrain tools. Ironic, considering they are touting this as AI Navigation. What's intelligent about it?