High performance ECS

Example scene which demonstrates how agents can be placed into subscenes to achieve the highest performance.

This example scene demonstrates how the FollowerEntity movement script can be used in conjunction with Unity's ECS (Entity Component System) to achieve very high performance when simulating large numbers of agents. The FollowerEntity always uses ECS under the hood (and thus has pretty good performance regardless), but by placing agents into subscenes, we can get rid of the overhead of keeping GameObjects around for each agent, which leads to even better performance.

The example scene spawns 5000 agents which all try to move in circles around a central point. The agents use both pathfinding and local avoidance.

Contents

Best performance when testing the example scene

When testing this example scene, you are likely interested in the performance characteristics. To achieve the best performance when testing the scene in the Unity Editor, you should do the following:

  1. Disable gizmo rendering in the scene view and game view. Gizmos can be quite expensive to render, especially when there are many of them.

  2. Show only the game view (not the scene view) to halve the rendering costs.

  3. Unload the ECS subscene by unchecking the checkbox next to it in the hierarchy window.

  4. Ensure Burst compilation is enabled.

Graph setup

The graph used in this scene is a RecastGraph. The graph is not the main focus of this scene, but a recast graph has good performance, so it will do well. A grid graph could also have been used, with similar results (but likely slightly worse performance due to higher pathfinding and path simplification costs).

Subscenes

Unity's ECS workflow is centered around the concept of subscenes. A subscene is a separate scene which can be loaded and unloaded at runtime, and which are baked into entities, instead of GameObjects.

If you place the FollowerEntity component on a GameObject in a subscene, it will automatically be baked into an entity. If you don't use subscenes, it will remain a GameObject (and all the ease-of-use that this entails), and instead spawn an entity behind the scenes to get most of the performance benefits of ECS.

Using subscenes is good for performance, but it also has some limitations and is quite a bit more complex to work with compared to MonoBehaviours. Therefore I only recommend using subscenes if you need the absolute best performance and are willing to put in the extra effort to work with them. Profile your game first to see if you actually need the extra performance before going down this route.

In this scene, however, most agents are not directly placed in the subscene. Instead, a single prefab is used to instantiate all but one of the agents. This is done by the CircleSpawner component. It will:

  1. Instantiate a bunch of copies of the prefab.

  2. Place them on the rim of a circle.

  3. Add the DestinationMoveInCircle component to each agent, which will make them move in circles around a central point. This is similar to the MoveInCircle MonoBehaviour component.

Performance

When the FollowerEntity component is used in a subscene, performance is very good in most cases. Almost everything is done in separate worker threads, and the main thread is freed up to handle all your other game logic. It also parallelizes most computations, to make use of multiple CPU cores.

In the above screenshot, you can see an annotated profiler capture from a standalone build of the example scene running with 5000 agents at around 120-200 fps. Your mileage may vary depending on your hardware. This particular test was done on a pretty high-end computer. Rendering in this test is also extremely simple, and in a real game you will likely have a lot more overhead from rendering and all kinds of other things.

See

Best practices for good performance, for more tips on performance when using the FollowerEntity component.