Here is a minimum example of baking a NavMesh at runtime without using the abandoned NavMesh components on GitHub. Check out the navmesh documentation to see what each parameter does. There is very little chance this will generate a usable navmesh using the default settings.
// This should be global in the class
NavMeshDataInstance navMeshDataInstance;
public LayerMask includedLayerMask;
private void BuildNavMesh(Transform xform)
{
// delete the existing navmesh if there is one
navMeshDataInstance.Remove();
List<NavMeshBuildSource> buildSources = new List<NavMeshBuildSource>();
NavMeshBuilder.CollectSources(
xform,
includedLayerMask,
navMeshCollectGeometry,
0,
new List<NavMeshBuildMarkup>(),
buildSources);
NavMeshData navData = NavMeshBuilder.BuildNavMeshData(
NavMesh.GetSettingsByID(0),
buildSources,
new Bounds(Vector3.zero, new Vector3(10000, 10000, 10000)),
Vector3.down,
Quaternion.Euler(Vector3.up));
navMeshDataInstance = NavMesh.AddNavMeshData(navData);
}
assign NavMesh Builder to some empty GO sorrounding the plane
assign NavMesh Surface to the plane
in a script i have public reference to NavMesh Surface called āNavSurfaceā where i drag drop my plane
at the start Iām calling: NavSurface.BuildNavMesh(); and myData = NavSurface.navMeshData;
(myData is a private variable name of NavMeshData)
when I want to rebuild a NavMesh in realtime Iām just using NavSurface.UpdateNavMesh(myData);
NavMesh Surface on the plane is set to update Children only and Terrain layer only.
all new spawned in real time objects (like obstacles or NavMeshModifierVolume) have terrain layer and are spawned under plane parent.
Works fine for me.
I had a hard time understanding what was going on here, but rewrote snacktimeās approach a bit and was able to get things working. I also changed it so that almost nothing ever takes place on the main thread because my game is procedural and needs to do everything on the fly. Thus, the Build() function is actually using an empty LayerMask to generate only the framework for a new navmesh, then actually generating it using the async operation.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class NavBuilder : MonoBehaviour {
Vector3 BoundsCenter = Vector3.zero;
Vector3 BoundsSize = new Vector3(512f, 4000f, 512f);
LayerMask BuildMask;
LayerMask NullMask;
NavMeshData NavMeshData;
NavMeshDataInstance NavMeshDataInstance;
void Start() {
AddNavMeshData();
BuildMask = ~0;
NullMask = 0;
}
void Update () {
if (Input.GetKeyDown(KeyCode.B)) {
Debug.Log("Build " + Time.realtimeSinceStartup.ToString());
Build();
Debug.Log("Build finished " + Time.realtimeSinceStartup.ToString());
}
else if (Input.GetKeyDown(KeyCode.U)) {
Debug.Log("Update " + Time.realtimeSinceStartup.ToString());
UpdateNavmeshData();
}
}
void AddNavMeshData() {
if (NavMeshData != null) {
if (NavMeshDataInstance.valid) {
NavMesh.RemoveNavMeshData(NavMeshDataInstance);
}
NavMeshDataInstance = NavMesh.AddNavMeshData(NavMeshData);
}
}
void UpdateNavmeshData() {
StartCoroutine(UpdateNavmeshDataAsync());
}
IEnumerator UpdateNavmeshDataAsync() {
AsyncOperation op = NavMeshBuilder.UpdateNavMeshDataAsync(
NavMeshData,
NavMesh.GetSettingsByID(0),
GetBuildSources(BuildMask),
new Bounds(BoundsCenter, BoundsSize));
yield return op;
AddNavMeshData();
Debug.Log("Update finished " + Time.realtimeSinceStartup.ToString());
}
void Build() {
NavMeshData = NavMeshBuilder.BuildNavMeshData(
NavMesh.GetSettingsByID(0),
GetBuildSources(NullMask),
new Bounds(BoundsCenter, BoundsSize),
Vector3.zero,
Quaternion.identity);
AddNavMeshData();
}
List<NavMeshBuildSource> GetBuildSources(LayerMask mask) {
List<NavMeshBuildSource> sources = new List<NavMeshBuildSource>();
NavMeshBuilder.CollectSources(
new Bounds(BoundsCenter, BoundsSize),
mask,
NavMeshCollectGeometry.PhysicsColliders,
0,
new List<NavMeshBuildMarkup>(),
sources);
Debug.Log("Sources found: " + sources.Count.ToString());
return sources;
}
}
note that this does require you download and install the git components, or at least does in 2017.1 which I am still running. I ran into some posts of increased navmesh performance in 2017.2 or 3 and will probably upgrade soon.
Now all I need is to write a tile management system for this and a linking system and I should have fully async procedural navmesh. Thank you very much for sharing the code!
But arenāt you deleting the whole navmesh when you reconstruct it rather than just updating only the parts of the navmesh that have not been affected by the updates to your transforms/meshes in the game world.
Iāve also found you canāt update areas with the modifier without re-bakeing the whole navmesh. Or you canāt even use the modifier at all if your object has a source tag component, so no chance of changing its area type in that situation. The API seems to do everything in its power to be non flexible and poor performance by unity as usual.
Iām not aware of the āmodifierā youāre talking about. I do rebake the area entirely every time a change is made, but it takes less than 1 second off thread now that I use:
a bounds size of 103.4
tileSize = 100
voxelSize = 0.15
I store all the datas and instances in separate <xPos,<zPos, var>> dictionaries so I can remove them from the system if they get too far or update them as necessary based on a simple SomethingChanged(Vector3 pos) call. That call generates a Vector2 (I use an Int2 custom class) that then stores it in a AreasToUpdate list. During Update(), the NavBuilder will either generate a new area from the AreasToAdd list, update an area in the AreasToUpdate list, or remove an area from the AreasToRemove list, one thing at a time in that order.