Unity Ecs Patterns
Build blazing-fast Unity games with data-oriented ECS architecture
✨ The solution you've been looking for
Master Unity ECS (Entity Component System) with DOTS, Jobs, and Burst for high-performance game development. Use when building data-oriented games, optimizing performance, or working with large entity counts.
See It In Action
Interactive preview & real-world examples
AI Conversation Simulator
See how users interact with this skill
User Prompt
I need to optimize my Unity game that has 10,000+ moving enemies. The frame rate is dropping badly with traditional GameObjects. Help me convert to ECS.
Skill Processing
Analyzing request...
Agent Response
Complete ECS implementation with systems for movement, health, and spawning that can handle thousands of entities at 60+ FPS
Quick Start (3 Steps)
Get up and running in minutes
Install
claude-code skill install unity-ecs-patterns
claude-code skill install unity-ecs-patternsConfig
First Trigger
@unity-ecs-patterns helpCommands
| Command | Description | Required Args |
|---|---|---|
| @unity-ecs-patterns high-performance-entity-management | Efficiently manage thousands of game entities using Unity's ECS architecture | None |
| @unity-ecs-patterns data-oriented-system-design | Convert object-oriented game logic to performant ECS patterns | None |
| @unity-ecs-patterns parallel-job-implementation | Implement CPU-intensive game logic using Unity's Job System and Burst compiler | None |
Typical Use Cases
High-Performance Entity Management
Efficiently manage thousands of game entities using Unity's ECS architecture
Data-Oriented System Design
Convert object-oriented game logic to performant ECS patterns
Parallel Job Implementation
Implement CPU-intensive game logic using Unity's Job System and Burst compiler
Overview
Unity ECS Patterns
Production patterns for Unity’s Data-Oriented Technology Stack (DOTS) including Entity Component System, Job System, and Burst Compiler.
When to Use This Skill
- Building high-performance Unity games
- Managing thousands of entities efficiently
- Implementing data-oriented game systems
- Optimizing CPU-bound game logic
- Converting OOP game code to ECS
- Using Jobs and Burst for parallelization
Core Concepts
1. ECS vs OOP
| Aspect | Traditional OOP | ECS/DOTS |
|---|---|---|
| Data layout | Object-oriented | Data-oriented |
| Memory | Scattered | Contiguous |
| Processing | Per-object | Batched |
| Scaling | Poor with count | Linear scaling |
| Best for | Complex behaviors | Mass simulation |
2. DOTS Components
Entity: Lightweight ID (no data)
Component: Pure data (no behavior)
System: Logic that processes components
World: Container for entities
Archetype: Unique combination of components
Chunk: Memory block for same-archetype entities
Patterns
Pattern 1: Basic ECS Setup
1using Unity.Entities;
2using Unity.Mathematics;
3using Unity.Transforms;
4using Unity.Burst;
5using Unity.Collections;
6
7// Component: Pure data, no methods
8public struct Speed : IComponentData
9{
10 public float Value;
11}
12
13public struct Health : IComponentData
14{
15 public float Current;
16 public float Max;
17}
18
19public struct Target : IComponentData
20{
21 public Entity Value;
22}
23
24// Tag component (zero-size marker)
25public struct EnemyTag : IComponentData { }
26public struct PlayerTag : IComponentData { }
27
28// Buffer component (variable-size array)
29[InternalBufferCapacity(8)]
30public struct InventoryItem : IBufferElementData
31{
32 public int ItemId;
33 public int Quantity;
34}
35
36// Shared component (grouped entities)
37public struct TeamId : ISharedComponentData
38{
39 public int Value;
40}
Pattern 2: Systems with ISystem (Recommended)
1using Unity.Entities;
2using Unity.Transforms;
3using Unity.Mathematics;
4using Unity.Burst;
5
6// ISystem: Unmanaged, Burst-compatible, highest performance
7[BurstCompile]
8public partial struct MovementSystem : ISystem
9{
10 [BurstCompile]
11 public void OnCreate(ref SystemState state)
12 {
13 // Require components before system runs
14 state.RequireForUpdate<Speed>();
15 }
16
17 [BurstCompile]
18 public void OnUpdate(ref SystemState state)
19 {
20 float deltaTime = SystemAPI.Time.DeltaTime;
21
22 // Simple foreach - auto-generates job
23 foreach (var (transform, speed) in
24 SystemAPI.Query<RefRW<LocalTransform>, RefRO<Speed>>())
25 {
26 transform.ValueRW.Position +=
27 new float3(0, 0, speed.ValueRO.Value * deltaTime);
28 }
29 }
30
31 [BurstCompile]
32 public void OnDestroy(ref SystemState state) { }
33}
34
35// With explicit job for more control
36[BurstCompile]
37public partial struct MovementJobSystem : ISystem
38{
39 [BurstCompile]
40 public void OnUpdate(ref SystemState state)
41 {
42 var job = new MoveJob
43 {
44 DeltaTime = SystemAPI.Time.DeltaTime
45 };
46
47 state.Dependency = job.ScheduleParallel(state.Dependency);
48 }
49}
50
51[BurstCompile]
52public partial struct MoveJob : IJobEntity
53{
54 public float DeltaTime;
55
56 void Execute(ref LocalTransform transform, in Speed speed)
57 {
58 transform.Position += new float3(0, 0, speed.Value * DeltaTime);
59 }
60}
Pattern 3: Entity Queries
1[BurstCompile]
2public partial struct QueryExamplesSystem : ISystem
3{
4 private EntityQuery _enemyQuery;
5
6 public void OnCreate(ref SystemState state)
7 {
8 // Build query manually for complex cases
9 _enemyQuery = new EntityQueryBuilder(Allocator.Temp)
10 .WithAll<EnemyTag, Health, LocalTransform>()
11 .WithNone<Dead>()
12 .WithOptions(EntityQueryOptions.FilterWriteGroup)
13 .Build(ref state);
14 }
15
16 [BurstCompile]
17 public void OnUpdate(ref SystemState state)
18 {
19 // SystemAPI.Query - simplest approach
20 foreach (var (health, entity) in
21 SystemAPI.Query<RefRW<Health>>()
22 .WithAll<EnemyTag>()
23 .WithEntityAccess())
24 {
25 if (health.ValueRO.Current <= 0)
26 {
27 // Mark for destruction
28 SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>()
29 .CreateCommandBuffer(state.WorldUnmanaged)
30 .DestroyEntity(entity);
31 }
32 }
33
34 // Get count
35 int enemyCount = _enemyQuery.CalculateEntityCount();
36
37 // Get all entities
38 var enemies = _enemyQuery.ToEntityArray(Allocator.Temp);
39
40 // Get component arrays
41 var healths = _enemyQuery.ToComponentDataArray<Health>(Allocator.Temp);
42 }
43}
Pattern 4: Entity Command Buffers (Structural Changes)
1// Structural changes (create/destroy/add/remove) require command buffers
2[BurstCompile]
3[UpdateInGroup(typeof(SimulationSystemGroup))]
4public partial struct SpawnSystem : ISystem
5{
6 [BurstCompile]
7 public void OnUpdate(ref SystemState state)
8 {
9 var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
10 var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
11
12 foreach (var (spawner, transform) in
13 SystemAPI.Query<RefRW<Spawner>, RefRO<LocalTransform>>())
14 {
15 spawner.ValueRW.Timer -= SystemAPI.Time.DeltaTime;
16
17 if (spawner.ValueRO.Timer <= 0)
18 {
19 spawner.ValueRW.Timer = spawner.ValueRO.Interval;
20
21 // Create entity (deferred until sync point)
22 Entity newEntity = ecb.Instantiate(spawner.ValueRO.Prefab);
23
24 // Set component values
25 ecb.SetComponent(newEntity, new LocalTransform
26 {
27 Position = transform.ValueRO.Position,
28 Rotation = quaternion.identity,
29 Scale = 1f
30 });
31
32 // Add component
33 ecb.AddComponent(newEntity, new Speed { Value = 5f });
34 }
35 }
36 }
37}
38
39// Parallel ECB usage
40[BurstCompile]
41public partial struct ParallelSpawnJob : IJobEntity
42{
43 public EntityCommandBuffer.ParallelWriter ECB;
44
45 void Execute([EntityIndexInQuery] int index, in Spawner spawner)
46 {
47 Entity e = ECB.Instantiate(index, spawner.Prefab);
48 ECB.AddComponent(index, e, new Speed { Value = 5f });
49 }
50}
Pattern 5: Aspect (Grouping Components)
1using Unity.Entities;
2using Unity.Transforms;
3using Unity.Mathematics;
4
5// Aspect: Groups related components for cleaner code
6public readonly partial struct CharacterAspect : IAspect
7{
8 public readonly Entity Entity;
9
10 private readonly RefRW<LocalTransform> _transform;
11 private readonly RefRO<Speed> _speed;
12 private readonly RefRW<Health> _health;
13
14 // Optional component
15 [Optional]
16 private readonly RefRO<Shield> _shield;
17
18 // Buffer
19 private readonly DynamicBuffer<InventoryItem> _inventory;
20
21 public float3 Position
22 {
23 get => _transform.ValueRO.Position;
24 set => _transform.ValueRW.Position = value;
25 }
26
27 public float CurrentHealth => _health.ValueRO.Current;
28 public float MaxHealth => _health.ValueRO.Max;
29 public float MoveSpeed => _speed.ValueRO.Value;
30
31 public bool HasShield => _shield.IsValid;
32 public float ShieldAmount => HasShield ? _shield.ValueRO.Amount : 0f;
33
34 public void TakeDamage(float amount)
35 {
36 float remaining = amount;
37
38 if (HasShield && _shield.ValueRO.Amount > 0)
39 {
40 // Shield absorbs damage first
41 remaining = math.max(0, amount - _shield.ValueRO.Amount);
42 }
43
44 _health.ValueRW.Current = math.max(0, _health.ValueRO.Current - remaining);
45 }
46
47 public void Move(float3 direction, float deltaTime)
48 {
49 _transform.ValueRW.Position += direction * _speed.ValueRO.Value * deltaTime;
50 }
51
52 public void AddItem(int itemId, int quantity)
53 {
54 _inventory.Add(new InventoryItem { ItemId = itemId, Quantity = quantity });
55 }
56}
57
58// Using aspect in system
59[BurstCompile]
60public partial struct CharacterSystem : ISystem
61{
62 [BurstCompile]
63 public void OnUpdate(ref SystemState state)
64 {
65 float dt = SystemAPI.Time.DeltaTime;
66
67 foreach (var character in SystemAPI.Query<CharacterAspect>())
68 {
69 character.Move(new float3(1, 0, 0), dt);
70
71 if (character.CurrentHealth < character.MaxHealth * 0.5f)
72 {
73 // Low health logic
74 }
75 }
76 }
77}
Pattern 6: Singleton Components
1// Singleton: Exactly one entity with this component
2public struct GameConfig : IComponentData
3{
4 public float DifficultyMultiplier;
5 public int MaxEnemies;
6 public float SpawnRate;
7}
8
9public struct GameState : IComponentData
10{
11 public int Score;
12 public int Wave;
13 public float TimeRemaining;
14}
15
16// Create singleton on world creation
17public partial struct GameInitSystem : ISystem
18{
19 public void OnCreate(ref SystemState state)
20 {
21 var entity = state.EntityManager.CreateEntity();
22 state.EntityManager.AddComponentData(entity, new GameConfig
23 {
24 DifficultyMultiplier = 1.0f,
25 MaxEnemies = 100,
26 SpawnRate = 2.0f
27 });
28 state.EntityManager.AddComponentData(entity, new GameState
29 {
30 Score = 0,
31 Wave = 1,
32 TimeRemaining = 120f
33 });
34 }
35}
36
37// Access singleton in system
38[BurstCompile]
39public partial struct ScoreSystem : ISystem
40{
41 [BurstCompile]
42 public void OnUpdate(ref SystemState state)
43 {
44 // Read singleton
45 var config = SystemAPI.GetSingleton<GameConfig>();
46
47 // Write singleton
48 ref var gameState = ref SystemAPI.GetSingletonRW<GameState>().ValueRW;
49 gameState.TimeRemaining -= SystemAPI.Time.DeltaTime;
50
51 // Check exists
52 if (SystemAPI.HasSingleton<GameConfig>())
53 {
54 // ...
55 }
56 }
57}
Pattern 7: Baking (Converting GameObjects)
1using Unity.Entities;
2using UnityEngine;
3
4// Authoring component (MonoBehaviour in Editor)
5public class EnemyAuthoring : MonoBehaviour
6{
7 public float Speed = 5f;
8 public float Health = 100f;
9 public GameObject ProjectilePrefab;
10
11 class Baker : Baker<EnemyAuthoring>
12 {
13 public override void Bake(EnemyAuthoring authoring)
14 {
15 var entity = GetEntity(TransformUsageFlags.Dynamic);
16
17 AddComponent(entity, new Speed { Value = authoring.Speed });
18 AddComponent(entity, new Health
19 {
20 Current = authoring.Health,
21 Max = authoring.Health
22 });
23 AddComponent(entity, new EnemyTag());
24
25 if (authoring.ProjectilePrefab != null)
26 {
27 AddComponent(entity, new ProjectilePrefab
28 {
29 Value = GetEntity(authoring.ProjectilePrefab, TransformUsageFlags.Dynamic)
30 });
31 }
32 }
33 }
34}
35
36// Complex baking with dependencies
37public class SpawnerAuthoring : MonoBehaviour
38{
39 public GameObject[] Prefabs;
40 public float Interval = 1f;
41
42 class Baker : Baker<SpawnerAuthoring>
43 {
44 public override void Bake(SpawnerAuthoring authoring)
45 {
46 var entity = GetEntity(TransformUsageFlags.Dynamic);
47
48 AddComponent(entity, new Spawner
49 {
50 Interval = authoring.Interval,
51 Timer = 0f
52 });
53
54 // Bake buffer of prefabs
55 var buffer = AddBuffer<SpawnPrefabElement>(entity);
56 foreach (var prefab in authoring.Prefabs)
57 {
58 buffer.Add(new SpawnPrefabElement
59 {
60 Prefab = GetEntity(prefab, TransformUsageFlags.Dynamic)
61 });
62 }
63
64 // Declare dependencies
65 DependsOn(authoring.Prefabs);
66 }
67 }
68}
Pattern 8: Jobs with Native Collections
1using Unity.Jobs;
2using Unity.Collections;
3using Unity.Burst;
4using Unity.Mathematics;
5
6[BurstCompile]
7public struct SpatialHashJob : IJobParallelFor
8{
9 [ReadOnly]
10 public NativeArray<float3> Positions;
11
12 // Thread-safe write to hash map
13 public NativeParallelMultiHashMap<int, int>.ParallelWriter HashMap;
14
15 public float CellSize;
16
17 public void Execute(int index)
18 {
19 float3 pos = Positions[index];
20 int hash = GetHash(pos);
21 HashMap.Add(hash, index);
22 }
23
24 int GetHash(float3 pos)
25 {
26 int x = (int)math.floor(pos.x / CellSize);
27 int y = (int)math.floor(pos.y / CellSize);
28 int z = (int)math.floor(pos.z / CellSize);
29 return x * 73856093 ^ y * 19349663 ^ z * 83492791;
30 }
31}
32
33[BurstCompile]
34public partial struct SpatialHashSystem : ISystem
35{
36 private NativeParallelMultiHashMap<int, int> _hashMap;
37
38 public void OnCreate(ref SystemState state)
39 {
40 _hashMap = new NativeParallelMultiHashMap<int, int>(10000, Allocator.Persistent);
41 }
42
43 public void OnDestroy(ref SystemState state)
44 {
45 _hashMap.Dispose();
46 }
47
48 [BurstCompile]
49 public void OnUpdate(ref SystemState state)
50 {
51 var query = SystemAPI.QueryBuilder()
52 .WithAll<LocalTransform>()
53 .Build();
54
55 int count = query.CalculateEntityCount();
56
57 // Resize if needed
58 if (_hashMap.Capacity < count)
59 {
60 _hashMap.Capacity = count * 2;
61 }
62
63 _hashMap.Clear();
64
65 // Get positions
66 var positions = query.ToComponentDataArray<LocalTransform>(Allocator.TempJob);
67 var posFloat3 = new NativeArray<float3>(count, Allocator.TempJob);
68
69 for (int i = 0; i < count; i++)
70 {
71 posFloat3[i] = positions[i].Position;
72 }
73
74 // Build hash map
75 var hashJob = new SpatialHashJob
76 {
77 Positions = posFloat3,
78 HashMap = _hashMap.AsParallelWriter(),
79 CellSize = 10f
80 };
81
82 state.Dependency = hashJob.Schedule(count, 64, state.Dependency);
83
84 // Cleanup
85 positions.Dispose(state.Dependency);
86 posFloat3.Dispose(state.Dependency);
87 }
88}
Performance Tips
1// 1. Use Burst everywhere
2[BurstCompile]
3public partial struct MySystem : ISystem { }
4
5// 2. Prefer IJobEntity over manual iteration
6[BurstCompile]
7partial struct OptimizedJob : IJobEntity
8{
9 void Execute(ref LocalTransform transform) { }
10}
11
12// 3. Schedule parallel when possible
13state.Dependency = job.ScheduleParallel(state.Dependency);
14
15// 4. Use ScheduleParallel with chunk iteration
16[BurstCompile]
17partial struct ChunkJob : IJobChunk
18{
19 public ComponentTypeHandle<Health> HealthHandle;
20
21 public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex,
22 bool useEnabledMask, in v128 chunkEnabledMask)
23 {
24 var healths = chunk.GetNativeArray(ref HealthHandle);
25 for (int i = 0; i < chunk.Count; i++)
26 {
27 // Process
28 }
29 }
30}
31
32// 5. Avoid structural changes in hot paths
33// Use enableable components instead of add/remove
34public struct Disabled : IComponentData, IEnableableComponent { }
Best Practices
Do’s
- Use ISystem over SystemBase - Better performance
- Burst compile everything - Massive speedup
- Batch structural changes - Use ECB
- Profile with Profiler - Identify bottlenecks
- Use Aspects - Clean component grouping
Don’ts
- Don’t use managed types - Breaks Burst
- Don’t structural change in jobs - Use ECB
- Don’t over-architect - Start simple
- Don’t ignore chunk utilization - Group similar entities
- Don’t forget disposal - Native collections leak
Resources
What Users Are Saying
Real feedback from the community
Environment Matrix
Dependencies
Framework Support
Context Window
Security & Privacy
Information
- Author
- wshobson
- Updated
- 2026-01-30
- Category
- architecture-patterns
Related Skills
Unity Ecs Patterns
Master Unity ECS (Entity Component System) with DOTS, Jobs, and Burst for high-performance game …
View Details →Async Python Patterns
Master Python asyncio, concurrent programming, and async/await patterns for high-performance …
View Details →Async Python Patterns
Master Python asyncio, concurrent programming, and async/await patterns for high-performance …
View Details →