unity-ecs-dots について
Unity 6.3 LTSで開発する人向けのECS/DOTS資料です。エンティティ、コンポーネント、システム、ベーキング、ジョブ、Burstの実践パターンと注意点を解説します。
主な機能
- 01ECSの基本概念とコンポーネント型
- 02ISystemとSystemAPIのパターン
- 03ベーキングとSubSceneのワークフロー
- 04ジョブのスケジューリングと並列処理
- 05Burstコンパイルと制約
- 06EntityCommandBufferのパターン
活用シーン
- 01GameObjectの処理をECSシステムに移行
- 02SubSceneでエンティティのベーキングを設定
- 03Jobsでエンティティを並列処理
- 04構造変更やBurstの問題に対処
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nice-wolf-studio/ unity-claude-skills --skill unity-ecs-dotsインストール後、Claude Code で/unity-ecs-dotsを入力して読み込まれたか確認します。
同梱ファイルreferences/ecs-api.mdreferences/jobs-burst.md
詳細
Unity ECS / DOTS(Data-Oriented Technology Stack)
Unity 6.3 LTS(6000.3)に基づきます -- Entities 1.3、Burst 1.8、Jobs パッケージ
コアコンセプト
ECS(Entity Component System)は、Unity のデータ指向フレームワークです。GameObject/MonoBehaviour モデルを、キャッシュ効率に優れた高性能なアーキテクチャに置き換えます。
ECS と MonoBehaviour の比較
| 項目 | MonoBehaviour | ECS |
|---|---|---|
| 識別子 | GameObject(重く、マネージド) | Entity(軽量な int ID) |
| データ | コンポーネントのクラスフィールド | アンマネージド構造体(IComponentData) |
| ロジック | MonoBehaviour のメソッド | システム(ISystem / SystemBase) |
| メモリレイアウト | ヒープ全体に分散 | アーキタイプごとの連続したチャンク |
| スレッド処理 | メインスレッドのみ | 並列処理のための Jobs + Burst |
エンティティ
エンティティは軽量な識別子(インデックスとバージョン整数)です。振る舞いもデータも持たず、コンポーネントを関連付けるために使用するハンドルです。
コンポーネント(IComponentData)
コンポーネントはメソッドを持たないアンマネージド構造体です。データのみを保持します。
using Unity.Entities;
public struct Speed : IComponentData
{
public float Value;
}
public struct Health : IComponentData
{
public float Current;
public float Max;
}
アーキタイプとチャンク
アーキタイプは、コンポーネント型の一意な組み合わせです。同じコンポーネント一式を持つすべてのエンティティは、同じアーキタイプを共有します。エンティティはアーキタイプごとにまとめられたチャンク(16 KB のブロック)に格納され、キャッシュ効率の高い反復処理が可能になります。
World と EntityManager
World は EntityManager と一連のシステムを含みます。デフォルトの World は自動的に作成されます。EntityManager は、エンティティの作成・破棄とコンポーネントの追加・削除に使用する主要な API です。
ベイキングと SubScene のワークフロー
ベイキングでは、オーサリング用 GameObject をランタイムエンティティに変換します。これはビルド時、または SubScene をエディターで読み込んだときに行われる一方向の変換です。
Baker
using Unity.Entities;
// Authoring component (MonoBehaviour on GameObject)
public class SpeedAuthoring : MonoBehaviour
{
public float speed;
}
// Baker converts authoring data to ECS components
public class SpeedBaker : Baker<SpeedAuthoring>
{
public override void Bake(SpeedAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new Speed { Value = authoring.speed });
}
}
SubScene
- SubScene には、ビルド時にエンティティへベイキングされる GameObject が含まれます
- ランタイムでは、エンティティデータが効率よくストリーミングされます(GameObject によるオーバーヘッドはありません)
- エディターでは、SubScene を開いて編集(GameObject を表示)するか、閉じてベイキング済みエンティティを表示できます
- ECS で管理するオブジェクトは、必ず SubScene 内に配置してください
TransformUsageFlags
| フラグ | 用途 |
|---|---|
Dynamic | エンティティがランタイムに移動する(LocalTransform、LocalToWorld を取得) |
Renderable | エンティティはレンダリングされるが、コードでは移動しない |
WorldSpace | エンティティがワールド空間のトランスフォームのみを使用する |
None | トランスフォームコンポーネントを追加しない |
システム
システムにはすべてのロジックが含まれます。コンポーネントクエリに一致するエンティティを反復処理します。
ISystem(推奨)
ISystem は、最新のアンマネージドシステム型です。Burst と互換性があり、SystemBase より優先して使用してください。
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
[BurstCompile]
public partial struct MovementSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<Speed>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
float deltaTime = SystemAPI.Time.DeltaTime;
foreach (var (transform, speed) in
SystemAPI.Query<RefRW<LocalTransform>, RefRO<Speed>>())
{
transform.ValueRW.Position +=
new float3(0, 0, speed.ValueRO.Value * deltaTime);
}
}
[BurstCompile]
public void OnDestroy(ref SystemState state) { }
}
主なポイント:
partial structである必要があります- 構造体と各メソッドに
[BurstCompile]を使用します OnCreate、OnUpdate、OnDestroyはref SystemStateを受け取りますstate.RequireForUpdate<T>()-- T が存在する場合にのみシステムが実行されますSystemAPI.Query<T>()-- 型安全な foreach 反復処理
SystemBase(マネージド、レガシー)
SystemBase は、クラスベースのマネージドシステムです。マネージドコードをサポートしますが、システムレベルでは Burst コンパイルできません。新しいコードではすべて ISystem を使用してください。SystemBase は SystemAPI.Query の代わりに Entities.ForEach() のラムダ構文を使用します。
SystemGroup と更新順序
システムは、定義された順序で更新されるグループに編成されます。
InitializationSystemGroup
-> BeginInitializationEntityCommandBufferSystem
SimulationSystemGroup (default group)
-> BeginSimulationEntityCommandBufferSystem
-> [Your systems go here by default]
-> EndSimulationEntityCommandBufferSystem
PresentationSystemGroup
-> BeginPresentationEntityCommandBufferSystem
属性を使用して順序を制御します。
[UpdateInGroup(typeof(SimulationSystemGroup))]
[UpdateBefore(typeof(OtherSystem))]
[UpdateAfter(typeof(AnotherSystem))]
public partial struct MySystem : ISystem { }
SystemAPI
SystemAPI は、安全で Burst 互換なアクセスを実現する静的メソッドを提供します。
| メソッド | 目的 |
|---|---|
SystemAPI.Query<T1, T2>() | 条件に一致するエンティティを反復処理します |
SystemAPI.Time | TimeData(DeltaTime、ElapsedTime)にアクセスします |
SystemAPI.GetSingleton<T>() | シングルトンのコンポーネント値を取得します |
SystemAPI.SetSingleton<T>(value) | シングルトンのコンポーネント値を設定します |
SystemAPI.GetComponent<T>(entity) | エンティティからコンポーネントを読み取ります |
SystemAPI.SetComponent<T>(entity, value) | エンティティのコンポーネントを書き込みます |
SystemAPI.HasComponent<T>(entity) | エンティティがコンポーネントを持つか確認します |
SystemAPI.GetComponentLookup<T>() | ランダムアクセス用のルックアップを取得します |
SystemAPI.GetBuffer<T>(entity) | DynamicBuffer を取得します |
SystemAPI.GetAspect<T>(entity) | エンティティのアスペクトを取得します |
コンポーネントの詳細
IComponentData(アンマネージド構造体)
基本となるコンポーネント型です。アンマネージド構造体である必要があります(参照型やマネージド配列は使用できません)。
ISharedComponentData
エンティティ間で共有されます。同じ共有コンポーネント値を持つエンティティは、同じチャンクにまとめられます。共有コンポーネント値の変更は構造変更です。
public struct TeamId : ISharedComponentData
{
public int Value;
}
IBufferElementData(DynamicBuffer)
エンティティにアタッチされる可変長配列です。
[InternalBufferCapacity(8)]
public struct DamageEvent : IBufferElementData
{
public float Value;
public Entity Source;
}
// Usage in a system
foreach (var (buffer, entity) in SystemAPI.Query<DynamicBuffer<DamageEvent>>()
.WithEntityAccess())
{
for (int i = 0; i < buffer.Length; i++)
totalDamage += buffer[i].Value;
buffer.Clear();
}
ICleanupComponentData
エンティティの破棄後も保持されます。クリーンアップ処理(ネイティブリソースの解放など)に使用されます。すべてのクリーンアップコンポーネントが削除されるまで、エンティティは完全には破棄されません。
IEnableableComponent
構造変更(チャンクの移動)を行わずに、有効/無効を切り替えられるコンポーネントです。
public struct Stunned : IComponentData, IEnableableComponent { }
// Toggle in system
SystemAPI.SetComponentEnabled<Stunned>(entity, true);
bool isStunned = SystemAPI.IsComponentEnabled<Stunned>(entity);
タグコンポーネント
クエリのフィルタリング専用に使用される、サイズゼロの構造体です。フィールドを持たず、エンティティごとのメモリコストはゼロです。
public struct IsPlayer : IComponentData { }
一般的なパターン
エンティティの作成と破棄
// Direct creation (structural change -- main thread only)
Entity e = entityManager.CreateEntity(typeof(Speed), typeof(Health));
// Preferred: use EntityCommandBuffer for deferred structural changes
var ecb = new EntityCommandBuffer(Allocator.TempJob);
Entity e2 = ecb.CreateEntity();
ecb.AddComponent(e2, new Speed { Value = 5f });
ecb.Playback(entityManager);
ecb.Dispose();
EntityCommandBuffer(ECB)
構造変更(エンティティの作成/破棄、コンポーネントの追加/削除)は、反復処理中には実行できません。ECB を使用して変更を後回しにします。ECB API の詳細は、references/ecs-api.md を参照してください。
システム管理 ECB(推奨)
[BurstCompile]
public partial struct SpawnSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var ecbSingleton = SystemAPI.GetSingleton<
EndSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
// ecb is played back automatically -- no Playback/Dispose needed
foreach (var spawner in SystemAPI.Query<RefRW<Spawner>>())
{
if (spawner.ValueRO.Timer <= 0)
{
ecb.Instantiate(spawner.ValueRO.Prefab);
spawner.ValueRW.Timer = spawner.ValueRO.Interval;
}
}
}
}
シングルトンコンポーネント
特定のコンポーネントを持つエンティティがちょうど 1 つだけの場合に使用します。SystemAPI.GetSingleton<T>()/SystemAPI.SetSingleton<T>(value)/SystemAPI.GetSingletonRW<T>() でアクセスします。グローバル設定、ゲームの状態など、1 つだけ存在するデータに便利です。
アスペクト(IAspect)
アスペクトは、関連するコンポーネントをまとめ、必要に応じたメソッドを備える単一のアクセスラッパーを提供します。
public readonly partial struct CharacterAspect : IAspect
{
public readonly RefRW<LocalTransform> Transform;
public readonly RefRO<Speed> Speed;
public readonly RefRW<Health> Health;
public void Move(float3 direction, float deltaTime)
{
Transform.ValueRW.Position +=
direction * Speed.ValueRO.Value * deltaTime;
}
}
// Use in system
foreach (var character in SystemAPI.Query<CharacterAspect>())
{
character.Move(new float3(1, 0, 0), deltaTime);
}
ジョブシステム
IJobEntity(推奨)
Execute のパラメーター型から EntityQuery を自動生成します。
[BurstCompile]
public partial struct MoveJob : IJobEntity
{
public float DeltaTime;
void Execute(ref LocalTransform transform, in Speed speed)
{
transform.Position += new float3(0, 0, speed.Value * DeltaTime);
}
}
// Schedule from system
[BurstCompile]
public partial struct MoveSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
new MoveJob
{
DeltaTime = SystemAPI.Time.DeltaTime
}.ScheduleParallel();
}
}
IJobChunk(低レベル)
最大限の制御を可能にする、手動でのチャンク反復処理です。詳しい例は references/jobs-burst.md を参照してください。
ジョブのスケジューリング
| メソッド | 動作 |
|---|---|
Run() | メインスレッドで実行します(ジョブのスケジューリングは行いません) |
Schedule() | ワーカースレッドでシングルスレッドのジョブを実行します |
ScheduleParallel() | チャンク全体でマルチスレッド実行します |
並列ジョブでの ECB
[BurstCompile]
public partial struct DestroyDeadJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter ECB;
void Execute([ChunkIndexInQuery] int sortKey, in Health health, Entity entity)
{
if (health.Current <= 0)
ECB.DestroyEntity(sortKey, entity);
}
}
Burst コンパイラー
Burst コンパイラーは、LLVM を使用して IL/.NET バイトコードを高度に最適化されたネイティブコードに変換します。
使用方法
ISystem 構造体(および各メソッド)と IJobEntity 構造体に [BurstCompile] を追加します。Burst は LLVM を介して、それらを最適化されたネイティブコードにコンパイルします。
制限事項
- マネージド型は使用できません(
class、string、List<T>、マネージド配列など) - 割り当てはできません(参照型に対する
newは使用できません) - try/catch/finally は使用できません
- 仮想メソッドやインターフェースは使用できません(ジョブインターフェースを除く)
- 静的な変更可能フィールドは使用できません(代わりに
SharedStatic<T>を使用してください) - コレクションには
NativeContainer型を使用する必要があります
Unity.Mathematics
UnityEngine.Mathf と Vector3 を置き換える、Burst に最適化された数学ライブラリです。
using Unity.Mathematics;
float3 position = new float3(1, 2, 3);
quaternion rot = quaternion.Euler(0, math.PI, 0);
float dist = math.distance(a, b);
float3 dir = math.normalize(b - a);
float val = math.lerp(0f, 1f, 0.5f);
| UnityEngine | Unity.Mathematics |
|---|---|
Vector3 | float3 |
Vector2 | float2 |
Quaternion | quaternion |
Mathf.Lerp | math.lerp |
Mathf.Sin | math.sin |
Matrix4x4 | float4x4 |
アンチパターン
| 内容 | 問題点 | 対処方法 |
|---|---|---|
ISystemで対応できる場面でSystemBaseを使用する | システムをBurstコンパイルできず、ヒープ割り当てが発生します | [BurstCompile]を付けたISystem(partial struct)を使用します |
| イテレーション中に構造変更を行う | イテレーターが無効になり、例外が発生します | 遅延変更にはEntityCommandBufferを使用します |
| ECBを使わずに並列ジョブで構造変更を行う | 競合状態やクラッシュが発生します | ソートキーを指定してEntityCommandBuffer.ParallelWriterを使用します |
| Burstコンパイルされるコードでマネージ型を使用する | Burstはマネージ型をコンパイルできません | 非マネージ構造体、FixedString、NativeContainerを使用します |
ISystemに[BurstCompile]を付け忘れる | システムがマネージコードとして実行され、パフォーマンスが低下します | 構造体とすべてのメソッドに[BurstCompile]を追加します |
NativeArrayを毎フレーム割り当て、破棄しない | メモリリークが発生します | Allocator.Tempで割り当てるか、OnDestroyで破棄します |
BurstコードでUnityEngine.Mathfを使用する | Burst向けに最適化されていません | Unity.Mathematics.mathを使用します |
state.RequireForUpdate<T>()を忘れる | 一致するエンティティが存在しなくてもシステムが実行されます | 不要な更新をスキップするため、OnCreateで呼び出します |
SharedComponentDataを頻繁に変更する | 変更のたびに構造変更(チャンクの移動)が発生します | 頻繁に変化するデータには通常のIComponentDataを使用します |
内側のループでGetComponentを使用する | ランダムアクセスによりキャッシュの一貫性が損なわれます | 線形イテレーションにはSystemAPI.Queryを使用します |
主要 API クイックリファレンス
| クラス/構造体 | 主なメンバー | 備考 |
|---|---|---|
EntityManager | CreateEntity、DestroyEntity、AddComponent<T>、RemoveComponent<T>、GetComponentData<T>、SetComponentData<T> | メインスレッド専用。構造変更に使用 |
EntityCommandBuffer | CreateEntity、DestroyEntity、AddComponent、RemoveComponent、Instantiate、Playback | 遅延構造変更 |
ECB.ParallelWriter | sortKeyパラメーターを持つECBと同様のメンバー | 並列ジョブでスレッドセーフ |
SystemAPI | Query<T>、Time、GetSingleton<T>、GetComponent<T>、GetComponentLookup<T> | システムからの静的アクセス |
EntityQuery | ToEntityArray、ToComponentDataArray<T>、CalculateEntityCount | 一括操作 |
IComponentData | (マーカーインターフェース) | 非マネージ構造体コンポーネント |
IBufferElementData | (マーカーインターフェース) | 動的バッファー要素 |
DynamicBuffer<T> | Add、RemoveAt、Length、Clear、AsNativeArray | エンティティごとの可変長配列 |
IJobEntity | Execute(ref T1, in T2, ...) | パラメーターからクエリを自動生成 |
IJobChunk | Execute(in ArchetypeChunk, ...) | 手動でのチャンクイテレーション |
World | DefaultGameObjectInjectionWorld、EntityManager | システムとエンティティのコンテナー |
RefRW<T>/RefRO<T> | ValueRW/ValueRO | 読み書き可能/読み取り専用のコンポーネント参照 |
関連スキル
- unity-scripting -- MonoBehaviourスクリプティング、コルーチン、async/await
- unity-physics -- 物理エンジン、Rigidbody、コライダー(Unity Physics ECSパッケージ向け)
- unity-foundations -- GameObjects、Transforms、Scenes、Prefabs
追加リソース
- Entity/Component APIの詳細についてはreferences/ecs-api.mdを参照してください
- JobsとBurstの詳細についてはreferences/jobs-burst.mdを参照してください
- Unity Entities マニュアル
- Unity Burst マニュアル
- Unity Jobs マニュアル
- Unity Entities API リファレンス