見出し画像

Unity DOTS 入門 (2) - エンティティの生成

Unity DOTS の練習として、「エンティティ」の生成を行います。

・Unity 2019.3.14.f1
・Entities 0.11.1

1. DOTSパッケージのインストール

Unity DOTS 入門 (1)」と同様。

2. エンティティの生成

スクリプト「Spawner」を作成し、「エンティティ」を生成します。

(1) Hierarchyウィンドウの「+ → Create Empty」で空のゲームオブジェクトを作成し、名前に「Spawner」を指定。

(2) 「Spawner」にスクリプト「Spawner」を追加し、以下のように編集。

using Unity.Entities;
using UnityEngine;

public class Spawner : MonoBehaviour
{
    void Start()
    {
        EntityManager entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        entityManager.CreateEntity();
    }
}

World.DefaultGameObjectInjectionWorld.EntityManager」でデフォルトワールドの「EntityManager」を取得し、CreateEntity()で「エンティティ」を生成しています。

(3) 実行。
メニュー「Window → Analysis → Entity Debugger」で生成した「エンティティ」の存在を確認できます。Inspectorウィンドウに表示される「エンティティ」の属性は、コンポーネントを持ってないので空になります。

画像1

3. コンポーネントの追加

アーキタイプ」(コンポーネント種別セット)を使って、「エンティティ」に「コンポーネント」を追加します。

(1) スクリプト「Spawner」を以下のように編集。

using Unity.Entities;
using UnityEngine;
using Unity.Transforms;
using Unity.Rendering;
using Unity.Mathematics;

public class Spawner : MonoBehaviour
{
    void Start()
    {
        EntityManager entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        EntityArchetype archetype = entityManager.CreateArchetype(
            typeof(Translation),
            typeof(Rotation),
            typeof(RenderMesh),
            typeof(RenderBounds),
            typeof(LocalToWorld));
        entityManager.CreateEntity(archetype);
    }
}

アーキタイプ」は「コンポーネント種別セット」です。entityManager.CreateArchetype()で生成します。
今回は、以下の「コンポーネント種別」を設定しています。

・Translation : 位置
・Rotation : 回転
・RenderMesh : レンダリングメッシュ
・RenderBounds : レンダリング領域
・LocalToWorld : ローカル座標

CreateEntity()の引数に「アーキタイプ」を渡すことで、「コンポーネント種別セット」に含まれる「コンポーネント」を保持する「エンティティ」が生成されます。

(2) 実行。
「Entity Debugger」で「エンティティ」を選択することで、Inspectorウィンドウに追加された「コンポーネント」が表示されます。値は初期化されていません。

画像2

4. コンポーネントのデータの設定

「コンポーネント」のデータを設定して、立方体を表示します。

(1) スクリプト「Spawner」を以下のように編集。

using Unity.Entities;
using UnityEngine;
using Unity.Transforms;
using Unity.Rendering;
using Unity.Mathematics;

public class Spawner : MonoBehaviour
{
    [SerializeField] private Mesh mesh;
    [SerializeField] private Material material;

    void Start()
    {
        // エンティティの生成
        EntityManager entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        EntityArchetype archetype = entityManager.CreateArchetype(
            typeof(Translation),
            typeof(Rotation),
            typeof(RenderMesh),
            typeof(RenderBounds),
            typeof(LocalToWorld));
        Entity entity =entityManager.CreateEntity(archetype);

        // Translationコンポーネントのデータの指定
        entityManager.AddComponentData(entity, new Translation {
            Value = new float3(2f, 0f, 4f)
        });

        // RenderMeshコンポーネントのデータの指定
        entityManager.AddSharedComponentData(entity, new RenderMesh {
            mesh = mesh,
            material = material
        });
    }
}

AddComponentData() で、「Translation」コンポーネント(IComponentDataを継承)のデータを指定します。第1引数が「エンティティ」、第2引数が「コンポーネントデータ」になります。

AddSharedComponentData() で、「RenderMesh」コンポーネント(ISharedComponentDataを継承)のデータを指定します。

立方体を複数複製した場合、「Translation」はオブジェクト毎に別データですが、「RenderMesh」は同データで良いので、共有データコンポーネントになります。

(2) コンポーネント「Spawner」で、以下のように設定。

Mesh = Cube
Material = Default-Material

画像3

(3) 実行。

画像4

【おまけ】 エンティティの操作

◎ エンティティの生成

EntityArchetype archetype = entityManager.CreateArchetype(
    typeof(Translation),
    typeof(Rotation),
    typeof(RenderMesh),
    typeof(RenderBounds),
    typeof(LocalToWorld));
entityManager.CreateEntity(archetype);

◎ エンティティのクローン

Entity entity = entityManager.Instantiate(entityPrefab);

◎ エンティティの破棄

entityManager.DestroyEntity(entity);

◎ コンポーネントの追加

・AddComponent(Entity, ComponentType) : すべて
・AddComponentData<T>(Entity, T) : 基本コンポーネント
・AddSharedComponentData<T>(Entity, T) : 共有コンポーネント
・AddBuffer<T>(Entity) : 動的バッファコンポーネント
・AddChunkComponentData<T>(Entity) : チャンクコンポーネント
・AddComponentObject(Entity, object) : コンポーネントオブジェクト
entityManager.AddComponentData(entity, new Translation {
    Value = new float3(2f, 0f, 4f)
});

entityManager.AddSharedComponentData(entity, new RenderMesh {
    mesh = mesh,
    material = material
});

◎ コンポーネントの取得

・GetComponentData<T>(Entity) : 基本コンポーネント
・GetSharedComponentData<T>(Entity) : 共有コンポーネント
・GetBuffer<T>(Entity) : 動的バッファコンポーネント
・GetChunkComponentData<T>(ArchetypeChunk) : チャンクコンポーネント
GetComponentObject<T>(Entity) : コンポーネントオブジェクト
Position position = entityManager.GetComponentData<Position>(entity);

◎ コンポーネントの更新

・SetComponentData<T>(Entity, T) : 基本コンポーネント
・SetSharedComponentData<T>(Entity, T) : 共有コンポーネント
・SetChunkComponentData<T>(ArchetypeChunk, T) : チャンクコンポーネント
entityManager.SetComponentData(entity, new Translation {
    Value = new float3(2f, 0f, 4f)
});

動的バッファコンポーネントは、GetBuffer<T>でDynamicBuffer<T>構造体を取得し、それを使用して操作します。

DynamicBuffer<MyElement> buffer = EntityManager.GetBuffer<MyElement>(entity);
buffer.Add(new MyElement { Value = 123 });
buffer.RemoveAt(0);

◎ コンポーネントの削除

entityManager.RemoveComponent<Position>(entity);

【おまけ】 コンポーネントデータのコードの確認

◎ Transform
「Packages/Entities/Unity.Transforms/Translation」でコードを確認できます。

using System;
using Unity.Entities;
using Unity.Mathematics;

namespace Unity.Transforms
{
    [Serializable]
    [WriteGroup(typeof(LocalToWorld))]
    [WriteGroup(typeof(LocalToParent))]
    public struct Translation : IComponentData
    {
        public float3 Value;
    }
}

◎ Rotation
「Packages/Entities/Unity.Transforms/Rotation」でコードを確認できます。

using System;
using Unity.Entities;
using Unity.Mathematics;

namespace Unity.Transforms
{
    [Serializable]
    [WriteGroup(typeof(LocalToWorld))]
    [WriteGroup(typeof(LocalToParent))]
    [WriteGroup(typeof(CompositeRotation))]
    public struct Rotation : IComponentData
    {
        public quaternion Value;
    }
}

◎ RenderMesh
「Packages/Hybrid Renderer/Unity.Rendering.Hybrid/RenderMeshProxy」でコードを確認できます。

using System;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Rendering;

namespace Unity.Rendering
{
    [Serializable]
    [MaximumChunkCapacity(128)]
    public struct RenderMesh : ISharedComponentData, IEquatable<RenderMesh>
    {
        public Mesh mesh;
        public Material material;
        public int subMesh;

       [LayerField]
       public int layer;

       public ShadowCastingMode castShadows;
       public bool receiveShadows;
       public bool needMotionVectorPass;

       public bool Equals(RenderMesh other)
       {
           return
               mesh == other.mesh &&
               material == other.material &&
               subMesh == other.subMesh &&
               layer == other.layer &&
               castShadows == other.castShadows &&
               receiveShadows == other.receiveShadows &&
               needMotionVectorPass == other.needMotionVectorPass;
       }

       public override int GetHashCode()
       {
           int hash = 0;
           if (!ReferenceEquals(mesh, null)) hash ^= mesh.GetHashCode();
           if (!ReferenceEquals(material, null)) hash ^= material.GetHashCode();
           hash ^= subMesh.GetHashCode();
           hash ^= layer.GetHashCode();
           hash ^= castShadows.GetHashCode();
           hash ^= receiveShadows.GetHashCode();
           hash ^= needMotionVectorPass.GetHashCode();
           return hash;
       }
   }

   [AddComponentMenu("DOTS/Deprecated/RenderMeshProxy-Deprecated")]
   [Obsolete("RenderMeshProxy has been deprecated. Please use the new GameObject-to-entity conversion workflows instead. (RemovedAfter 2020-07-03).")]
   public class RenderMeshProxy : SharedComponentDataProxy<RenderMesh>
   {
       internal override void UpdateComponentData(EntityManager manager, Entity entity)
       {
           if (!manager.HasComponent<LocalToWorld>(entity))
               manager.AddComponentData(entity, new LocalToWorld {Value = float4x4.identity});

           base.UpdateComponentData(manager, entity);
       }
   }
}

◎ RenderBounds
「Packages/Hybrid Renderer/Unity.Rendering.Hybrid/RenderBoundsComponent」でコードを確認できます。

using Unity.Entities;
using Unity.Mathematics;

namespace Unity.Rendering
{
    public struct RenderBounds : IComponentData
    {
        public AABB Value;
    }

    public struct WorldRenderBounds : IComponentData
    {
        public AABB Value;
    }

    public struct ChunkWorldRenderBounds : IComponentData
    {
        public AABB Value;
    }
}

◎ RenderBounds
「Packages/Entities/Unity.Transforms/LocalWorld」でコードを確認できます。

using System;
using Unity.Entities;
using Unity.Mathematics;

namespace Unity.Transforms
{
   [Serializable]
   [WriteGroup(typeof(WorldToLocal))]
   public struct LocalToWorld : IComponentData
   {
       public float4x4 Value;

       public float3 Right => new float3(Value.c0.x, Value.c0.y, Value.c0.z);
       public float3 Up => new float3(Value.c1.x, Value.c1.y, Value.c1.z);
       public float3 Forward => new float3(Value.c2.x, Value.c2.y, Value.c2.z);
       public float3 Position => new float3(Value.c3.x, Value.c3.y, Value.c3.z);

       public quaternion Rotation => new quaternion(Value);
   }
}


この記事が気に入ったらサポートをしてみませんか?