DY
GAMEPLAY PROGRAMMER
Back to blog
·Diego Yagüe

Data-Oriented Design for Gameplay Systems: A Practical Introduction

Why the object-oriented instincts you learned in school can hurt game performance — and how thinking about data layout first leads to faster, cleaner gameplay code.

PerformanceECSC++Unity DOTSArchitecture

The Problem with OOP in Games

Most of us learned to model game entities as objects:

class Enemy {
    Transform transform;
    Health health;
    AIController* ai;
    Renderer* renderer;
    // ...
};

This feels intuitive. An enemy is an object with properties. But this model has a silent performance cost: cache misses.

When the game loop processes a thousand enemies, it jumps between random memory addresses for each component. Modern CPUs hate this. The CPU stalls waiting for RAM far more than it actually executes instructions.

The DOD Alternative: Think About the Data First

Data-Oriented Design (DOD) inverts the model. Instead of asking "what is an enemy?", ask "what data do I process, and what operations do I apply to it?"

// Instead of an array of enemies...
struct EnemyData {
    float posX[MAX_ENEMIES];
    float posY[MAX_ENEMIES];
    float posZ[MAX_ENEMIES];
    int health[MAX_ENEMIES];
    // ...
};

Now when the AI system iterates to update positions, all position data is contiguous in memory. The CPU prefetcher loads it efficiently. You go from cache-miss hell to cache-friendly bliss.

In Practice: Unity DOTS

Unity's Data-Oriented Technology Stack (DOTS) applies these ideas at the engine level with three pieces:

  • Entities (entity IDs, no behavior)
  • Components (pure data structs)
  • Systems (pure functions that query and transform component data)

A movement system in DOTS looks like:

[BurstCompile]
public partial struct MoveSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (transform, velocity) in
            SystemAPI.Query<RefRW<LocalTransform>, RefRO<Velocity>>())
        {
            transform.ValueRW.Position += velocity.ValueRO.Value * SystemAPI.Time.DeltaTime;
        }
    }
}

The [BurstCompile] attribute compiles this to highly vectorized native code. We moved 10,000 projectiles per frame in Arena Protocol using exactly this pattern.

When Not to Use DOD

DOD shines for hot path systems — movement, collision detection, particle updates. For systems with complex branching logic, small counts, or infrequent updates (dialogue, inventory, cutscenes), the added complexity of an ECS architecture rarely pays off.

The rule: profile first, optimize only what's slow. But understanding DOD makes you a better architect even when you're not using it directly.