Advanced Engineering • Optimization

Unity ECS: The Data Oriented Technology Stack (DOTS) Guide

Written by Sudhishkumar K • Updated Apr 2026 • 22 Min Read

If you have ever tried to instantiate 10,000 GameObjects in Unity, you know the pain. The frame rate drops to single digits, the Garbage Collector spikes, and your phone starts to heat up.

This isn't Unity's fault; it's the fault of Object Oriented Programming (OOP). The standard MonoBehaviour workflow is designed for flexibility, not for raw performance. Every GameObject is a heavy C# object with a Transform, a name string, and dozens of internal flags.

Enter DOTS (Data Oriented Technology Stack) and its core pillar, ECS (Entity Component System). This is not just a new feature; it is a paradigm shift in how we write code. It allows us to simulate massive battles, complex traffic systems, and rich ecosystems at 60 FPS on mobile devices.

100x Faster Logic
0ms GC Allocation
Multi Threaded by Default

1. The Problem: Memory Layout and Cache Misses

To understand why ECS is fast, you must understand computer hardware. Your CPU is incredibly fast, but fetching data from RAM is incredibly slow (relatively speaking).

In OOP (GameObjects):
Your data is scattered across the RAM heap. When you access Enemy[0], it might be at address 100. When you access Enemy[1], it might be at address 5000. The CPU has to wait for each fetch. This is a "Cache Miss."

In DOTS (ECS):
We pack data of the same type tightly together. All "Health" integers for 10,000 enemies are stored in a single contiguous array. The CPU grabs the first one and inevitably grabs the next 64 in the same "Cache Line."

2. The Holy Trinity: Entity, Component, System

ECS decouples data from behavior. There are no "Classes" that hold both variables and functions.

1. The Entity (The ID)

An Entity is essentially just an int ID (e.g., Entity #402). It holds no data itself. It is merely a key used to look up components. It is lightweight—you can have millions of them.

2. The Component (The Data)

Components are pure C# structs that implement IComponentData. They contain only blittable types (float, int, bool, float3). No references to classes, no strings (use FixedString), and no logic.


using Unity.Entities;
using Unity.Mathematics;

// A simple data struct. No functions allowed!
public struct ZombieStats : IComponentData 
{
    public float WalkSpeed;
    public float Health;
    public float3 TargetLocation;
}
            

3. The System (The Logic)

This is where the code lives. A System queries the world for all entities that match a specific criteria (e.g., "Give me everything with ZombieStats and Transform") and processes them.

3. The Modern Workflow: Baking & Aspects

In the early days of ECS, you had to write code to convert GameObjects. In Unity 2026 (Entities 1.0+), we use Baking.

The Baking Process

You still build your prefabs using GameObjects and MonoBehaviours (the "Authoring" view). You then write a Baker that translates that data into ECS components during the build process.


// 1. The MonoBehaviour (Authoring)
public class ZombieAuthoring : MonoBehaviour {
    public float Speed = 5f;
    public float Health = 100f;
}

// 2. The Baker (Converts MonoBehaviour -> ECS Component)
public class ZombieBaker : Baker {
    public override void Bake(ZombieAuthoring authoring) {
        // Create the component and attach it to the Entity
        var entity = GetEntity(TransformUsageFlags.Dynamic);
        AddComponent(entity, new ZombieStats {
            WalkSpeed = authoring.Speed,
            Health = authoring.Health
        });
    }
}
            

4. Writing High-Performance Systems

There are two ways to write systems: SystemBase (easier, managed) and ISystem (faster, unmanaged). For maximum performance, we use ISystem combined with the Burst Compiler.

Example: A Zombie Movement System


using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;

// [BurstCompile] optimizes this C# code into native machine code
[BurstCompile]
public partial struct ZombieMoveSystem : ISystem 
{
    // Ran once per frame
    [BurstCompile]
    public void OnUpdate(ref SystemState state) 
    {
        float deltaTime = SystemAPI.Time.DeltaTime;

        // Iterate over every entity with a Transform and ZombieStats
        foreach (var (transform, stats) in 
                 SystemAPI.Query, RefRO>()) 
        {
            // Logic: Move forward
            float3 forward = transform.ValueRO.Forward();
            transform.ValueRW.Position += forward * stats.ValueRO.WalkSpeed * deltaTime;
        }
    }
}
            

The Burst Compiler

Burst is Unity's LLVM-based compiler. It takes the restricted C# subset used in ECS jobs and compiles it into highly optimized native code (SIMD instructions). This is often 10x to 50x faster than standard C#.

5. The C# Job System: Multithreading Made Safe

Normally, writing multithreaded code is a nightmare of race conditions and deadlocks. DOTS uses the Job System to manage this for you.

Instead of processing the loop above on the main thread, we can schedule it to run across all available CPU cores (Worker Threads).


[BurstCompile]
public partial struct JobifiedZombieSystem : ISystem 
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state) 
    {
        var job = new MoveJob { DeltaTime = SystemAPI.Time.DeltaTime };
        
        // Schedule the job to run in parallel across all cores
        job.ScheduleParallel(); 
    }
}

[BurstCompile]
public partial struct MoveJob : IJobEntity 
{
    public float DeltaTime;

    // This method executes on multiple threads simultaneously
    public void Execute(ref LocalTransform transform, in ZombieStats stats) 
    {
        transform.Position += transform.Forward() * stats.WalkSpeed * DeltaTime;
    }
}
            

6. Hybrid ECS: You Don't Need to Go 100% DOTS

A common misconception is that you have to rewrite your entire game in ECS. This is false.

The Hybrid Approach:
Use GameObjects for things that don't need scale: UI, Main Menu, Audio, Input Handling, and the Main Character.
Use ECS for things that demand scale: Projectiles, Enemy Swarms, Particle logic, Traffic, Boids.

You can even synchronize data between them. For example, your GameObject player can set a singleton component PlayerPosition, which the 10,000 ECS zombies read to know where to walk.

Conclusion

Unity ECS is not just an optimization; it is a tool that unlocks design possibilities. Before DOTS, designing a game with 5,000 active units on a mobile phone was a fantasy. Now, it is a tutorial.

The learning curve is steep, but the payoff is absolute control over your game's memory and performance. Start small—try converting just one heavy system (like bullet movement) to ECS, and watch your frame rate soar.


Frequently Asked Questions (FAQ)

Q: Is ECS production ready in 2026?
A: Yes. With the release of Entities 1.0 (and subsequent 1.x updates), the API is stable. Major studios are shipping titles using DOTS.

Q: Can I use ECS with the Animator component?
A: Not directly. The standard Animator is GameObject-based. However, the com.unity.charactercontroller and com.unity.physics packages provide DOTS-native alternatives. For simple animations, many developers bake animations into vertex shaders or use "GPU Instancing" libraries.

Q: What is the difference between SystemBase and ISystem?
A: SystemBase is a class (Managed). It is easier to write but has slightly more overhead and creates Garbage Collection pressure. ISystem is a struct (Unmanaged). It is harder to write but is faster and fully compatible with Burst.