Procedural Level Generation With Unity DOTS and Burst

Game Development
Date:October 24, 2026
Topic:
Procedural Level Generation With Unity DOTS and Burst
⏱ 3 min read

Procedural generation promises infinite content. Unity DOTS and the Burst compiler deliver the throughput to make it real-time. Most tutorials stop at noise functions. This one shows how to architect a generation pipeline that scales across cores without allocating garbage.

Why DOTS Changes the Math

Classic MonoBehaviour approaches choke on per-chunk overhead. Each GameObject carries transform hierarchy, component lookup, and GC pressure. DOTS replaces that with contiguous arrays of plain structs. The Burst compiler then vectorizes the math — SIMD instructions process four, eight, or sixteen voxels per cycle.

csharp
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Jobs;

[BurstCompile]
public struct TerrainNoiseJob : IJobParallelFor {
    [ReadOnly] public NativeArray<float3> positions;
    public NativeArray<float> heights;
    public float scale;
    public int seed;

    public void Execute(int index) {
        float3 p = positions[index] * scale;
        heights[index] = noise.cnoise(p + seed);
    }
}

Entity Archetypes for Chunk Data

Define one archetype per chunk resolution. A 32³ voxel chunk becomes a single entity with a blob asset reference. The blob stores the density field — no per-voxel entities. This keeps the entity count low and the chunk count high.

csharp
public struct ChunkData : IComponentData {
    public BlobAssetReference<DensityBlob> density;
    public int3 chunkCoord;
    public uint revision;
}

public struct DensityBlob {
    public int3 dimensions;
    public BlobArray<float> values;
}
💡
TipUse BlobAssetReference for immutable chunk data. It avoids copy-on-write and enables zero-cost sharing across systems.

Streaming With Job Dependencies

Generation must not stall the main thread. Schedule noise jobs, mesh extraction jobs, and blob baking jobs as a dependency chain. The EntityCommandBufferSystem plays back structural changes at sync points.

csharp
public partial class TerrainStreamingSystem : SystemBase {
    protected override void OnUpdate() {
        var ecb = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer();
        
        Dependency = new ChunkRequestJob {
            playerPos = SystemAPI.GetSingleton<PlayerPosition>().value,
            ecb = ecb.AsParallelWriter()
        }.ScheduleParallel(Dependency);
    }
}

Marching Cubes on the GPU Side

Burst handles the density evaluation. For mesh extraction, dispatch a compute shader that reads the blob via a StructuredBuffer. This avoids marshaling vertex data back to CPU. The compute shader writes triangles directly into a GraphicsBuffer for DrawProcedural.

StageThreadData Flow
Noise EvalBurst (CPU)NativeArray→Blob
Mesh ExtractCompute (GPU)Blob→GraphicsBuffer
RenderGPUDrawProcedural

Deterministic Seeds for Multiplayer

Sync the world seed once. Derive per-chunk seeds with a hash: seed = hash(worldSeed, chunkCoord). Every client generates identical terrain without networked voxel data. Only modifications — player-placed blocks — replicate.

"

Determinism isn't a feature. It's the absence of hidden state.

— Mike Acton

Memory Budget Guardrails

Allocate a fixed NativeArray pool for active chunks. When the pool fills, evict the furthest chunk using a priority queue keyed by distance. Return its blob to a recycle queue. Zero allocations after warm-up.

⚠️
WarningForgetting to Dispose NativeArrays in the eviction path leaks memory fast. Wrap pools in a IDisposable manager and verify with the Memory Profiler.

Ship It Checklist

  1. Profile Burst timings with Unity.Profiling.ProfilerMarker per job.
  2. Validate determinism: run two clients, compare chunk hashes.
  3. Stress test: 10k chunks, 60 FPS on mid-tier mobile.
  4. Add a debug overlay showing job graph latency.

✦

Procedural worlds at scale demand data-oriented design. DOTS and Burst give you the primitives. The architecture — archetypes, blobs, job chains, GPU handoff — is yours to build. Start with a single chunk job. Measure. Extend. The infinite world fits in a finite budget.

Share𝕏 Twitterin LinkedInin Whatsapp