Unity ECS+Jobs SystemノートECS-Component(三)

13853 ワード

ソース:https://docs.unity3d.com/Packages/[email protected]/manual/index.html私は公式ドキュメントの内容を少し整理して、必要があれば公式ドキュメントを見ることができます

2.コンポーネント——Component


コンポーネントに格納されているエンティティに関連するデータ(エンティティを介してコンポーネントとそのデータをインデックスする)ECSのコンポーネントは、次のインタフェースを持つ構造です.
  • IComponentData
  • ISharedComponentData
  • ISystemStateComponentData
  • ISharedSystemStateComponentData

  • EntityManagerは、同じプロトタイプを持つすべてのエンティティのコンポーネントを1つのメモリに格納するArchetype(プロトタイプ)を構成します.chunk(ブロック)と呼ばれ、1つのブロックのすべてのコンポーネントが同じプロトタイプを持つ.SharedComponent(共有コンポーネント)は、エンティティを細分化するための特別な値を使用する特殊なコンポーネントです(他のエンティティを区別するために使用されます).エンティティに共有コンポーネントを追加すると、EntityManagerは同じ値を持つすべてのエンティティを同じブロックに配置し、共有コンポーネントはシステムが同じエンティティを一緒に処理できるようにします.注意:共有コンポーネントを過度に使用すると、プロトタイプと共有コンポーネントフィールドごとの一意の値の組み合わせに基づいて拡張するために必要なメモリブロックの数が異なるため、共有コンポーネントに不要なフィールドを追加することを避ける必要があります.Entity Debuggerで現在のブロック使用率を表示できます.エンティティにコンポーネントを追加または削除し、SharedComponentの値を変更すると、EntityManagerはエンティティを別のChunkに移動し、必要に応じて新しいChunkを作成します.システム・ステータス・コンポーネントの動作は、通常のコンポーネントまたは共有コンポーネントと似ていますが、EntityManagerでは、エンティティを破棄するときにシステム・ステータス・コンポーネントは削除されず、エンティティIDを削除する前に回収されません.この違いにより、エンティティを破棄するときにシステムが内部ステータスを消去したり、リソースを解放したりすることができます.

    2.1、コンポーネントの紹介


    2.1.1、ComponentData


    UnityのComponentData(標準ECSシステムのComponentでもある)は、エンティティにとってデータのみを含む構造であり、メソッドは含まれません.古いUnityシステムに比べて、彼は古いComponentシステムに似ていますが、データだけを含むComponentです.UnityECSはIComponentDataインタフェースを提供して使用します.
  • 従来のUnityコンポーネント(MonoBehaviourを含む)は、データおよびメソッド
  • を含むオブジェクト向けクラスである.
  • IComponentDataはECSコンポーネントであるため、データ
  • のみを含むメソッドは含まれません.
  • IComponentDataはクラスではなく構造体です.これは、デフォルトでは参照によってコピーされるのではなく値によってコピーされることを意味します.次のモードを使用してデータを変更する必要があります.
  • var transform = group.transform[index]; // Read
    
    transform.heading = playerInput.move; // Modify
    transform.position += deltaTime * playerInput.move * settings.playerMoveSpeed;
    
    group.transform[index] = transform; // Write
    

    2.1.2、SharedComponentData


    IComponentDataは、エンティティ間の異なるデータ、例えば、格納されているWorldに適用され、ISharedComponentDataは、多くのエンティティに共通点がある場合に使用されます.例えば、Boidプレゼンテーションでは、同じPrefabからのRenderMeshの多くのBoidエンティティをインスタンス化する必要があります.これらのエンティティは完全に同じです.
    [System.Serializable]
    public struct RenderMesh : ISharedComponentData
    {
        public Mesh                 mesh;
        public Material             material;
    
        public ShadowCastingMode    castShadows;
        public bool                 receiveShadows;
    }
    

    最高は、ISharedComponentDataを使用すると、エンティティごとのメモリコストがほぼゼロになります.ISharedComponentDataを使用すると、同じInstanceRendererデータを持つエンティティがグループ化され、すべてのマトリクスが効率的に抽出されてレンダリングされます.アクセスするデータのレイアウトが全く同じであるため、生成されたコードは簡単で効果的です.SharedComponentDataの使用上の注意点:
  • 同じSharedComponentDataを持つエンティティは同じChunkによって組織されているが、これらのSharedComponentDataのインデックスは各エンティティではなくChunkによって格納されているため、SharedComponentDataは各エンティティのメモリオーバーヘッドに対してゼロ
  • である.
  • EntityQueryを使用すると、同じタイプのエンティティ
  • をすべて反復できます.
  • また、EntityQueryを使用することができる.SetFilter()反復特定のSharedComponentData値を持つエンティティは、データレイアウトのためにコストが低い
  • を反復する.
  • EntityManagerを使用GetAllUniqueSharedComponentsは、既存のエンティティに追加されたすべてのSharedComponentData
  • を取り戻すことができます.
  • SharedComponentDataは、
  • のカウントを自動的に参照します.
  • SharedComponentDataはほとんど変更されないはずです.変更が必要な場合はmemcpyを使用してそのエンティティのすべてのComponentDataを異なるChunkにコピーする必要があります.

    2.1.3、SystemStateComponents


    SystemStateComponentDataの目的は、システム内部のリソースを追跡し、必要なリソースを適切に作成して破棄することです.システムStateComponentDataとSystemStateSharedComponentDataは、ComponentDataとSharedComponentDataのような重要な側面を除きます.
  • SystemStateComponentDataは、エンティティの破棄時に
  • を削除しません.
    DestroyEntityの役割は次のとおりです.
  • は、特定のエンティティIDに基づいて、そのすべてのコンポーネントの参照
  • を見つける.
  • これらのコンポーネント
  • を削除する.
  • は、
  • を再利用するためにエンティティIDを回収する.
    ただし、SystemStateComponentDataが存在する場合は削除されません.これにより、エンティティIDに関連付けられたリソースまたはステータスを消去することができ、エンティティ内のすべてのSystemStateComponentDataが削除された場合にのみ、エンティティIDが再利用されます.
    動機
  • システムは、ComponentDataの内部状態を維持する必要がある場合がある、例えば、リソースの割り当て時
  • .
  • システムは、値および状態の変更が他のシステムによって行うため、例えば、コンポーネント内の値が変更された場合、または関連するコンポーネントが追加または削除された場合、その状態を管理する必要がある
  • .
  • 「コールバックなし」はECS設計規則の重要な要素である
  • コンセプト
    SystemStateComponentDataの一般的な用途は、ユーザコンポーネントをミラーすることであり、内部状態を提供することである.
  • FooComponent(ComponentData、ユーザー割当)
  • FooStateComponent(SystemComponentData,システム割当て)
  • 検出コンポーネントの追加
    ユーザーがFooComponentを追加すると、FooStateComponentにFooSystemが存在しない場合、FooStateComponentが存在しない状態でクエリーFooComponentコンポーネントが更新され、追加されたと推定されます.この場合、FooSystemはFooStateComponentと必要な内部状態を追加します.
    コンポーネント削除の検出
    ユーザーがFooComponentを削除すると、FooStateComponentは依然として存在します.FooSystemは、FooComponentなしでクエリーを更新します.FooStateComponentコンポーネントは削除されたと推定されます.FooSystemは、FooStateComponentを削除し、必要な内部状態を修復します.
    エンティティの破棄の検出
    DestroyEntityの役割は次のとおりです.
  • は、特定のエンティティIDに基づいて、そのすべてのコンポーネントの参照
  • を見つける.
  • これらのコンポーネント
  • を削除する.
  • は、
  • を再利用するためにエンティティIDを回収する.
    ただし、SystemStateComponentDataは削除しないと、DestroyEntityが最後のコンポーネントを削除するまでエンティティIDは回収されません.これにより、システムはコンポーネントを削除するのと同じ方法で内部状態をクリーンアップする機会があり、SystemStateComponentDataを作成するときにReadOnlyの修飾子を与えます.
    SystemStateComponent
    SystemStateComponentData ComponentData ComponentDataと同様の使い方
    struct FooStateComponent : ISystemStateComponentData
    {
    }
    

    SystemStateComponentDataの修飾子の使用方法は通常のコンポーネントの使用方法と同じである(using private,public,internal)が、例外として一般的には、
    SystemStateSharedComponent
    SystemStateSharedComponentData SharedComponentDataと同様の使い方
    struct FooStateSharedComponent : ISystemStateSharedComponentData
    {
    	public int Value;
    }
    

    2.1.4、Dynamic Buffers


    DynamicBufferは、エンティティに関連付けられた可変サイズの弾力性のあるコンポーネントで、一定の数の要素に耐えられるコンポーネントとして表現されますが、内部容量が消費されるとスタックメモリが割り当てられます.このメソッドを使用すると、メモリ管理は自動化され、メモリはDynamicBuffersによって接続され、DynamicBuffersはEntityManagerによって管理されるため、DynamicBufferコンポーネントを削除すると、関連するヒープメモリも自動的に解放されます.削除されたfixed arrayに代わってDynamicBuffers
    Bufferの要素タイプを宣言
    Bufferを宣言するには、挿入する要素タイプを使用して宣言します.
    // This describes the number of buffer elements that should be reserved
    // in chunk data for each instance of a buffer. In this case, 8 integers
    // will be reserved (32 bytes) along with the size of the buffer header
    // (currently 16 bytes on 64-bit targets)
    [InternalBufferCapacity(8)]
    public struct MyBufferElement : IBufferElementData
    {
        // These implicit conversions are optional, but can help reduce typing.
        public static implicit operator int(MyBufferElement e) { return e.Value; }
        public static implicit operator MyBufferElement(int e) { return new MyBufferElement { Value = e }; }
    
        // Actual value each buffer element will store.
        public int Value;
    }
    

    これは奇妙に見えますが、この説明は2つのメリットからなります.
  • 複数のfloat 3タイプのDynamicBufferまたは他の一般的な値タイプをサポートし、同じ値タイプを利用した任意の数のデータをBuffersに追加することができます.要素がユニークにトップ構造体に
  • 包装されている限り、
  • EntityArchetypesにBuffer要素タイプを含めることができます.これは通常、1つのコンポーネント
  • として表現されます.
    エンティティへのBufferタイプの追加
    Bufferタイプコンポーネントをコンポーネントタイプに追加する一般的な方法を使用します.
  • AddBuffer()
  • を使用
    entityManager.AddBuffer<MyBufferElement>(entity);
    
  • プロトタイプ
  • を使用
    Entity e = entityManager.CreateEntity(typeof(MyBufferElement));
    

    アクセスBufferタイプ
    DynamicBuffersにアクセスするには、従来のコンポーネントデータへの並列アクセス方法としていくつかの方法があります.
  • プライマリスレッドのみが
  • に直接アクセス
    DynamicBuffer<MyBufferElement> buffer = entityManager.GetBuffer<MyBufferElement>(entity);
    

    エンティティベースのアクセス
  • JobComponentSystemを介して各エンティティコンポーネントに対してBuffers
  • を問い合わせる
        var lookup = GetBufferFromEntity<EcsIntElement>();
        var buffer = lookup[myEntity];
        buffer.Append(17);
        buffer.RemoveAt(0);