諸注意
- 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どこに行った)
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】
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から見えなくなるようです。
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);
}
}
}
サンプルを実行すると、キューブが回転します。
(次回予定)Spawnerについて
上記のコードで最低限ECSを体験することができますが、実際はPrefabからEntityを作成するためのSpawnerクラスを作成するほうが便利に使えるようです。
(GameObjectをあらかじめ数千個用意するのは現実的ではないので・・・)
動作的に理解できてない箇所もあるので、次回があればSpawnerクラスについて紹介したいと思います。