LoginSignup
13
8

More than 5 years have passed since last update.

修羅のためのUnity ECS学習(1)(2019年04月の時点、Unity2019.1.0b5)

Last updated at Posted at 2019-04-06

諸注意

  • 2019.1.0b5以降がベースになります。Unity2018をお使いの方は参考にならないと思います。(以下の公式サンプルが2019.1.0b5使ってます。)
  • 記事を読む前に、 https://github.com/Unity-Technologies/EntityComponentSystemSamples のサンプルを必ず目を通してください。 Unity ECSを学ぶにはソースコードを読むのが一番早いです。 サンプルに目を通さずに、(この記事も含めて)ネット上にある記事のコードを参考にすると、新旧のコードの書き方の違いに悩まされます。
  • この記事は2019.04.06 に書かれました。 記事に書かれている内容は 今後の開発によって非推奨(deprecated)になる可能性があります。
  • ("ECS"タグを使っていないのは、AWSのECSとかぶっているためです。)

記事の内容

  • 2019年4月時点のECSのサンプルコードを雑に紐解く
  • どんなコードが使われているかを紹介(HelloCube_02の内容がメイン)
  • Spawnerについては次回の予定(HelloCube_06の内容を予定)

はじめに

4月を開始してぐらいから、UnityのEntityComponentSystem(ECS)を改めて勉強するために、いろいろネット上の記事を調べていましたが、2018年から現在までにかなり処理系統が変更されているようで、今どんなコードが使えて、どんなコードが使えないのかを整理するため、この記事を書きました。

そもそもUnity ECSとは何か

これについてはテラシュールブログ様の記事を読むのが良いです。

【Unity】Unity 2018のEntity Component System(通称ECS)について(1)- テラシュールブログ
【Unity】Unity 2018のEntity Component System(通称ECS)について(2)- テラシュールブログ

すごく雑に言うと、「Monobehaviourを継承したクラスのUpdate()内で数千数万のGameObjectやComponentを直接操作するのはやめろ。代わりにメインスレッド外でGameObjectやComponentっぽいものを扱えるようにしたのでこっち(ECS)を使え」 という感じです。

サンプルで登場するクラスやコードたち

各要素の単位はコンポーネントと呼ばれているようなので、ここからはコンポーネントと呼びます。(UnityEngine.Componentとは異なるので注意です。)

  • IComponentData
    自分で定義できるコンポーネントです。Transform以外のコンポーネントを用意するときに使用します。 位置情報(Translation)回転(Rotation)などのTransformに関連するコンポーネントはUnity.Transforms内で既に用意されているので、Transform関係の処理は自分で定義しなくて良いです。 以下は公式サンプルのソースです。 (共有データっぽいのにIComponentDataが使用されています。ISharedComponentDataどこに行った)
RotationSpeed.cs
using System;
using Unity.Entities;

namespace Samples.HelloCube_02
{
    [Serializable]
    public struct RotationSpeed : IComponentData
    {
        public float RadiansPerSecond;
    }
}
  • ComponentSystem(ForEach使用時), JobComponentSystem,(JobSystem使用時)
    コンポーネントに対する処理を定義するためのクラスです。OnUpdate() 内に具体的な処理を記述します。 以下は公式サンプルのソースのうち、ジョブの定義にIJobProcessComponentDataを使ったものです。 (※0.0.12-preview.30から IJobForEachにリネームされるようです)
    【1.OnUpdate()内でジョブを初期化】、【2.ジョブをスケジュール】、【3.別スレッドでジョブ実行】というループの処理になっているようです。(ループ実行は IJobProcessComponentData のみかもしれません。)
    それぞれの元のソースコードは以下のGitHubのコードを参照してください。(以下の引用ではBurstCompileのアトリビュートを外しています。) ForEachは結局メインスレッド内で計算処理をしているため、処理を効率化したい場合は使わないほうがよさそうです。

【HelloCube_01_ForEach/RotationSpeedSystem.cs】
【HelloCube_02_IJobProcessComponentData/RotationSpeedSystem.cs】
【HelloCube_03_IJobChunk/RotationSpeedSystem.cs】

HelloCube_02_IJobProcessComponentData/RotationSpeedSystem.cs
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

namespace Samples.HelloCube_02
{
    public class RotationSpeedSystem : JobComponentSystem
    {
        struct RotationSpeedJob : IJobProcessComponentData<Rotation, RotationSpeed>
        {
            public float DeltaTime;

            public void Execute(
                ref Rotation rotation,
                [ReadOnly] ref RotationSpeed rotSpeed
            )
            {
                rotation.Value = 
                    math.mul(
                        math.normalize(rotation.Value),
                        quaternion.AxisAngle(
                            math.up(),
                            rotSpeed.RadiansPerSecond * DeltaTime
                        )
                    );
            }
        }

        protected override JobHandle OnUpdate(JobHandle inputDependencies)
        {
            var job = new RotationSpeedJob()
            {
                DeltaTime = Time.deltaTime
            };

            return job.Schedule(this, inputDependencies);
        }
    }
}
  • IConvertGameObjectToEntity
    ECSのコンポーネントは直接作成するのではなく、エンティティ(Entity)という実体の中のコンポーネントとして登録します。(GameObjectと似た構造?)
    EntityはEntityManagerから直接作成する方法と、GameObjectやPrefabから変換して用意する方法(Hybrid)があります。IConvertGameObjectToEntityは後者の方法です。(ほかにもやり方があれば、誰か教えてください。)
    変換対象のGameObjectにIConvertGameObjectToEntityを継承したスクリプトを追加します。 注意点として、GameObjectからEntityに変換する場合、そのGameObjectに別途ConvertToEntityのスクリプトを手動で追加する必要があります。(Prefabから作成する場合も同様に、Entityを作成しているGameObjectにConvertToEntityのスクリプトを追加しておく必要があります。Prefab内にはConvertToEntityは必要ありません。その他、別のシーンのGameObjectから作成する方法もありますが、割愛します。)
    ゲーム実行時にEntityに変換されます。変換後は対象のGameObjectは通常の管理下から外れ、Hierarchyから見えなくなるようです。
HelloCube_02_IJobProcessComponentData/RotationSpeedProxy.cs
using System;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

namespace Samples.HelloCube_02
{
    [RequiresEntityConversion]
    public class RotationSpeedProxy : MonoBehaviour, IConvertGameObjectToEntity
    {
        public float DegreesPerSecond = 360;

        public void Convert(
            Entity entity,
            EntityManager dstManager,
            GameObjectConversionSystem conversionSystem
         )
         {
            var data = new RotationSpeed
            {
                RadiansPerSecond = math.radians(DegreesPerSecond)
            };
            dstManager.AddComponentData(entity, data);
        }
    }
}

rotationcube1.jpg

サンプルを実行すると、キューブが回転します。

rotationcube2.jpg

(次回予定)Spawnerについて

上記のコードで最低限ECSを体験することができますが、実際はPrefabからEntityを作成するためのSpawnerクラスを作成するほうが便利に使えるようです。
(GameObjectをあらかじめ数千個用意するのは現実的ではないので・・・)
動作的に理解できてない箇所もあるので、次回があればSpawnerクラスについて紹介したいと思います。

13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8