Best way to handle a large number of static dynamically placed objects

So my game requires a large amount of objects to be dynamically placed when the game starts, but then they normally won’t move for large periods of time afterwards. In this case it’s trees, I spawn in on normal 20k trees randomly across the scene, and then they stay there on average for a couple hours before either changing into a different tree (aging), or get destroyed. What’s the best way to handle updating the navmesh for this? I tried doing AstarPath.active.UpdateGraphs, but it added too much overhead starting the scene. I tried NavmeshCut, and it was a bit faster starting the scene, but still pretty slow, and added too much overhead each time it tries to process them again because of looping through all the 20k objects. Any thoughts?

Hi

If it’s randomly across the scene, then you essentially have to assume that the whole graph has changed, so I think you should recalculate the whole graph. You can do this by calling AstarPath.active.Scan(). In version 4.0 you can use ScanAsync to be able to show a progress bar while it scans the graph. You will want to make sure the A* Inspector -> Settings -> Scan On Awake is disabled to avoid scanning the graph twice (just unnecessary overhead). Version 4.0 also has some major optimizations for recast graphs that should make them much faster to scan in many cases (see https://arongranberg.com/astar/documentation/dev_4_0_4_3496c98/changelog.php).

When the tree is changed or destroyed you should use graph updates however.

Make sure that you use tiles which are not too large in the recast graph. When updating the graph only the affected tiles need to be recalculated and it is obviously faster if they are smaller (up to a limit of course).

I assume that the terrain is too large to use with a grid graph?

Hmm okay, I can give that a shot. It takes a couple minutes to do the scanning in Unity though, so I’m still not sure it will be fast enough though. Might just have to make the users suffer and wait, it’ll only be on servers. I was caching it to file currently to avoid that startup cost.

Right now my tiles are 128 in size, and going lower continues to increase scan time, right?

My terrain is 2k, so yeah when I tried it with a grid graph is was too big.

Ok. 128 in size should work well.

Try 4.0. The more tiles and objects there are in the world the larger is the speedup compared to 3.8.x.

Okay I’ll try that out. Is there a way to upgrade currently? When I go to the asset store it still shows 3.8.

Sorry. I’m still waiting on the Unity Asset Store review process to complete.
If you bought the package within the last 2 months you should be able to use your previous invoice number however.
If not, I have sent you a PM with a license key that will be valid for a week that you can use in the meantime to evaluate if it is worth upgrading or not.

We’ve had A* for a while. Thanks, I appreciate that!

1 Like

Note that this is a major upgrade. So make sure you create a backup of your project before upgrading (or better yet, use version control).

We do use version control, thanks.

So that new version, and using the async scan (which I assume is probably slower than the other, but won’t lock up the game instead), seems to take ~1 minute to finish the scan. That’s much better than what it would have been in 3.8, so thanks. That’s in a build though, if I run it in Unity it takes ~3 minutes for the scene to finish loading instead. Is there something I can do to get that time similar to the build version?

1 Like

For recast graphs, actually not really (except for the overhead of also running the rest of the game which may use some CPU power). The recast graph will spin up a few threads and the async scan method will essentially just check if those threads are done every time the ScanAsync iterator progresses.

Sorry. Unity disables a lot of optimizations in the editor (presumably to speed up compile times or to enable debugging or something, I am not really sure), there is not much you can do about it I’m afraid.

To speed up the recast graph scan you can try:

  • Increase the cell size (if possible)
  • Make sure Rasterize Meshes and Rasterize Colliders are not both enabled (as then it is likely that you are rasterizing some things effectively twice).
  • Use Rasterize Colliders instead of Rasterize Meshes as meshes are often much more detailed compared to colliders.
  • If Rasterize Colliders is enabled, reduce the collider detail (I know it is really hard to understand what that does exactly, but just try to reduce it until you see that capsule or sphere colliders aren’t being handled correctly. It only affects capsule and sphere colliders).
  • Increase the terrain sample size if possible.
  • You can check the Optimizations -> ASTAR_RECAST_BFS option. It may or may not work, I haven’t tested it in a while, but I think it should work in almost all cases (if I remember correctly it could sometimes fail in cases that looked like spiral staircases).
  • Try experimenting with the tile size, double it or halve it and check the impact on the time it takes to scan the graph.

Okay, I’ll give that stuff a try. If I rasterize colliders instead of meshes, will that still rasterize mesh colliders? I just noticed that the navmesh is including trigger colliders as well. Is that because I have it set to meshes instead of colliders?

Yes it will rasterize mesh colliders.

When it is set to meshes it will look for all MeshFilter components (i.e rendered meshes) and rasterize those, it completely ignores colliders (even mesh colliders). If set to rasterize colliders it should ignore all triggers.

So I changed it to colliders instead of meshes, and it did increase the performance, but it also seems to ignore some colliders now.

And you are sure that the ‘Flora’ layer is included in the layer mask in the recast graph settings?

Yes, most of the flora seems to be handled correctly, except for a few.

That’s odd. Does it detect it if you move it around a bit?

No, there’s a few locations on the map using this type of tree and it seems to ignore all of them. It seems to be the bigger trees that it’s ignoring oddly, while the small plants and trees seem to be working correctly.

Here’s a screen showing that off better

Hm… I wonder. Have I ensured that capsule colliders still work properly if the Direction is not the default one (which is the Y axis). Try to change it to the Y axis and see if it detects it then.