I’ve built a way for the player to modify the terrain height in-game, all is working great - the last piece is to update the nav mesh so that AI’s see the new shape of the terrain.
I’ve tried NavMeshSurface.BuildNavMesh() and it works, but is slow - and doesn’t make sense to rebake the mesh for the entire terrain when only a small portion has been modified.
So I set NavMeshSurface.center and NavMeshSurface.size - which “works” - the area in that volume is updated, but the rest of the nav mesh is gone.
Is there any way to re-bake just a portion of the nav mesh?
In searching for ways to do this I found NavMeshBuilder.UpdateNavMeshData which might be a way as it does specify a bounds, but can only find snippets of code here and there - anyone have a good explanation of how to use?
Also see an example script on the Nav Mesh Components page on github called MeshTool.cs which seems to be a way to move nav mesh verticies - interesting idea, if I could just move the nav mesh verts at the same time I am adjusting the terrain height map I’d be golden.
Anyone?
Thanks.
I know its been a while since this has been asked, but I have an answer on how to use NavMeshBuilder.UpdateNavMeshDataAsync which, while not the same a NavMeshBuilder.UpdateNavMeshData should still help.
Also, another important qualifier, I have not yet tested to see if this works with modifying meshes but I plan to work on such a feature in the near future and update this answer accordingly. What I have verified is that this works with adding meshes to an existing surface and updating the navmesh of said surface accordingly.
using System.Collections; // for IEnumerator
using UnityEngine.AI; // for navmesh stuff
// keep track of NavMeshBuildSources manually
List<NavMeshBuildSource> sources;
NavMeshSurface surface; //surface which will include the mesh modified
//...
void UpdateSurface()
{
// apply changes to modified mesh -prior- to running this function
StartCoroutine( UpdateNavMeshAsyncCoroutine(surface) );
}
public IEnumerator UpdateNavMeshAsyncCoroutine(NavMeshSurface surface)
{
var operation = NavMeshBuilder.UpdateNavMeshDataAsync(
surface.navMeshData,
surface.GetBuildSettings(),
sources,
new Bounds(Vector3.zero, new Vector3(1000,1000,1000)) // set these accordingly
);
do { yield return null; } while ( !operation.isDone );
// any logic that you want run -after- the update is applied goes here
}
This should do the trick. Again, I haven’t tested this with mesh modification yet, but I believe it will work
Thanks for posting this - just saw it the other day. I’ve been playing with this and it kinda works but I have the same problem I had before with this - the area in the Bounds is updated and the rest of the nav mesh is gone.
If I set the bounds to be the entire terrain it works fine - but ideally I’d only update the changed area.
Also realized that one thing I was doing wrong before was that I had baked the builtin terrain mesh so by adding the NavMeshSurface component I had 2 nav meshes. The rebuild was updating the NavMeshSurface but the agent was using the built in terrain. If I clear the builtin nav mesh it works, except the problem described above.
Hey, just a heads up before I continue, I haven’t touched my NavMesh code since I last worked on it in September so I’m pretty rusty and I might miss an important detail here or there.
First off, can you clarify what you mean by ‘builtin terrain mesh’? I’m not certain whether you are referring to a GameObject with a Terrain component or just a GameObject with a mesh that you are using as terrain.
Secondly, when you call UpdateNavMeshAsyncCoroutine or UpdateNavMeshData do you pass the list of all the sources or just put the single, modified mesh in and pass that? The documentation for UpdateNavMeshData states that each source (or mesh) is checked to see if it has changed so that unchanged portions don’t get rebaked when they don’t need to. However, if you are changing a small portion of a very large mesh this may not help much.
Thirdly, can you verify that the NavMeshSurface of the builtin terrain and the one from the component are two, different instances? I would just recommend that you change the NavAgents target surface to your new component NavMeshSurface or, better yet, just use the pre existing terrains NavMeshSurface