NavMeshSurface UpdateNavMesh with a Entity?

Hello Guys,

i want to use the NagAgents in ECS therefore i used this github: GitHub - zulfajuniadi/unity-ecs-navmesh: A demo implementation of Unity Entity Component System with NavMesh
The Problem is that i dont know how to add my Wall Entitys to the “NavMeshSurface UpdateNavMesh” to Update my NavMeshSurface.

The Wall is created at Runtime with the Button and 2 Mousclicks.
Here is a Picture of my Wall Entitys(the Entitys behind work with the NavAgent Component):
5101010--502985--upload_2019-10-24_8-10-32.png

I think adding a navmesh obstacle component to the walls is what you want to do.

Dont work because its no Entity Component, it dont apply to it.

You could try creating a prefab and using IConvertToEntity and InjectOriginalGameObject ticked. Then use dstManager.AddComponentData(new CopyTransformFromGameObject{}). https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/Assets/HelloCube/5.%20SpawnFromMonoBehaviour/Spawner_FromMonoBehaviour.cs

He wants a pure approach, which is totally doable as I’ve done it a while back (6-12months ago). Having a quick look at an old repo to refresh my memory, it seems you can use NavMeshBuilder.UpdateNavMeshDataAsync

2 Likes

@riskparitygawd i know i can do this but i want a pure approach. Therefore is no Component to apply.
@tertle can you give me a insight how you did the pure approach without InjectOriginalGameObject?

There is no pure approach for navmesh build sources, because meshes and Physx colliders are the only two options.

I’m pretty certain meshes exist in a pure ECS solution.

So to do this, i did create conversion for the existing components but the main thing you need to understate is how to generate navmeshes from mesh data only without the Unity components.
I’d post code but its 7month out of date but let me give you the basics

Quick bit of setup.

private Bounds bounds;
private NavMeshData navMeshData;
private NavMeshDataInstance navMeshDataInstance;

protected override void OnCreate()
{
   this.navMeshData = new NavMeshData();
   this.navMeshDataInstance = NavMesh.AddNavMeshData(this.navMeshData);
   this.bounds = new Bounds(Vector3.zero, new Vector3(8192, 256, 8192)); // whatever you want. for performance you can create multiple nav meshes and only update parts etc but to start just do it in one.
}

protected override void OnUpdate()
{
   this.UpdateMeshLink();
   this.UpdateNavMesh();
}

For links I won’t go into much detail. Once you get what’s going on these will make more sense anyway, but you can add nav mesh links with
https://docs.unity3d.com/ScriptReference/AI.NavMesh.AddLink.html

while (this.newLinks.TryDequeue(out var l))
{
   NavMeshLinkInstance linkHandle = NavMesh.AddLink(l.NavMeshLinkData, l.Translation, l.Rotation);
   var result = this.linkHandles.TryAdd(l.Entity, linkHandle);
   Assert.IsTrue(result);
}

And you can remove nav mesh link with
https://docs.unity3d.com/ScriptReference/AI.NavMesh.RemoveLink.html

while (this.removeLinks.TryDequeue(out var entity))
{
   var result = this.linkHandles.TryGetValue(entity, out var linkHandle);
   Assert.IsTrue(result);
   this.linkHandles.Remove(entity);
   NavMesh.RemoveLink(linkHandle);
}

These queues are populated in a job from a link IComponentData I created.

To build your actual NavMesh you need to use
https://docs.unity3d.com/ScriptReference/AI.NavMeshBuilder.UpdateNavMeshDataAsync.html

This takes
AI.NavMeshData data
AI.NavMeshBuildSettings buildSettings
List sources
Bounds localBounds

You can get your settings from the scene using
var buildSettings = NavMesh.GetSettingsByID(0);
or customize it yourself.

With that you have the data, buildSettings and localBounds fields. So you need to generate your sources, which is the most important part.

https://docs.unity3d.com/ScriptReference/AI.NavMeshBuildSource.html

You can either use primitive shapes (easy) or meshes which takes a little more work.
This was done in a burst job so as meshes can’t be used I grabbed the meshIndex and stored that in a temp stash.

/// <inheritdoc />
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
   var meshInstanceIndex = chunk.GetSharedComponentIndex(this.MeshInstanceRendererType);
   var localToWorlds = chunk.GetNativeArray(this.LocalToWorldType);
   var updateNavMeshEvents = chunk.GetNativeArray(this.UpdateNavMeshEventType);

   for (var index = 0; index < chunk.Count; index++)
   {
       var transform = localToWorlds[index].Value;
       var navMeshSource = updateNavMeshEvents[index];

       if (navMeshSource.Shape == NavMeshBuildSourceShape.Mesh)
       {
           if (meshInstanceIndex != -1)
           {
               this.Meshes.Enqueue(
                   new MeshStash
                   {
                       Area = navMeshSource.Area,
                       Transform = transform,
                       MeshIndex = meshInstanceIndex,
                   });
           }
       }
       else
       {
           this.Source.Enqueue(new NavMeshBuildSource
           {
               shape = navMeshSource.Shape,
               area = navMeshSource.Area,
               transform = transform,
               size = navMeshSource.Size,
           });
       }
   }
}

The stash was just

private struct MeshStash
{
   public Matrix4x4 Transform;
   public int Area;
   public int MeshIndex;
}

Then back on the main thread, I would take this and get the RenderMesh from the index and build my list of sources.

while (this.meshes.TryDequeue(out var mesh))
{
   var renderMesh = this.EntityManager.GetSharedComponentData<RenderMesh>(mesh.MeshIndex);

   if (renderMesh.mesh == null)
   {
       continue;
   }

   this.sources.Add(new NavMeshBuildSource
   {
       shape = NavMeshBuildSourceShape.Mesh,
       sourceObject = renderMesh.mesh,
       transform = mesh.Transform,
       area = mesh.Area,
   });
}

Once that is done, it’s as simple as calling update

var buildSettings = NavMesh.GetSettingsByID(0);

this.operation = NavMeshBuilder.UpdateNavMeshDataAsync(
   this.navMeshData,
   buildSettings,
   this.sources,
   this.bounds);
20 Likes

@tertle thank you that helped me a lot, im now at implement the functions in my game :slight_smile:

@tertle I about broke my finger liking this. Hugs