mcpField
U
unity-ecs-dots

unity-ecs-dots

github.com/nice-wolf-studio開発4068 回インストール6 週間前に更新
リポジトリ

unity-ecs-dots について

Unity 6.3 LTSで開発する人向けのECS/DOTS資料です。エンティティ、コンポーネント、システム、ベーキング、ジョブ、Burstの実践パターンと注意点を解説します。

主な機能

  1. 01ECSの基本概念とコンポーネント型
  2. 02ISystemとSystemAPIのパターン
  3. 03ベーキングとSubSceneのワークフロー
  4. 04ジョブのスケジューリングと並列処理
  5. 05Burstコンパイルと制約
  6. 06EntityCommandBufferのパターン

活用シーン

  1. 01GameObjectの処理をECSシステムに移行
  2. 02SubSceneでエンティティのベーキングを設定
  3. 03Jobsでエンティティを並列処理
  4. 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 の比較
項目MonoBehaviourECS
識別子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.TimeTimeData(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);
UnityEngineUnity.Mathematics
Vector3float3
Vector2float2
Quaternionquaternion
Mathf.Lerpmath.lerp
Mathf.Sinmath.sin
Matrix4x4float4x4

アンチパターン

内容問題点対処方法
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 クイックリファレンス

クラス/構造体主なメンバー備考
EntityManagerCreateEntity、DestroyEntity、AddComponent<T>、RemoveComponent<T>、GetComponentData<T>、SetComponentData<T>メインスレッド専用。構造変更に使用
EntityCommandBufferCreateEntity、DestroyEntity、AddComponent、RemoveComponent、Instantiate、Playback遅延構造変更
ECB.ParallelWritersortKeyパラメーターを持つECBと同様のメンバー並列ジョブでスレッドセーフ
SystemAPIQuery<T>、Time、GetSingleton<T>、GetComponent<T>、GetComponentLookup<T>システムからの静的アクセス
EntityQueryToEntityArray、ToComponentDataArray<T>、CalculateEntityCount一括操作
IComponentData(マーカーインターフェース)非マネージ構造体コンポーネント
IBufferElementData(マーカーインターフェース)動的バッファー要素
DynamicBuffer<T>Add、RemoveAt、Length、Clear、AsNativeArrayエンティティごとの可変長配列
IJobEntityExecute(ref T1, in T2, ...)パラメーターからクエリを自動生成
IJobChunkExecute(in ArchetypeChunk, ...)手動でのチャンクイテレーション
WorldDefaultGameObjectInjectionWorld、EntityManagerシステムとエンティティのコンテナー
RefRW<T>/RefRO<T>ValueRW/ValueRO読み書き可能/読み取り専用のコンポーネント参照

関連スキル

  • unity-scripting -- MonoBehaviourスクリプティング、コルーチン、async/await
  • unity-physics -- 物理エンジン、Rigidbody、コライダー(Unity Physics ECSパッケージ向け)
  • unity-foundations -- GameObjects、Transforms、Scenes、Prefabs

追加リソース

コメント