ProjectTinyを眺めてみる その1

前書き

アドカレ用に書こうと思います。
https://qiita.com/advent-calendar/2020/botti-unityqiita.com



注意点

DOTSなので今後もAPI等が変わる可能性が十分にあります。



環境



プロジェクトを開く

今回はProjectTinySamples/Tiny3Dを開きます。



眺める

プロジェクト自体は風車がくるくる回るだけのようです。
このシーンで用意されているスクリプトは2つあります。

Assets/Scripts/Components/Rotate.cs

回転に必要な要素を持つコンポーネントです。

using UnityEngine;
using Unity.Entities;

namespace Tiny3D
{
    [GenerateAuthoringComponent]
    public struct Rotate : IComponentData
    {
        public float speedX;
        public float speedY;
        public float speedZ;
    }
}
GenerateAuthoringComponent属性

これはGameObjectをEntityに自動で変換してくれるものです。
今までIConvertGameObjectToEntityを継承したクラスを作り、EntityManagerにコンポーネントをセットする必要がありました。

IComponentData

汎用コンポーネントを作るにはこのインターフェイスを継承する必要があります。

Assets/Scripts/Systems/RotationSystem.cs

using System;
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny;
using UnityEngine;

namespace Tiny3D
{
    public class RotationSystem : SystemBase
    {
        protected override void OnUpdate()
        {
            float time = (float)Time.ElapsedTime;
            Entities.ForEach((ref Rotate rotate, ref Rotation rotation) =>
            {
                quaternion qx = quaternion.RotateX(time * rotate.speedX);
                quaternion qy = quaternion.RotateY(time * rotate.speedY);
                quaternion qz = quaternion.RotateZ(time * rotate.speedZ);
                rotation.Value = math.normalize(math.mul(qz, math.mul(qy, qx)));
            }).ScheduleParallel();
        }
    }
}
SystemBase

Systemを実装するのに必要なクラスになります。一連のライフサイクルイベントが定義されおり、全てメインスレッドで動作します。

  • OnCreate()
    • GameObjectのAwake関数にあたります。
  • OnStartRunning()
    • GameObjectのOnEnable関数にあたります。
  • OnUpdate()
    • GameObjectのUpdate関数にあたります。
  • OnStopRunning()
    • GameObjectのOnDisable関数にあたります。
  • OnDestroy()
    • GameObjectのOnDestroy関数にあたります。
Entities.ForEach

ラムダ式の引数にコンポーネントを指定すると、含んでいるEntityを取得し処理します。
引数にrefを指定すると書き込み専用、inを指定すると読み取り専用になり、ジョブが最適化されます。
ジョブを実行するのに、Schedule()ScheduleParallel()を使います。前者は1スレッド、後者は複数スレッドでジョブを実行します。
また、Run()を使用すると、メインスレッド上で直ちにジョブが実行します。



最後に

今回は一番シンプルなシーンになっています。ECSの必要最低限の機能を紹介しました。
次回はこのシーンで使われているTiny用の汎用スクリプトを見ていきます。