Using high performance ECS

How to use Unity's Entity Component System together with this package.

Unity's ECS (Entity Component System) is a different way of building game code that, in many cases, allows much higher performance, compared to the classical approach of using MonoBehaviours. ECS has downsides, however, such as higher code complexity and more boilerplate in many cases, and it is seldom useful for a very small number of items (whether that is NPCs, buttons or bullets).

ECS really shines when it can operate on a large number of items (in our case, agents), as it can parallelize calculations, and make use of unity's Burst compiler for batch-processing.

Contents

Using ECS for movement scripts

The A* Pathfinding Project supports using ECS when using the FollowerEntity movement script. With it, you can achieve good performance even with thousands of agents.

There are two ways of using it:

  1. Use the FollowerEntity as a normal MonoBehaviour. Behind the scenes, it will always use ECS anyway, and you'll get most of the performance benefit out-of-the-box. This makes it easy to integrate with a classical MonoBehaviour workflow.

  2. For even higher performance, place the FollowerEntity in a subscene. Then it will be automatically baked into a pure entity, without any overhead from gameObjects and MonoBehaviours. However, then you'll have to write ECS-code to interact with it.

Note

Other built-in movement scripts have no support for ECS.

See

For an example of subscenes, check out the included example scene High performance ECS

When to use ECS, and when not to

I recommend using ECS when you have a large number of agents (at least 100, likely more), so that the reduced overhead is actually noticable. Before that, you'll just be adding extra complexity to your game for pretty small wins. Keep in mind that the FollowerEntity always uses ECS behind the scenes, so you get most of the performance benefit out-of-the-box.

If you are already using ECS for the rest of the game, then of course using ECS for the movement scripts is also reasonable.

When in doubt, test both approaches and profile your game, and weigh that against the additional code complexity.

In any case, I strongly recommend starting out with MonoBehaviours to prototype things.

Interacting with a baked FollowerEntity

When used in a subscene, you cannot use any properties, fields, or methods on the FollowerEntity in play mode, since the component is baked away and won't even exist at runtime. Instead, you can either use ECS components directly, or you can use the wrapper FollowerEntityProxy which acts almost identically to the FollowerEntity component, but uses an entity as the source instead.

Using the FollowerEntityProxy

When you have a baked entity in a given world (how to get a reference to one is out of scope for this tutorial, check out Unity's ECS documentation), you can create a proxy wrapper to easily access most data:

var follower = new FollowerEntityProxy(world, entity);
follower.maxSpeed = 5;
follower.destination = new Vector3(1, 2, 3);

if (follower.currentNode.Tag == 1) {
Debug.Log("The agent is right now traversing a node with tag 1");
}
The interface is almost identical to the FollowerEntity MonoBehaviour component.

Note

This proxy can only be used on the main thread, and not in jobs. In those cases, you'll need to use ECS components directly.

Accessing ECS components directly

The FollowerEntity is baked into many components. You can find a list of them at FollowerEntity Component List. All settings of the movement script are reflected in one of those components.

For example, to change the agent's speed, destination and read the current node, like above, we can do:

// Read and then write back
var data = world.EntityManager.GetComponentData<MovementSettings>(entity);
data.follower.speed = 5;
world.EntityManager.SetComponentData(entity, data);

world.EntityManager.SetComponentData<DestinationPoint>(entity, new DestinationPoint {
destination = new Vector3(1, 2, 3),
facingDirection = Vector3.zero,
});

var currentNode = world.EntityManager.GetComponentData<ManagedState>(entity).pathTracer.startNode;
if (currentNode.Tag == 1) {
Debug.Log("The agent is right now traversing a node with tag 1");
}
In addition to being a bit more clunky, this has one important difference compared to using the proxy or MonoBehaviour:

When using the proxy or MonoBehaviour to change properties, the code will try VERY hard to ensure that all other related properties are as up-to-date as possible. For example, when changing the destination of the agent, the reachedDestination property will immediately update to reflect this. When directly accessing ECS components, no such update is done. Therefore, related properties may be out of date until the next simulation loop for the agent runs (typically the next frame). This means it's also faster, of course. You'll just have to be aware of it.

Note

Unsure about in which component to find a given setting? Check the source code for the property on the FollowerEntityProxy that you are interested in. Most implementations are only a few lines, and you'll be able to see exactly how to access it.

Other baked components

There are a few other commonly used components which can be baked, among those are:

They work essentially the same when baked as when they are used as MonoBehaviours.

ECS physics

This package does not currently have any support for the com.unity.physics package. All physics is done using Unity's built-in PhysX engine.

If you are simulating multiple worlds simultaneously (e.g. on a server) and have multiple physics scenes, you can use the PhysicsSceneRef component to specify which physics scene to use for ground collision and such.

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.