Search Unity

100,000 entities traversing navmesh

Discussion in 'Entity Component System' started by zulfajuniadi, Jun 4, 2018.

  1. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Hey guys,

    I've created a sample project using ECS to query the navmesh and moving 100,000 entities at the same time. I'm getting around 40-50 fps with mid range desktop. Right now the bottleneck is the command buffers that can't be called in parallel inside the IJobProcessComponentData. I believe if that is solved it can easily scale to a few hundred thousand.

    The buildings are generated via the MapBox Unity API (you can change the location) and the navmesh is baked afterwards.

    Navmesh path query results are heavily cached so that helps a lot.

    Video:



    Github:

    https://github.com/zulfajuniadi/unity-ecs-navmesh
     
    Last edited: Jun 26, 2018
  2. Defiant_Games

    Defiant_Games

    Joined:
    Jul 18, 2012
    Posts:
    6
    Great sample, thanks for sharing.
     
    zulfajuniadi likes this.
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Good stuff. One question please. I'm digging your solution, and can't find, how you move capsules :) I saw systems where you attach needed tags components, calculate path corners and cache them, but I can't see moving directly. Kick me to the right class please :)
     
  4. chanfort

    chanfort

    Joined:
    Dec 15, 2013
    Posts:
    641
    Nice! The next step would be local avoidance, that agents would not be going through each other.
     
  5. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    It's implemented via the built in MoveForwardSystem. Anything with these components will move the entity forward.

    upload_2018-6-5_20-10-4.png
     
    eizenhorn likes this.
  6. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Yeah local avoidance is pretty tricky to achieve at this scale as per demonstrated in the boids demo
     
  7. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Oh, yeah.... Heading for direction, MoveSpeed for, suddenly, for speed :) and MoveForward, of course for directly movement....thx for right "heading" :D
     
    zulfajuniadi likes this.
  8. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Hey guys. I've updated the code to fully utilise the job system and ECS. Right now the only bottleneck is the mesh rendering system where DrawMeshInstanced can't be called outside of the main thread.

    The navmesh queries are also jobified which means that it will run on all threads. In my tests it shows that I'm able to query resolve around 1000 paths per second. I've moved all the navmesh querying code into one NavMeshQuerySystem.cs file. You can drop that file into your project and start querying the navmesh.

    upload_2018-6-12_3-56-20.png
     
    psuong, hadynlander, Djayp and 4 others like this.
  9. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I am impressed with your work. I guess for local avoidance one option would be the Physx threaded queries for ovelap checks.
     
  10. chanfort

    chanfort

    Joined:
    Dec 15, 2013
    Posts:
    641
    Nice! Did you tried using DrawMeshInstancedIndirect and pass positions and rotations through the shader? That could improve performance further. I think in Nordeus demo DrawMeshInstancedIndirect is called outside the main thread, but need to check that...
     
  11. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    I'm sure the unity team is working on exposing some physics api / components over ECS. Once thats in it'll be much more simpler to do local avoidance.

    Is there a way to access Physx via the Unity C# API?
     
  12. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Yes it does seem that the demo does use DrawMeshInstancedIndirect to render the arrows. I'll have a look into it later. Thanks.

    Right now I'm working on the reusability of the nav agent component so that it can be dropped into any ECS enabled project and be used.
     
  13. tarahugger

    tarahugger

    Joined:
    Jul 18, 2014
    Posts:
    129
    Awesome work, thanks for sharing!
     
  14. avvie

    avvie

    Joined:
    Jan 26, 2014
    Posts:
    74
    thoroughly enjoyed this ! there is so much stuff to play around. gjgjgjgj
     
  15. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I think it already is available to ECS or will be soon, I know raycasts are, and they might actually be enough for your purposes.
     
  16. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Yes I'm aware that raycast jobs are available, but implementing that would mean i would need a gameobject for each unit and a collider too. It won't be "pure" ECS anymore :p
     
  17. chanfort

    chanfort

    Joined:
    Dec 15, 2013
    Posts:
    641
    I agree, I think the best here would be to have ECS alternative for NavMeshAgent component with its local avoidance. And also NavMeshAgentSystem.

    As for PhysX, I think only colliders can make local avoidance. But I think PhysX limits are around 1k colliders, so it's quite far from 100k. In boids demo it was used "sparse" grid. While in Nordeus demo there is implemented Crowd System. I think these could be good directions to look further when going towards 100k.
     
  18. Vanamerax

    Vanamerax

    Joined:
    Jan 12, 2012
    Posts:
    938
    I tried to get your project running from github but unfortunately when pressing play I get an error from Mapbox that is tries to acces a non existing path. Did you forget to include some files in git or did I miss a configuration step?

    Would love to try this out myself
     
  19. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Yeah this project is turning into a plug and play solution for jobified navmesh thus i'm going to remove the existing dependency on mapbox and upload to a new repo.

    I'll be adding a couple more examples such as a hybrid approach with rigidbodies and colliders soon so stay tuned.

    But the current repo should work. I just pulled a fresh one a couple of days ago.
     
    Last edited: Jun 12, 2018
  20. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Sparse grid is okay I guess for a demo with a fixed size grid. I'm not sure how it would work on maps with dynamic nav meshes that can change sizes. I wouldn't want to impose any limitations on the map size even if it were via an adjustable property.

    I guess for the time being using kinematic rigidbodies and colliders with game object entities would suffice until there's a ECS solution for physics. I would be happy to have 5000 units running around with local avoidance at 60fps via this method.

    The Unity team did mention that they're working on the physics too.
     
  21. Vanamerax

    Vanamerax

    Joined:
    Jan 12, 2012
    Posts:
    938
    Alright, I will try it again soon then. I did however get errors with the following stacktrace:
    ArgumentException: path
    System.IO.DirectoryInfo.CheckPath (System.String path) (at <e1a80661d61443feb3dbdaac88eeb776>:0)
    System.IO.DirectoryInfo..ctor (System.String path, System.Boolean simpleOriginalPath) (at <e1a80661d61443feb3dbdaac88eeb776>:0)
    (wrapper remoting-invoke-with-check) System.IO.DirectoryInfo..ctor(string,bool)
    System.IO.Directory.CreateDirectoriesInternal (System.String path) (at <e1a80661d61443feb3dbdaac88eeb776>:0)
    System.IO.Directory.CreateDirectory (System.String path) (at <e1a80661d61443feb3dbdaac88eeb776>:0)
    Mapbox.Platform.MbTiles.MbTilesDb.openOrCreateDb (System.String dbName) (at Assets/Packages/Mapbox/Core/mapbox-sdk-cs/Platform/MbTiles/MbTiles.cs:156)
    Mapbox.Platform.MbTiles.MbTilesDb..ctor (System.String tileset, System.Nullable`1[T] maxTileCount) (at Assets/Packages/Mapbox/Core/mapbox-sdk-cs/Platform/MbTiles/MbTiles.cs:38)
    Mapbox.Platform.Cache.MbTilesCache.initializeMbTiles (System.String mapId) (at Assets/Packages/Mapbox/Core/mapbox-sdk-cs/Platform/Cache/MbTilesCache.cs:112)
    Mapbox.Platform.Cache.MbTilesCache.Get (System.String mapId, Mapbox.Map.CanonicalTileId tileId) (at Assets/Packages/Mapbox/Core/mapbox-sdk-cs/Platform/Cache/MbTilesCache.cs:144)
    Mapbox.Platform.Cache.CachingWebFileSource.Request (System.String uri, System.Action`1[T] callback, System.Int32 timeout, Mapbox.Map.CanonicalTileId tileId, System.String mapId) (at Assets/Packages/Mapbox/Core/mapbox-sdk-cs/Platform/Cache/CachingWebFileSource.cs:122)
    Mapbox.Unity.MapboxAccess.Request (System.String url, System.Action`1[T] callback, System.Int32 timeout, Mapbox.Map.CanonicalTileId tileId, System.String mapId) (at Assets/Packages/Mapbox/Unity/MapboxAccess.cs:247)
    Mapbox.Map.Tile.Initialize (Mapbox.Platform.IFileSource fileSource, Mapbox.Map.CanonicalTileId canonicalTileId, System.String mapId, System.Action p) (at Assets/Packages/Mapbox/Core/mapbox-sdk-cs/Map/Tile.cs:144)
    VectorDataFetcher.FetchVector (Mapbox.Map.CanonicalTileId canonicalTileId, System.String mapid, Mapbox.Unity.MeshGeneration.Data.UnityTile tile, System.Boolean useOptimizedStyle, Mapbox.Unity.Map.Style style) (at Assets/Packages/Mapbox/Unity/MeshGeneration/Factories/VectorDataFetcher.cs:16)
    Mapbox.Unity.MeshGeneration.Factories.VectorTileFactory.OnRegistered (Mapbox.Unity.MeshGeneration.Data.UnityTile tile) (at Assets/Packages/Mapbox/Unity/MeshGeneration/Factories/VectorTileFactory.cs:120)
    Mapbox.Unity.MeshGeneration.Factories.AbstractTileFactory.Register (Mapbox.Unity.MeshGeneration.Data.UnityTile tile) (at Assets/Packages/Mapbox/Unity/MeshGeneration/Factories/AbstractTileFactory.cs:101)
    Mapbox.Unity.Map.AbstractMapVisualizer.LoadTile (Mapbox.Map.UnwrappedTileId tileId) (at Assets/Packages/Mapbox/Unity/Map/AbstractMapVisualizer.cs:202)
    Mapbox.Unity.Map.AbstractMap.TileProvider_OnTileAdded (Mapbox.Map.UnwrappedTileId tileId) (at Assets/Packages/Mapbox/Unity/Map/AbstractMap.cs:560)
    Mapbox.Unity.Map.AbstractTileProvider.AddTile (Mapbox.Map.UnwrappedTileId tile) (at Assets/Packages/Mapbox/Unity/Map/AbstractTileProvider.cs:43)
    Mapbox.Unity.Map.RangeAroundTransformTileProvider.Update () (at Assets/Packages/Mapbox/Unity/Map/RangeAroundTransformTileProvider.cs:66)

    Also when looking at the project after importing, the assets under Probuilder Data have missing scripts. I tried importing in both Unity 2018.1.4 and 2018.2b7, same thing happens.
     
  22. chanfort

    chanfort

    Joined:
    Dec 15, 2013
    Posts:
    641
    Hmm, if we would be moving from 100k down to 5k just for local avoidance / colliders, I think it's pretty safe to conclude that assumptions-free local avoidance and collisions should be the next high priority challenge to solve :) .

    In a mean time, I think 5k at 60 fps should be already working with old NavMeshAgent components and GameObjects, or pretty close to this performance. If you manually move units along NavMeshQuery requested paths, then it may also work to use just NavMeshObstacle (without carving) on each GameObject (didn't tested it this way).

    P.S. You may also get another problem for agents with local avoidance. Crowds can get jammed in these streets when moving in the opposite directions once local avoidance is present. But that's another story for another day :) .

    Anyways, will keep an eye what comes next with local avoidance / collisions.
     
  23. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Regarding local avoidance isn't that what the classic boids example does with each boid trying to maintain a gap between it and it's neighbours?
     
  24. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Boids doesn't have to reason about the navmesh. So there are some similarities in areas but the core logic and flow is very different.

    With navigation meshes you basically navigate polygon to polygon. That is then narrowed to finding the best point to point path between the polygons, and then finally you straighten that path.

    So you can also construct a notion of path corridors through those polygons. That's what Detour does which is what Unity uses. So crowd handling moves agents along the corridors while keeping distance from each other. Every tick of crowd control it checks every agent to see if it can progress through the corridor, taking into account other agents.

    I haven't looked at the experimental navmesh api closely enough to say if it exposes enough information to implement crowd handling. Given that it does give enough info to do path straightening I would think so, or close enough where you can fill in the gaps.

    You wouldn't want to use physics here really. Other then possibly at build time to refine/extend the strategic information embedded into the data used.
     
    zulfajuniadi and chanfort like this.
  25. BuzzJive

    BuzzJive

    Joined:
    Mar 6, 2014
    Posts:
    75
    @Vanamerax and @zulfajuniadi - The map making bug is a Windows only bug, which may be why zulfa isn't seeing it. I just commented out line 154 in MbTiles.cs - it's in function openOrCreateDb. Seems like there's an attempt to use long paths on Windows, but that fails. Once I commented that out everything worked as expected. Thanks for the demo zulfa!
     
    brnkhy and zulfajuniadi like this.
  26. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Thanks for the fix! Funny thing is i'm developing on both windows and mac and I can't seem to reproduce the problem.
     
  27. Vanamerax

    Vanamerax

    Joined:
    Jan 12, 2012
    Posts:
    938
    Right, this fixes the problem for me. I get to 100000 until I dip below 60 fps in the editor using a 6700K and HD7870 at stock speeds. Apparently rendering the instanced meshing is taking almost all of that CPU time. The time that the navigation is taking is almost negligible.
     
    zulfajuniadi likes this.
  28. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    That is true. I tried using fustrum culling but it made things worst. Theoretically, I could push the number of units a bit more using drawmeshinstancedindirect but i guess thats pretty much out of scope for this project.

    Still finding inspiration for a good way to implement local avoidance that could fit into any size and type of terrain. Looking into implementing quadtrees either via ecs or compute shaders. Might even partition it to run via batching as i don't think you'll need to calculate collisions for every unit at every frame.
     
  29. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    I've got a simple local avoidance running. Planning to implement RVO2 in the future. Performance drops quite considerably. I could only get 15,000 agents moving at a time at 60 fps (i7 6700, GTX 1060).

    With local avoidance:



    Without local avoidance:

     
  30. chanfort

    chanfort

    Joined:
    Dec 15, 2013
    Posts:
    641
    Looks fantastic! Did you also tried using it in 2D, i.e. using Vector2 and float2 ? Just thought these might be a bit faster.

    Yeah, I guess local avoidance is the main performance eater :)

    Oh, and I see you also use Mathf.RoundToInt when running inside burst job (lines 88-89 inside NavAgentAvoidanceSystem). Did you tried just typecasting (int) ? Not sure how fast Mathf here would be.
     
    Last edited: Jun 26, 2018
  31. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Yeah theres parts of the code that are still unoptimized. I'm in the process of re implementing the avoidance anyway so that section is probably going to be overwritten.

    API's won't change much though.
     
  32. chanfort

    chanfort

    Joined:
    Dec 15, 2013
    Posts:
    641
    Sounds like a great plan. Good luck and looking forward :) . RVO2 should be great too!
     
  33. brnkhy

    brnkhy

    Joined:
    Oct 12, 2015
    Posts:
    6
    Hey Zulfa,
    I played around with your project a lot last week while trying to get into ecs stuff, really nice work. I'm working on mapbox unity sdk so it was very interesting to see it in your project.
    Also as @BuzzJive said, that error with mapbox sdk is a combination of w10 file path restrictions and .net 4.6. We fixed it in previous release but I think you were using a slightly older version.
    I saw you switched to a voronoi based city generator now but if you ever need help with Mapbox stuff I would love to help. I also do lots of experimental stuff with mapbox sdk (like realistic and detailed city scenes) might look good with what you're doing.
     
    zulfajuniadi likes this.
  34. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Hey @brnkhy yeah I switched it because it was turning to be a plug and play system and no more a mere demo. Nothing wrong with the mapbox SDK it's just that it's a bit heavy to distribute around. If you do have something simple I'd be happy to create a demo on that. :)

    Cheers
     
    brnkhy likes this.
  35. brnkhy

    brnkhy

    Joined:
    Oct 12, 2015
    Posts:
    6
    That would be great!
    You can find some of the stuff I've been working on here; https://twitter.com/brnkhy. Maybe we can do something realistic or toonish.
    We can talk about it on twitter dm or mail if you prefer (brnkahyaoglu@gmail.com), let me know what you think :)
     
    zulfajuniadi likes this.
  36. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    Nice example :)
    I have a question about local avoidance. It has NativeMultiHashMap which is filled in one job, then it is passed to second but never used inside a job. Is it doing something or it is for some a future solution?
     
  37. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    Umm, just wondering, but have you tried adding non one-colored textures and animations to check how that would fare inside a normal game?
     
  38. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Yeah that was remnants of my test code. You got me :D
     
  39. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    With baking animations into textures you can have around 20 - 30K agents running around the map on a mid level PC. Here's a link to a demo on how I implemented it https://forum.unity.com/threads/graphics-drawmeshinstanced.537927/#post-3550738
     
    Novack likes this.
  40. Djayp

    Djayp

    Joined:
    Feb 16, 2015
    Posts:
    114
    Hello ! Thanks for sharing !

    Since TransformMatrix is no longer useful to MeshInstanceRenderer, is there any reason to have position and rotation twice (NavAgent.position, NavAgent.rotation, Position and Rotation Components) ? Is setting the Position and Rotation components values from NavAgentSystem slower than copying it from NavAgent via NavAgentSyncSystem ?
     
    Last edited: Nov 23, 2018
  41. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    This is very nice!
    Unfortunately, when I tried to adapt it to the latest entities package (mainly MatrixTransform -> LocalToWorld), I am now running into an endless loop, freezing Unity.

    Has anyone successfully adapted this code for the latest entities package, by any chance?
     
  42. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Yeah I have a similar version running fine in my project.

    What line in particular is your issue?

    I've cleaned it up and modified it a bit.
     
    florianhanke likes this.
  43. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Thanks tertle for the quick answer. I've restarted Unity, and now it's running on 2019.1.0a12, with entities 0.0.12-preview.21 (also with slight modifications). Apologies for posting without performing that basic debug step :oops:
     
  44. Djayp

    Djayp

    Joined:
    Feb 16, 2015
    Posts:
    114
    I moved AgentStatus in another component (splitting Agent and NavData) and removed Position and Rotation from AgentComponent. Here it is :

    NavAgentSystem.cs
    Code (CSharp):
    1. #region
    2.  
    3. using System.Collections.Concurrent;
    4. using UnityEngine;
    5. using Unity.Burst;
    6. using Unity.Collections;
    7. using Unity.Entities;
    8. using Unity.Jobs;
    9. using Unity.Mathematics;
    10. using Unity.Transforms;
    11. using NavJob.Components;
    12.  
    13. #endregion
    14.  
    15. namespace NavJob.Systems
    16. {
    17.     class SetDestinationBarrier : BarrierSystem { }
    18.     class PathSuccessBarrier : BarrierSystem { }
    19.     class PathErrorBarrier : BarrierSystem { }
    20.  
    21.     [DisableAutoCreation]
    22.     public class NavAgentSystem : JobComponentSystem
    23.     {
    24.         private struct AgentData
    25.         {
    26.             public int index;
    27.             public Entity entity;
    28.             public Agent agent;
    29.             public NavData navData;
    30.             public Position position;
    31.         }
    32.  
    33.         private NativeQueue<AgentData> needsWaypoint;
    34.         private ConcurrentDictionary<int, Vector3[]> waypoints = new ConcurrentDictionary<int, Vector3[]> ();
    35.         private NativeHashMap<int, AgentData> pathFindingData;
    36.  
    37.         [BurstCompile]
    38.         private struct DetectNextWaypointJob : IJobParallelFor
    39.         {
    40.             public int navMeshQuerySystemVersion;
    41.             public InjectData data;
    42.             public NativeQueue<AgentData>.Concurrent needsWaypoint;
    43.  
    44.             public void Execute (int index)
    45.             {
    46.                 Agent agent = data.Agents[index];
    47.                 NavData navData = data.NavDatas[index];
    48.                 if (navData.remainingDistance - navData.stoppingDistance > 0 || agent.status != AgentStatus.Moving)
    49.                 {
    50.                     return;
    51.                 }
    52.                 if (navData.nextWaypointIndex != navData.totalWaypoints)
    53.                 {
    54.                     needsWaypoint.Enqueue (new AgentData { navData = data.NavDatas[index], position = data.Positions[index], entity = data.Entities[index], index = index });
    55.                 }
    56.                 else if (navMeshQuerySystemVersion != navData.queryVersion || navData.nextWaypointIndex == navData.totalWaypoints)
    57.                 {
    58.                     navData.totalWaypoints = 0;
    59.                     navData.currentWaypoint = 0;
    60.                     agent.status = AgentStatus.Idle;
    61.                     data.Agents[index] = agent;
    62.                     data.NavDatas[index] = navData;
    63.                 }
    64.             }
    65.         }
    66.  
    67.         private struct SetNextWaypointJob : IJob
    68.         {
    69.             public InjectData data;
    70.             public NativeQueue<AgentData> needsWaypoint;
    71.             public void Execute ()
    72.             {
    73.                 while (needsWaypoint.TryDequeue (out AgentData item))
    74.                 {
    75.                     Entity entity = data.Entities[item.index];
    76.                     if (instance.waypoints.TryGetValue (entity.Index, out Vector3[] currentWaypoints))
    77.                     {
    78.                         NavData agent = data.NavDatas[item.index];
    79.                         agent.currentWaypoint = currentWaypoints[agent.nextWaypointIndex];
    80.                         agent.remainingDistance = Vector3.Distance (data.Positions[item.index].Value, agent.currentWaypoint);
    81.                         agent.nextWaypointIndex++;
    82.                         data.NavDatas[item.index] = agent;
    83.                     }
    84.                 }
    85.             }
    86.         }
    87.  
    88.         [BurstCompile]
    89.         private struct MovementJob : IJobParallelFor
    90.         {
    91.             private readonly float dt;
    92.             private readonly float3 up;
    93.             private readonly float3 one;
    94.  
    95.             private InjectData data;
    96.  
    97.             public MovementJob (InjectData data, float dt)
    98.             {
    99.                 this.dt = dt;
    100.                 this.data = data;
    101.                 up = Vector3.up;
    102.                 one = Vector3.one;
    103.             }
    104.  
    105.             public void Execute (int index)
    106.             {
    107.                 if (index >= data.NavDatas.Length)
    108.                 {
    109.                     return;
    110.                 }
    111.  
    112.                 Agent agent = data.Agents[index];
    113.                
    114.                 if (agent.status != AgentStatus.Moving)
    115.                 {
    116.                     return;
    117.                 }
    118.  
    119.                 NavData navData = data.NavDatas[index];
    120.  
    121.                 if (navData.remainingDistance > 0)
    122.                 {
    123.                     Position position = data.Positions[index];
    124.                     Rotation rotation = data.Rotations[index];
    125.  
    126.                     navData.currentMoveSpeed = Mathf.Lerp (navData.currentMoveSpeed, navData.moveSpeed, dt * navData.acceleration);
    127.                     // todo: deceleration
    128.                     if (navData.nextPosition.x != Mathf.Infinity)
    129.                     {
    130.                         position.Value = navData.nextPosition;
    131.                     }
    132.                     Vector3 heading = navData.currentWaypoint - position.Value;
    133.                     navData.remainingDistance = heading.magnitude;
    134.                     if (navData.remainingDistance > 0.001f)
    135.                     {
    136.                         Vector3 targetRotation = Quaternion.LookRotation (heading, up).eulerAngles;
    137.                         targetRotation.x = targetRotation.z = 0;
    138.                         if (navData.remainingDistance < 1)
    139.                         {
    140.                             rotation.Value = Quaternion.Euler (targetRotation);
    141.                         }
    142.                         else
    143.                         {
    144.                             rotation.Value = Quaternion.Slerp (rotation.Value, Quaternion.Euler (targetRotation), dt * navData.rotationSpeed);
    145.                         }
    146.                     }
    147.  
    148.                     navData.nextPosition = position.Value + math.forward(rotation.Value) * navData.currentMoveSpeed * dt;
    149.                     data.NavDatas[index] = navData;
    150.                     data.Positions[index] = position;
    151.                     data.Rotations[index] = rotation;
    152.                 }
    153.                 else if (navData.nextWaypointIndex == navData.totalWaypoints)
    154.                 {
    155.                     navData.nextPosition = new float3 { x = Mathf.Infinity, y = Mathf.Infinity, z = Mathf.Infinity };
    156.                     agent.status = AgentStatus.Idle ;
    157.                     data.Agents[index] = agent;
    158.                     data.NavDatas[index] = navData;
    159.                 }
    160.             }
    161.         }
    162.  
    163.         private struct InjectData
    164.         {
    165.             public readonly int Length;
    166.             [ReadOnly] public EntityArray Entities;
    167.             public ComponentDataArray<Agent> Agents;
    168.             public ComponentDataArray<NavData> NavDatas;
    169.             public ComponentDataArray<Position> Positions;
    170.             public ComponentDataArray<Rotation> Rotations;
    171.         }
    172.  
    173.         [Inject] private InjectData data;
    174.         [Inject] private NavMeshQuerySystem querySystem;
    175.         [Inject] SetDestinationBarrier setDestinationBarrier;
    176.         [Inject] PathSuccessBarrier pathSuccessBarrier;
    177.         [Inject] PathErrorBarrier pathErrorBarrier;
    178.  
    179.         protected override JobHandle OnUpdate (JobHandle inputDeps)
    180.         {
    181.             inputDeps = new DetectNextWaypointJob { data = data, needsWaypoint = needsWaypoint.ToConcurrent(), navMeshQuerySystemVersion = querySystem.Version }.Schedule (data.Length, 64, inputDeps);
    182.             inputDeps = new SetNextWaypointJob { data = data, needsWaypoint = needsWaypoint }.Schedule (inputDeps);
    183.             inputDeps = new MovementJob (data, Time.deltaTime).Schedule (data.Length, 64, inputDeps);
    184.             return inputDeps;
    185.         }
    186.  
    187.         /// <summary>
    188.         /// Used to set an agent destination and start the pathfinding process
    189.         /// </summary>
    190.         /// <param name="entity"></param>
    191.         /// <param name="navData"></param>
    192.         /// <param name="destination"></param>
    193.         public void SetDestination (Entity entity, Agent agent, NavData navData, Position position, Vector3 destination, int areas = -1)
    194.         {
    195.             if (pathFindingData.TryAdd (entity.Index, new AgentData { index = entity.Index, entity = entity, agent = agent, navData = navData, position = position }))
    196.             {
    197.                 agent.status = AgentStatus.PathQueued;
    198.                 navData.destination = destination;
    199.                 navData.queryVersion = querySystem.Version;
    200.                 setDestinationBarrier.CreateCommandBuffer().SetComponent(entity, agent);
    201.                 setDestinationBarrier.CreateCommandBuffer().SetComponent(entity, navData);
    202.                 querySystem.RequestPath (entity.Index, position.Value, navData.destination, areas);
    203.             }
    204.         }
    205.  
    206.         /// <summary>
    207.         /// Static counterpart of SetDestination
    208.         /// </summary>
    209.         /// <param name="entity"></param>
    210.         /// <param name="navData"></param>
    211.         /// <param name="destination"></param>
    212.         public static void SetDestinationStatic (Entity entity, Agent agent, NavData navData, Position position, Vector3 destination, int areas = -1)
    213.         {
    214.             instance.SetDestination (entity, agent, navData, position, destination, areas);
    215.         }
    216.  
    217.         protected static NavAgentSystem instance;
    218.  
    219.         protected override void OnCreateManager ()
    220.         {
    221.             instance = this;
    222.             querySystem.RegisterPathResolvedCallback (OnPathSuccess);
    223.             querySystem.RegisterPathFailedCallback (OnPathError);
    224.             needsWaypoint = new NativeQueue<AgentData> (Allocator.Persistent);
    225.             pathFindingData = new NativeHashMap<int, AgentData> (0, Allocator.Persistent);
    226.         }
    227.  
    228.         protected override void OnDestroyManager ()
    229.         {
    230.             needsWaypoint.Dispose ();
    231.             pathFindingData.Dispose ();
    232.         }
    233.  
    234.         private void SetWaypoint (Entity entity, Agent agent, NavData navData, Position position, Vector3[] newWaypoints)
    235.         {
    236.             waypoints[entity.Index] = newWaypoints;
    237.             agent.status = AgentStatus.Moving;
    238.             navData.nextWaypointIndex = 1;
    239.             navData.totalWaypoints = newWaypoints.Length;
    240.             navData.currentWaypoint = newWaypoints[0];
    241.             navData.remainingDistance = Vector3.Distance (position.Value, navData.currentWaypoint);
    242.             pathSuccessBarrier.CreateCommandBuffer().SetComponent(entity, agent);
    243.             pathSuccessBarrier.CreateCommandBuffer().SetComponent(entity, navData);
    244.         }
    245.  
    246.         private void OnPathSuccess (int index, Vector3[] waypoints)
    247.         {
    248.             if (pathFindingData.TryGetValue (index, out AgentData entry))
    249.             {
    250.                 SetWaypoint (entry.entity, entry.agent, entry.navData, entry.position, waypoints);
    251.                 pathFindingData.Remove (index);
    252.             }
    253.         }
    254.  
    255.         private void OnPathError (int index, PathfindingFailedReason reason)
    256.         {
    257.             if (pathFindingData.TryGetValue (index, out AgentData entry))
    258.             {
    259.                 entry.agent.status = AgentStatus.Idle;
    260.                 pathErrorBarrier.CreateCommandBuffer().SetComponent(entry.entity, entry.agent);
    261.                 pathFindingData.Remove (index);
    262.             }
    263.         }
    264.     }
    265. }
    NavDataComponent.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Entities;
    3. using Unity.Mathematics;
    4.  
    5. namespace NavJob.Components
    6. {
    7.     [System.Serializable]
    8.     public struct NavData : IComponentData
    9.     {
    10.         public float stoppingDistance;
    11.         public float moveSpeed;
    12.         public float acceleration;
    13.         public float rotationSpeed;
    14.         public int areaMask;
    15.         public float3 destination { get; set; }
    16.         public float currentMoveSpeed { get; set; }
    17.         public int queryVersion { get; set; }
    18.         public float3 nextPosition { get; set; }
    19.         public float remainingDistance { get; set; }
    20.         public float3 currentWaypoint { get; set; }
    21.         public int nextWaypointIndex { get; set; }
    22.         public int totalWaypoints { get; set; }
    23.  
    24.         public NavData (
    25.             float stoppingDistance = 1f,
    26.             float moveSpeed = 4f,
    27.             float acceleration = 1f,
    28.             float rotationSpeed = 10f,
    29.             int areaMask = -1
    30.         )
    31.         {
    32.             this.stoppingDistance = stoppingDistance;
    33.             this.moveSpeed = moveSpeed;
    34.             this.acceleration = acceleration;
    35.             this.rotationSpeed = rotationSpeed;
    36.             this.areaMask = areaMask;
    37.             destination = Vector3.zero;
    38.             currentMoveSpeed = 0;
    39.             queryVersion = 0;
    40.             nextPosition = new float3 (Mathf.Infinity, Mathf.Infinity, Mathf.Infinity);
    41.             remainingDistance = 0;
    42.             currentWaypoint = Vector3.zero;
    43.             nextWaypointIndex = 0;
    44.             totalWaypoints = 0;
    45.         }
    46.     }
    47.  
    48.     public class NavDataComponent : ComponentDataWrapper<NavData> { }
    49. }
    AgentComponent.cs
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3.     public enum AgentStatus
    4.     {      
    5. Idle = 0,
    6. PathQueued = 1,
    7. Moving = 2,
    8. Paused = 4
    9.     }
    10.  
    11.     [System.Serializable]
    12.     public struct Agent : IComponentData
    13.     {
    14.         public AgentStatus status { get; set; }
    15.     }
    16.  
    17.     public class AgentComponent : ComponentDataWrapper<Agent> { }
    I did the same for local avoidance and totally removed the rest. Let me know if there's a problem, but it should work.
     
    florianhanke likes this.
  45. ChiuanWei

    ChiuanWei

    Joined:
    Jan 29, 2012
    Posts:
    131
    speed up ? faster ?
     
  46. Djayp

    Djayp

    Joined:
    Feb 16, 2015
    Posts:
    114
    Sorry, I didn't compare, I just needed to make it work with current packages.
     
  47. Danistmein

    Danistmein

    Joined:
    Nov 15, 2018
    Posts:
    82
    awesome work!
     
    jameju likes this.
  48. MarioRuiz

    MarioRuiz

    Joined:
    Nov 7, 2009
    Posts:
    161
    @zulfajuniadi just wondering, did you ever finish rvo2 on your project?
     
    andoran likes this.
  49. Ardinius

    Ardinius

    Joined:
    Oct 4, 2015
    Posts:
    57
    Hi,
    Has anyone been able to get this to complie, i see @Djayp did some work to get it working for "current packages" do we know the version of Unity and DOTS that this project works in?
    I have seen a post on the git by PodeCaradox 10 days ago who linked a new NavAgentSystem.cs but still 41 complie errors remain
     
    Last edited: Nov 2, 2019
  50. Djayp

    Djayp

    Joined:
    Feb 16, 2015
    Posts:
    114
    Hello ! It was Entities 0.0.12-preview.21 or just after... I think there's a lot of changes since but I didn't update it. You'd better go with PodeCaradox's code.