Simulating Thousands of Fish Using Boids in Unity ECS.
by Jettelly
Published |
25
Share
Megapop Games showed us how they use boids and Unity’s Entities system to simulate marine life in-game.
Megapop Games recently shared a look at their marine life simulation for their game Life Below, built using Unity’s Entities system

Instead of controlling each fish on its own, the system uses a boids-based method. After reaching out, they shared some details about the process behind it.
Everything starts with three main rules the fish follows: separation, alignment, and cohesion.

  • Separation: avoid getting too close to others 
  • Alignment: move in the same direction as the group 
  • Cohesion: stay close to the group
In the code, these rules run as independent jobs, which can lead to better performance when using multithreading.
public struct Boid : IComponentData
{
    public float MovementSpeed;
    public float TurnSpeed;
    public float Size;

    public float4 Velocity;
    public float4 AngularVelocity;

    public BoidType Type;
}
Each job handles a different force for the boids, for example, in the case of separation it checks nearby neighbors and applies a force to push them away. 
public partial struct SeparationJob : IJobEntity
{
    [WriteOnly] public NativeArray<float4> Separation;

    [ReadOnly] public NativeParallelMultiHashMap<int2, Entity> BoidCells;
    [ReadOnly] public ComponentLookup<LocalTransform> TransformLookup;

    public float AvoidFactor;

    public void Execute(Entity e, int index, in LocalTransform transform, in Boid boid)
    {
        var delta = float4.zero;

        var key = (int3)(transform.Position * ChunkSize);
        key.y = 0;

        var neighbors = BoidCells.GetValuesForKey(key.xz);

        while (neighbors.MoveNext())
        {
            if (neighbors.Current.Equals(e)) continue;

            var other = TransformLookup[neighbors.Current];

            if (math.distancesq(transform.Position, other.Position) < boid.Size * boid.Size)
            {
                delta += transform.Position.xyzz - other.Position.xyzz;
            }
        }

        Separation[index] = delta * AvoidFactor;
    }
}
Once all these forces are ready, a final job combines them. That happens in the MoveJob, where everything comes together and gets applied to the boid’s velocity.
public partial struct MoveJob : IJobEntity
{
    [ReadOnly] public NativeArray<float4> Separation;
    [ReadOnly] public NativeArray<float4> Alignment;
    [ReadOnly] public NativeArray<float4> Cohesion;

    public float DeltaTime;

    public void Execute(ref LocalTransform transform, ref Boid boid)
    {
        var velocity =
            boid.Velocity +
            (Separation[index] + Alignment[index] + Cohesion[index]) * DeltaTime;

        boid.Velocity = velocity;

        transform.Position += boid.Velocity.xyz * DeltaTime;
    }
}
Beyond the base behavior, there are also other systems that can affect movement, like housing areas where fish tend to stay, obstacle avoidance, and interactions between species

These are handled as separate jobs, adding their own forces to the final result.
But, how does all this affect performance? Well, to keep things running well, the system avoids comparing each fish with all the others, instead it groups boids based on their position and only calculates distances within those groups.
The system works well with SIMD, using compatible data types and avoiding unnecessary branching inside the jobs. 

If you would like to learn more about Life Below or the team behind it, you can find their links below. 

Interested in learning more?
If you’re interested in the technical side of Unity? The Unity Dev Bundle brings together six books covering shaders, math, procedural shapes, editor tools, and character customization.

This is for developers and technical artists who want to build a stronger foundation and work with more advanced graphics and systems in their projects.
Jettelly wishes you success in your professional career! Did you find an error? No worries! Write to us at [email protected], and we'll fix it!

Subscribe to our newsletter to stay up to date with our latest offers

© 2026 Jettelly Inc. All rights reserved. Made with ❤️ in Toronto, Canada