PrefabからEntityを生成(Entity Component System)

PrefabからEntityを生成(Entity Component System)

ECS(Entity Component System) 基礎中の基礎で作成したCubeを大量に生成します。

Script

Cube

CubeDataで使用するデータを定義しています。

using UnityEngine;
using Unity.Entities;
using Random = Unity.Mathematics.Random;

namespace EmptyCan
{
    public struct CubeData : IComponentData
    {
        public float Speed;
        public Vector3 Direction;
        public Vector3 Axis;
        public Quaternion Rotaiton;
        public Quaternion InitRotation;
        public float MaxRadius;

        public static CubeData Set(uint seed, float max_radius)
        {
            float min_speed = 100f;
            float max_speed = 10000f;
            Random random = new Random(seed);
            Quaternion rotation = Quaternion.Euler(random.NextFloat(0f, 360f), random.NextFloat(0f, 360f), random.NextFloat(0f, 360f));

            float min_value = 1f / max_radius;
            float t = random.NextFloat(min_value, 1f);
            t = random.NextFloat(t, 1f);  //原点付近で生成される数を減らすため
            float radius = t * max_radius;
            Vector3 direction = rotation * Vector3.up * radius;
            Vector3 axis = rotation * Vector3.forward;

            Quaternion init_rotation = Quaternion.FromToRotation(Quaternion.FromToRotation(Vector3.forward, axis) * Vector3.up, -direction);
            return new CubeData()
            {
                Speed = random.NextFloat(min_speed, max_speed),
                Direction = direction,
                Axis = axis,
                Rotaiton = rotation,
                InitRotation = init_rotation,
                MaxRadius = max_radius
            };
        }
    }
}

Entityの取得及びComponentの追加を行っています。

using Unity.Entities;
using UnityEngine;
using Unity.Mathematics;

namespace EmptyCan
{
    public class CubeAuthoring : MonoBehaviour
    {
        public uint Seed;
        public float MaxRadius;

        class Baker : Baker<CubeAuthoring>
        {
            public override void Bake(CubeAuthoring authoring)
            {
                CubeData data = CubeData.Set(math.max(1, authoring.Seed), math.max(1f, authoring.MaxRadius));
                Entity entity = GetEntity(TransformUsageFlags.Dynamic);
                AddComponent(entity, data);
            }
        }
    }
}

Cubeを動かすための処理です。今回は大量にCubeを動かすのでJobSytemを使用して並列に実行しています。

using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

namespace EmptyCan
{
    public partial struct CubeSystem : ISystem
    {
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            CubeUpdateJob job = new CubeUpdateJob() { Elapsed = (float)SystemAPI.Time.ElapsedTime };
            job.ScheduleParallel();
        }
    }

    [BurstCompile]
    partial struct CubeUpdateJob : IJobEntity
    {
        public float Elapsed;

        void Execute(in CubeData cube, ref LocalTransform xform)
        {
            float radius = cube.MaxRadius;
            float angle = cube.Speed / radius * Elapsed;
            Vector3 direction = cube.Direction;
            Vector3 axis = cube.Axis;
            Quaternion init_rotation = cube.InitRotation;

            Vector3 position = Quaternion.AngleAxis(angle, axis) * direction;
            xform.Position = position;

            Quaternion quaternion = init_rotation * Quaternion.FromToRotation(Vector3.forward, axis);
            quaternion = Quaternion.AngleAxis(angle, axis) * quaternion;
            xform.Rotation = quaternion;
        }
    }
}

Cubeの生成

Prefabを使用してCubeを生成します。以下のScriptはCubeを生成する際に使用するデータを定義しています。

using Unity.Entities;

namespace EmptyCan
{
    public struct Config : IComponentData
    {
        public Entity Prefab;
        public int SpawnCount;
        public uint RandomSeed;
    }
}

Cubeを生成するためのEntityの取得とComponentの追加を行っています。

using Unity.Entities;
using UnityEngine;
using Unity.Mathematics;

namespace EmptyCan
{
    public class ConfigAuthoring : MonoBehaviour
    {
        public GameObject Prefab;
        public int SpawnCount;
        public uint RandomSeed;

        class Baker : Baker<ConfigAuthoring>
        {
            public override void Bake(ConfigAuthoring authoring)
            {
                Config data = new Config()
                {
                    Prefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic),
                    SpawnCount = authoring.SpawnCount,
                    RandomSeed = math.max(1, authoring.RandomSeed)
                };

                AddComponent(GetEntity(TransformUsageFlags.None), data);
            }
        }
    }
}

Cubeを生成するためのSystemです。

  • RequireForUpdate
    SubScene内に指定したComponentがないとSystemが起動しないようにできます。よって、このSystemはConfigが存在しない場合は実行されません。
  • SystemAPI.GetSingleton
    Componentを一つだけ取得できます。これを利用してConfigを取得します。
  • state.EntityManager.Instantiate
    指定した数だけPrefabからEntityを生成します。
  • state.Enabled = false
    Systemを無効にします。これにより、Systemが一度だけ実行されるようにしています。
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Random = Unity.Mathematics.Random;

namespace EmptyCan
{
    [UpdateInGroup(typeof(InitializationSystemGroup))]
    public partial struct SpawnSystem : ISystem
    {
        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            state.RequireForUpdate<Config>(); //Configが存在しない場合、Systemは実行されない
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            Config config = SystemAPI.GetSingleton<Config>();
            //SpawnCountだけEntityを取得
            NativeArray<Entity> entities = state.EntityManager.Instantiate(config.Prefab, config.SpawnCount, Allocator.Temp);

            //取得したEntityのデータを設定
            Random random = new Random(config.RandomSeed);
            foreach (Entity entity in entities)
            {
                RefRW<CubeData> cube = SystemAPI.GetComponentRW<CubeData>(entity);
                cube.ValueRW = CubeData.Set(random.NextUInt(), cube.ValueRO.MaxRadius);
            }

            state.Enabled = false; //Systemを止める
        }
    }
}

設定と実行結果

CubeAuthoringを追加したPrefabを作成します。

空のGameObjectを作成し、ConfigAuthoringを追加します。これをSubScene内へ移動します。今回はSpawnCount=5000、RandomSeed=10としました。

これを実行すると以下のように、回転するCubeが大量に生成されます。

参考ページ

YouTube:Unity ECS で高速化!とりあえずキャラを 5,000 体出してみよう!