PrefabからEntityを生成する方法メモ(Entities v0.8.0)

はじめに

DOTS以前のUnityでは、PrefabからGameObjectを生成したい時、

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnPrefab : MonoBehaviour {

    [SerializeField] private GameObject prefab;

    private void Start () {
        Instantiate (prefab);
    }
}

のようなスクリプトを作成し、何らかのオブジェクトにアタッチしておけば、実行時にPrefabからGameObjectが生成されていました。

これと同じことをHybrid ECSで行う工程をメモとして残しておきたいと思い、この記事を書きました。

環境

  • Unity 2019.3.6f1
  • Entities 0.8.0
  • Unity Physics 0.2.5
  • Hybrid Renderer 0.4.0

Prefabの準備

生成したいGameObjectにConvertToEntityをアタッチし、必要に応じてAuthoring、PhysicsShape、PhysicsBody等をつけた後、Prefab化します。 (旧アーキテクチャのColliderがついている場合は消しておきましょう)

必要なComponentDataの作成

using Unity.Entities;

/// <summary>
/// 生成するPrefabに対応するEntityの情報
/// </summary>
public struct SpawnerData : IComponentData
{
    public Entity PrefabEntity;
}
using Unity.Entities;

/// <summary>
/// Spawnerを識別するためのタグコンポーネント
/// </summary>
public struct Spawner : IComponentData
{
}
using Unity.Entities;

/// <summary>
/// Spawnをするタイミングを伝えるためのタグコンポーネント
/// </summary>
public struct SpawnTag : IComponentData
{
}

Spawnerオブジェクトの作成

空のゲームオブジェクトを作成し、ConvertToEntityを付けます。

SpawnerAuthoringの作成

using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;

[DisallowMultipleComponent]
[RequiresEntityConversion]
public class SpawnerAuthoring : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
{
    [SerializeField] private GameObject prefab;
    
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new Spawner());

        dstManager.AddComponentData(entity, new SpawnerData()
        {
            PrefabEntity = conversionSystem.GetPrimaryEntity(prefab)
        });

        // 今回は簡単な例を考え、実行時にEntityを生成することにします。なのでここでSpawnTagをつけます
        dstManager.AddComponentData(entity, new SpawnTag());
    }

    public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    {
        referencedPrefabs.Add(prefab);
    }
}

これをSpawnerオブジェクトにアタッチし、最初に作成したPrefabをエディタ上のPrefabフィールドに代入します。

SpawnSystemの作成

using Unity.Entities;

/// <summary>
/// Prefabを生成するSystem
/// </summary>
public class SpawnSystem : SystemBase
{
    private EntityCommandBufferSystem _entityCommandBufferSystem;
    
    protected override void OnCreate()
    {
        _entityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        var concurrent = _entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent();
        
        Entities
            .WithBurst()
            .WithAll<SpawnTag, Spawner>()
            .ForEach(
            (Entity entity, int entityInQueryIndex, in SpawnerData spawnerData) =>
            {
                concurrent.Instantiate(entityInQueryIndex, spawnerData.PrefabEntity);

                // Entityを2つ以上生成しないために、ここでSpawnTagを剥がします
                // これをしないと、毎フレームEntityが生成されてしまうので
                concurrent.RemoveComponent<SpawnTag>(entityInQueryIndex, entity);
            }).ScheduleParallel();
        
        _entityCommandBufferSystem.AddJobHandleForProducer(Dependency);
    }
}

これで実行時にPrefabからEntityが生成されます。

ここでは、実行時にEntityを生成する簡単な場合を考えましたが、好きなタイミングでEntityを生成したい場合は、そのタイミングでSpawnTag ComponentDataをSpawnerのEntityに付ければokです。