【Unity】DOTS Visual Scriptingを試してみた

今年のUniteの「[Unity開発ロードマップ最新情報](https://learning.unity3d.jp/3354/)」にて、**DOTSベースのビジュアルスクリプティングツール**が開発中であると言う旨が話されました。

![unite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/28f26b1e-8f3f-b9c8-2343-9c6618d3dbee.png)
※上記講演のスライドより引用

このツール自体は引用しているスライド中にある通り、Unity2020系統でのプレビューリリース(付け加えると2020年のGDCの前後)を目標としているものとなりますが、ツール自体は**フィードバックを目的とした開発版の物がフォーラムにて公開されており、**現時点で6thまでリリースされてます。

今回話す内容としては最新版である「[DOTS Visual Scripting 6th experimental drop](https://forum.unity.com/threads/dots-visual-scripting-6th-experimental-drop.795807/)」を触った上での機能や所感について色々とメモしていければと思います。


### <font color="Red">注意点</font>

フォーラムでも免責事項として言及されてますが、大雑把に纏めると以下の点についてはご了承ください。

- **実稼働用ではない**
    - まだ動作不安定なところがあったりも
- **破壊的変更の可能性大いに有り**

後はDOTS(特にECS)に関する知識が多少必要となってきますが、記事中では基礎的な所や概念については触れません。
これらに関する初学者向けの資料としては今年のUniteであった以下の講演が比較的新しい上に分かりやすくてオススメです。(どちらか一方ではなく、合わせて見た方が良いかも)

- [たのしいDOTS 〜初級から上級まで〜](https://learning.unity3d.jp/3252/)
- [大量のオブジェクトを含む広いステージでも大丈夫、そうDOTSならね](https://learning.unity3d.jp/3266/)






### ツールの呼び名について

フォーラムの表題では`DOTS Visual Scripting`と記載されてますが、Package Manager側の方では`Visual Scripting ECS`と表記されていたりします。[^1]

[^1]: 後述しますが、現時点で出来ることとしては「ECSのコード生成」となるので呼び名的には後者が正しい感はある。

最終的にどの名称が採択されるのかはわかりませんが、この記事中では**「DOTS Visual Scripting」**の名称で統一します。 (便宜上、`DOTS VS`と省略)


### インストール & バージョン情報

- **Unity 2019.3.0f3**
- **DOTS VS**
    - [DOTS Visual Scripting 6th experimental drop](https://forum.unity.com/threads/dots-visual-scripting-6th-experimental-drop.795807/)

インストール方法については↑のフォーラムリンクを参照。
ちなみに6thからはmanifest.jsonに指定のパッケージを追記する形で取得できます。
(※Editor上のPackageManagerからの取得はまだ出来ない)










# 学び始めるには

具体的な解説に入っていく前にオススメのチュートリアル動画を紹介します。
こちらの方も合わせて見ると理解が深まるかもしれません。

**`DOTS VS`を学び始めるにあたっては以下の動画がおすすめです。**
「[Unity公式のSpace Shooter tutorial](https://learn.unity.com/project/space-shooter-tutorial)」をベースに`DOTS VS`で実装を行う内容であり、全5回分の動画があります。

英語動画ではありますが、字幕対応しているので自動翻訳で日本語化することも可能です。
(精度は自動翻訳なのでそこだけはご了承を)

- [Chapter 1: Create your first Entity and Component in ECS](https://www.youtube.com/watch?v=bptCy1ZDQ2g)
- [Chapter 2: Create ComponentSystem](https://www.youtube.com/watch?v=9EuGd2WmDvU)
- [Chapter 3： Instantiate Entity and Create MacroNode in Unity ECS](https://www.youtube.com/watch?v=ZbS2mHGdogM)
- [Chapter 4: Boundary Detection and Destroy Entity in Unity ECS with Visual Scripting Tool](https://www.youtube.com/watch?v=vNOX8ZMEauM)
- [Chapter 5: Enemy Generation and Collision Detection](https://www.youtube.com/watch?v=7C7luPCViW8)

他にも[3rd Dropにて**Roll a Ball Demo**のデモ](https://forum.unity.com/threads/dots-visual-scripting-3rd-experimental-drop.727190/)が公開されているので、こちらをサンプルとして追ってみるのも有りかもしれません。










# 機能について

ここからは機能解説に入っていきます。
※ここらを把握済みであり「所感を聞きたい」と言う方は読み飛ばしてもokです。 → [触ってみた所感について](https://qiita.com/mao_/private/864022d716af679ff618#%E8%A7%A6%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F%E6%89%80%E6%84%9F%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

## 何が出来るのか？

現時点の機能で出来ることとしては、大まかに言うと**ツールで組んだロジックからECSのコードを生成する事が出来ます。** [^2], [^3]
※具体的にどういった物が生成できるのかについては後述

[^2]: 故に名称的にはPackageManagerの表記である「`Visual Scripting ECS`」が機能的に正しい感があったりも。
[^3]: 逆に言うとECSの知見があって自分でコードを書ける場合には`DOTS VS`を使わなくても実装出来ると思われる。

ビジュアルスクリプティングでECSのコードを実装できるということは、非プログラマーの方でもロジックに集中して実装を行いつつもECSベースのパフォーマンスの良いコードを生成できるという利点があるかと思います。

具体的な機能詳細について解説していきます。





## ComponentDataの定義

**機能の1つとして「ツール上から`ComponentData`を定義したコードを生成する機能」があります。**
ComponentDataの定義はAssetsのメニューから「Create → Visual Script → New Component各種」で生成できます。

![componentdata.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/2dea4065-b16c-2010-97ed-a82449bacc53.png)

生成すると以下の画面が開かれるので、こちらからComponentDataが持つフィールドなどを設定していきます。

ちなみにメニュー側にも`Component`の他に`Shared Component`や`Buffer Element`と言った物がありますが、これらを指定することでそれに応じたComponentDataを定義することも可能です。[^4]

[^4]: `Event`と言うのもありますが...詳細についてまでは追いきれておらず...パッと見た感じだと`IBufferElementData`が生成される上で`DotsEventAttribute`と言う属性がつくっぽいが...?

![componentdata_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/edb6dfc1-8c69-19c0-4326-7728816b08b7.png)


### 生成コードについて

定義が完了したら上記ウィンドウの**右下にある【Save】ボタンを押下することでコードが生成されます。**

生成されるコードとしては、**指定した名称に対し`Proxy`と言うサフィックスが付いたC#コードとなります。** 
→ `Sample`と言う名称を指定したら`SampleProxy.cs`が生成される。


#### サフィックスの理由について

明言されていたわけではないので自分の考えとなりますが、**現時点でサフィックスが付く理由としては、生成されたコードを見た感じだと「オーサリングコンポーネントとしても機能出来るようにする」と言った意図がありそうです。**
※生成されるコードはテキストエディタで直接開くか、上記ウィンドウと一緒に開かれるであろう`Code Viewer`と言うウィンドウから確認することが可能。

以下に生成したサンプルを引用します。(説明用に幾つかコメントを追記)

<details><summary>**`SampleProxy.cs`(クリックで展開)**</summary><div>

```csharp:SampleProxy.cs
using System;
using System.ComponentModel;
using Unity.Entities;
using UnityEngine;
using VisualScripting.Entities.Runtime;
using System.Collections.Generic;

// Editor上から定義したデータ本体
[Serializable, ComponentEditor]
public struct Sample : IComponentData
{
    public int IntValue;
    public Unity.Entities.Entity Prefab;    // GameObjectから変換されたPrefabEntity
}

// オーサリング用コンポーネント
[AddComponentMenu("Visual Scripting Components/Sample")]
class SampleProxy : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
{
    public int IntValue;
    
    // Editor上で「GameObject」として指定したデータはEntityに変換される。
    // これによりPrefabEntityの運用が可能に。
    public UnityEngine.GameObject Prefab;

    public void Convert(Unity.Entities.Entity entity, Unity.Entities.EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new Sample { IntValue = IntValue, Prefab = conversionSystem.GetPrimaryEntity(Prefab) });
    }

    public void DeclareReferencedPrefabs(List<UnityEngine.GameObject> referencedPrefabs)
    {
        referencedPrefabs.Add(Prefab);
    }
}
```

</div></details>


#### 補足: GameObjectについて

ComponentDataを生成する際にはフィールドに対し、**直接`GameObject`を指定することが出来ます。**

この指定を行うとGameObjectは自動的に**`PrefabEntity`に変換されてPureECS上で機能するようになります。**
変換のフローについては上記の生成後のコードを見ると分かりやすいかもです。

![componentdata_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/b40780ee-41fe-6459-e3c3-457036a896fc.png)





## ECS Graph(ComponentSystem)の実装

もう一つの機能である`ECS Graph`は実際にロジックを組む部分であり、**最終的には組み込んだロジックを元にComponentSystemのコードが生成されます。**

こちらはAssetsのメニューから「Create → Visual Script → ECS Graph」でグラフの生成できます。

![graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/b19debfe-a297-2bf3-90dd-c12e4a9c1869.png)

生成を行うと以下の初期画面(初期テンプレ)が表示されます。

![Art002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/58d03dcf-9ffa-3a28-2383-42b9100a13ea.png)

ちなみにこの状態で生成されるコードとしては以下のものとなり、`OnUpdate`(グラフ上では`On Update Entities`)の中で`myQuery`と言う`EntityQuery`を見て処理を行っているのが見えてきます。

<details><summary>**`SampleSystem.cs`(クリックで展開)**</summary><div>

```csharp:SampleSystem.cs
using Microsoft.CSharp;
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

public class SampleSystem : ComponentSystem
{
    private Unity.Entities.EntityQuery myQuery;
    protected override void OnCreate()
    {
        myQuery = EntityManager.UniversalQuery;
    }

    protected override void OnUpdate()
    {
        {
            Entities.With(myQuery).ForEach((Unity.Entities.Entity myQueryEntity) =>
            {
            }

            );
        }
    }
}
```

</div></details>


### 画面構成

基本的な画面構成については初期版のForumである「[DOTS Visual Scripting first experimental drop](https://forum.unity.com/threads/dots-visual-scripting-first-experimental-drop.677476/)」に纏まってますが、一部補足を加えたものを記事中でも記載します。
※Forumの方は初期版故に一部画面が古いので注意。(それを言うとこの記事も今後古くなっていくと思われるが..。)

サンプルとして用意してある以下のグラフの詳細についてはStickynoteのコメントを御覧ください。
このグラフ自体はシーン中にCubeと言ったGameObjectを置き、`Convert to Entity`でEntityに変換することで動作確認可能です。

![all_graph2_com2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/9fdb76c9-9e19-0cd7-ce75-b4221d6b8c0e.png)


#### 各種ウィンドウについて

各種ウィンドウに対しては<font color="Blue">青い番号</font>で振ってあります。

- **<font color="Blue">①: Visual Script</font>**
    - メインとなるグラフビュー
- **<font color="Blue">②: Blackboard</font>**
    - `EntityQuery`の定義やグラフ内(ComponentSystem内)での変数の定義が可能
- **<font color="Blue">③: MiniMap</font>**
    - グラフ全体のミニマップを表示
- **<font color="Blue">④: Code Viewer</font>**
    - 現在のグラフに対応するComponentSystemのコードを表示
- **<font color="Blue">⑤: Inspector</font>**
    - グラフの詳細の設定
    - **「JobSytstemの有無」や「実行順」に関する設定はこちらから指定可能**
        - `Use Job System` → JobSystemの有効/無効
        - `UpdateBefore`/`UpdateAfter` → グラフ間の実行順
            - ※ComponentSystem間の実行順とも言い換えられる
        - `Execution Order` → グラフ内の実行順
            - ※`On Start Entities`や`On Update Entities`などの実行順を指す
    - グラフ内で適当なノードを選択したり、ProjectViewからグラフのAssetを選択することで表示可能


#### 各種ボタン/設定項目について

各種ボタンや設定項目については<font color="Red">赤い番号</font>で振ってあります。

- **<font color="Red">①: Save All</font>**
    - グラフの保存
- **<font color="Red">②: Build All</font>**
    - **グラフのビルドを行い、結果をC#コードとして出力**
        - 出力先は`Assets/Runtime/VisualScripting/[GraphName].cs`
    -  ※挙動が少し複雑なので後述
- **<font color="Red">③: Code Viewer</font>**
    - ※上述の<font color="Blue">**「④: Code Viewer」**</font>を開く
- **<font color="Red">④: Show MiniMap</font>**
    - ※上述の<font color="Blue">**「③: MiniMap」**</font>を開く
- **<font color="Red">⑤: Show Blackboard</font>**
    - ※上述の<font color="Blue">**「②: Blackboard」**</font>を開く
- **<font color="Red">⑥: Refresh UI</font>**
    - UI全体の更新
- **<font color="Red">⑦: Options</font>**
    - コンパイルや`Show unused nodes`の設定切り替えが可能
- **<font color="Red">⑧: EntityQuery → Required Components</font>**
    - `EntityQuery`に対する必要となるComponentDataの定義
        - ※上の例では`Translation`と`Rotation`を持つEntityを対象に設定
- **<font color="Red">⑨: EntityQuery → Additional Criterias</font>**
    - `EntityQuer`に対する条件式
        - ※上の例ではBlackboardからは設定していないが、`On Update Entities`ノード側で「Tlanslate.y < 10」でフィルタリングするように設定している。
- **<font color="Red">⑩: Toggle Tracing for Current Instance</font>**
    - インスタンスに対するフレームトレースを行う。詳細は後述


#### グラフの実行/ビルドについて

グラフを実行する際には**「そのグラフを開いている時のみ」実行時に自動でコンパイルが走るように思われました。**
※グラフ中に`Compilation Pending`と表示されるはず。

**逆に言うとグラフを閉じている時には自動コンパイルが走らないために、そのままだと動作しません。**
閉じた状態で実行を行うには**【Build All】**ボタンから**グラフをビルドしてC#コードとして出力する必要があります。**

ここで一点注意点として、「グラフをビルドした状態」で「グラフを開いたまま」実行を行うと、**実行中に動的コンパイルが走って処理が2回呼ばれる事がありました。**
→ 例えば`On Start Entities`に初期化処理を書いているとした場合、実行時にビルドしたコード側からの`On Start Entities`の呼び出したあった後に動的コンパイルが走り、完了した時点でもう一度「コンパイル済みの方」の`On Start Entities`が呼ばれるので初期化処理が2回走ります。

どこか設定を見落としているだけかもしれない + 将来的に改修されるところかもしれませんが、一応補足まで。

### 補足: グラフの出力結果について

参考までに上記のグラフの出力結果を載せておきます。(こちらも説明用に幾つかコメントを追記)

<details><summary>**`SampleSystem.cs`(クリックで展開)**</summary><div>

```csharp:SampleSystem.cs
using Microsoft.CSharp;
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

public class SampleSystem : ComponentSystem
{
    private Unity.Entities.EntityQuery TransformQuery;
    private Unity.Entities.EntityQuery HogeQuery;

    // Blackboard上から定義した変数はSingletonなEntityとして保持される
    public struct GraphData : Unity.Entities.IComponentData
    {
        public float addAngle;
    }

    protected override void OnCreate()
    {
        // Blackboard上から定義したEntityQueryの初期化
        TransformQuery = GetEntityQuery(
            ComponentType.ReadWrite<Unity.Transforms.Translation>(),
            ComponentType.ReadWrite<Unity.Transforms.Rotation>());
        HogeQuery = GetEntityQuery(ComponentType.ReadOnly<Unity.Transforms.Translation>());
        EntityManager.CreateEntity(typeof(GraphData));
        SetSingleton(new GraphData { addAngle = 0.3F });
    }

    protected override void OnUpdate()
    {
        // On Update Entitiesの処理内容
        // ※グラフ中には説明用に「On Start Entities」と「On End Entities」のノードも置いてあるが、
        //   処理が未定義なので未出力となっている。
        GraphData graphData = GetSingleton<GraphData>();
        {
            Entities.With(TransformQuery).ForEach((
                Unity.Entities.Entity TransformQueryEntity,
                ref Unity.Transforms.Translation TransformQueryTranslation,
                ref Unity.Transforms.Rotation TransformQueryRotation) =>
            {
                // 「Additional Criterias」に設定した条件式
                var Criteria = TransformQueryTranslation.Value.y < 10F;
                if (!Criteria)
                {
                    return;
                }

                // 「Translate by」の処理
                TransformQueryTranslation.Value += new float3(0F, 0.01F, 0F);

                // 「Rotate by」の処理
                TransformQueryRotation.Value = math.mul(
                    TransformQueryRotation.Value,
                    quaternion.AxisAngle(math.normalize(new float3(0F, 1F, 0F)), graphData.addAngle));
            }

            );
        }
    }
}
```

</div></details>





## ECS Graphの拡張について

**グラフのノードは自分で拡張することが出来ます。**

拡張方法は簡単であり、`Graph Tools Foundation`と言うパッケージ内の`NodeAttributes.cs`に定義されている**[Node]属性を使うだけです。**

## サンプル

サンプルとして以下の拡張ノードを定義してみました。

<details><summary>**`CustomNode.cs`(クリックで展開)**</summary><div>

```csharp:CustomNode.cs
using Unity.Mathematics;
using UnityEngine.VisualScripting;

namespace VisualScripting.CustomNode
{
    // [Node]を付けたクラスのstatic methodがノードとして表示される
    [Node]
    public static class CustomNode
    {
        // 受け取った値を加算して返す
        public static int Sample(int x, int y, int z) => x + y + z;


        // こうすればテーブルの参照も出来たり
        // ※現状のDOTS VSでは配列が定義できないっぽいので..?
        static readonly int[] SampleTable = new int[] {0, 1, 2, 3};
        public static int GetSampleTable(int index) => SampleTable[index];


        // 行列演算用のノードも作成出来る
        // ※現状のDOTS VSでは行列に対する直接な操作は出来ないっぽいのが、拡張ノード経由で対応可能
        public static float4x4 MatrixSample(float3 pos, quaternion rot)
        {
            return new float4x4(rot, pos);
        }
    }
}
```

</div></details>

拡張したノードはグラフビュー上で右クリック → `Create Node`を開くと、**[Node]属性を付けたクラス名が項目に追加されているのでそちらから呼び出すことが出来ます。**

![Art003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/8a231cd4-9d34-f06f-2e1f-e7676468b192.png)

#### 参考

- [DOTS Visual Scripting 4th experimental drop #10](https://forum.unity.com/threads/dots-visual-scripting-4th-experimental-drop.749273/#post-5015483)
- [DOTS Visual Scripting 5th experimental drop #52](https://forum.unity.com/threads/dots-visual-scripting-5th-experimental-drop.772520/page-2#post-5215028)






## 他にサポートされている機能

他にも以下の機能などがサポートされているみたいです。


### Coroutine

面白い機能としては、ECSベースのCoroutineが実装されてました。

一例を挙げると以下のように`On Update Entities`にて`Wait`を呼び出すと、**「待機中に呼び出す処理」「完了時に呼び出す処理」を指定することが出来ます。**

以下の例では待機中には"Waiting"と言うログを、完了時には"Finish"と言うログを出力します。

![coroutine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/82ad0845-802c-6b85-1f09-dd5a93890324.png)

ただ、現状のCoroutine自体は`ComponentSystem.OnUpdate`の中にて自前のステート管理を自動生成して制御しているっぽく、完了時にもう一度Coroutineが回り始めるので考えて制御する必要はありそうです。

参考までに上記グラフの出力コードを引用します。

<details><summary>**`SampleSystem.cs`(クリックで展開)**</summary><div>

```csharp:SampleSystem.cs
using Microsoft.CSharp;
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

public class SampleSystem : ComponentSystem
{
    private Unity.Entities.EntityQuery TransformQueryExcludeCoroutine;
    private Unity.Entities.EntityQuery TransformQuery;
    private Unity.Entities.EntityQuery HogeQuery;
    public struct TransformQueryCoroutine : Unity.Entities.ISystemStateComponentData
    {
        public int State;
        public VisualScripting.Entities.Runtime.Wait Wait;
    }

    private static bool UpdateTransformQueryCoroutine(
        float deltaTime,
        ref TransformQueryCoroutine transformQueryCoroutine)
    {
        switch (transformQueryCoroutine.State)
        {
            case 0:
                {
                    transformQueryCoroutine.Wait.Time = 3F;
                    transformQueryCoroutine.State = 1;
                    return SampleSystem.UpdateTransformQueryCoroutine(deltaTime, ref transformQueryCoroutine);
                }

            case 1:
                {
                    if (transformQueryCoroutine.Wait.MoveNext(deltaTime))
                    {
                        Debug.Log("Waiting");
                        return true;
                    }

                    transformQueryCoroutine.State = 2;
                    return SampleSystem.UpdateTransformQueryCoroutine(deltaTime, ref transformQueryCoroutine);
                }

            case 2:
                {
                    Debug.Log("Finish");
                    return false;
                }
        }

        return false;
    }

    public struct GraphData : Unity.Entities.IComponentData
    {
        public float addAngle;
    }

    protected override void OnCreate()
    {
        TransformQueryExcludeCoroutine = GetEntityQuery(
            ComponentType.Exclude<TransformQueryCoroutine>(),
            ComponentType.ReadOnly<Unity.Transforms.Translation>(),
            ComponentType.ReadOnly<Unity.Transforms.Rotation>());
        TransformQuery = GetEntityQuery(
            ComponentType.ReadWrite<TransformQueryCoroutine>(),
            ComponentType.ReadOnly<Unity.Transforms.Translation>(),
            ComponentType.ReadOnly<Unity.Transforms.Rotation>());
        HogeQuery = GetEntityQuery(ComponentType.ReadOnly<Unity.Transforms.Translation>());
        EntityManager.CreateEntity(typeof(GraphData));
        SetSingleton(new GraphData { addAngle = 0.3F });
    }

    protected override void OnUpdate()
    {
        EntityManager.AddComponent<TransformQueryCoroutine>(TransformQueryExcludeCoroutine);
        {
            Entities.With(TransformQuery).ForEach((
                Unity.Entities.Entity TransformQueryEntity,
                ref TransformQueryCoroutine transformQueryCoroutine) =>
            {
                {
                    if (!SampleSystem.UpdateTransformQueryCoroutine(Time.deltaTime, ref transformQueryCoroutine))
                    {
                        transformQueryCoroutine.State = 0;
                    }
                }
            }

            );
        }
    }
}
```

</div></details>


### フレームトレース機能

画面構成の章で解説した<font color="Red">**【⑩: Toggle Tracing for Current Instance】**</font>ボタンを押下すると、ボタン群が並んでいるエリアの下にフレーム番号が記載されたエリアが表示されます。

**この状態でグラフを実行すると「フレーム毎に処理をトレース」する事ができ、一時停止/実行停止中に以下のように操作してトレース結果を追うことが出来ます。**

![wait.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/295e0d96-4926-22e3-8f02-5fe255282983.gif)


### マクロ

グラフ内で計算式などを組むと、**計算式のノード構成**を使いまわしたくなるときが出てくるかと思います。**この要件はマクロを使うことで対応可能です。**

これを使うことで複雑な計算式と言った「使い回せる処理」に関しては、グラフ内で関数化するような感じで定義を纏めることが出来ます。

#### やり方

サンプルとして「引数から受け取った4つの値を加算して返す」処理をマクロ化してみます。

先ずは予め計算式と組まれている一連のノード郡を選択し、右クリックメニューから「`Refactor` → `Extract Macro`」を選択します。

![Art006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/e1a9ca9b-f6c5-9dbb-d944-17b7c1877622.png)

そうすると以下のようなマクロノードとマクロ用の`ECS Graph`のアセットが生成されます。
今回は名前を`SampleMacro`と設定しました。

次にマクロノードをダブルクリックして開きます。

![Art004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/9f3193e0-9674-b688-88eb-0a6f24bf10b1.png)

この中ではマクロの実装を定義できます。
以下の様に引数/戻り値を設定して計算式を組み上げます。

![hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/f7c00905-e8cd-81b4-b23a-10a8f21a7fec.png)

計算式を組み上げたら前に開いていたグラフに戻ります。
すると**マクロノードが引数/戻り値に応じて入出力が変わっているので、**後はこちらを使うだけです。

サンプルでは「1+2+3+4」の結果をログに出力してます。(シンプルすぎた感..)

![Art003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/bca128b0-ad51-66ac-24b0-24d03d6f54ee.png)




### キー/ボタン入力の取得

グラフ上から直接キー/ボタン入力を取得することが出来ます。

**入力を実際に組み込むにあたっては、入力側の抽象化を考慮して「カスタムノードで抽象化して取るか？」「ECS側で入力を取らずにEntityとして入力を渡すか？」など色々と設計を考えられるかと思いますが、簡易的に確認する場合には有用かもしれません。**


![Art004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80207/f5562dc2-2746-60d6-b1db-ebc7e38140d3.png)


### その他

幾つかの例を挙げましたが、他にも色々とあります。
記事中では全て解説しないので、他の機能についてはフォーラムにある各バージョンのfeatureを御覧ください。

[関連リンク → Forum](将来的な変更によっては今回の記載内容と相違する点が出てくるかもしれません。**)




## 現時点ではサポートされていない機能?

主に出来ることについて纏めていきましたが、触っている感じだと出来ないことも幾つか有るように思われました。
ひょっとしたら見落としているだけかもしれませんが...調査した範囲で見えてきたことを纏めます。

- **配列が使えない？**
    - 一応Forumにて[こういったやり取りがありましたが](https://forum.unity.com/threads/dots-visual-scripting-4th-experimental-drop.749273/#post-5074448)..詳細は不明 + 追いきれておらず...
- **名前空間の設定**
    - 現状だと全てグローバル名前空間に置かれる
        - まだ未検証だが...C#コードの出力先は決まっているので、上手く`ADF`(Assembly Definition Files)を設定することが出来たら多少マシになるかも
- **ComponentDataにポインタを持たせられない?**
    - ComponentDataを生成するツール上からは見受けられなかった
        - ※ポインタ自体はBlittable型なので持たせること自体は可能
- **グラフ上から行列にアクセスできない**
    - 「行列の型(float4x4など)」自体はあるが、要素に対するアクセスなどが無い？

他にもあるかもしれませんが、自分の方で触った際には上記の項目が気になりました。

とは言えど、例えば行列については将来的に対応されることを期待して今はカスタムノードで対応したり、ポインタについては直接触らない方向で別手段を検討(例えばラップするとか)していくなどすれば代替出来るかもしれません。

何れにせよ今後の更新で改善される可能性があるので、引き続きForumなど追っていければと思います。









# 触ってみた所感について

触った際の所感について記載していきます。
**ここからは自分の考えが主なので、考えの一環程度に捉えて頂けると幸いです。**


## ECSの考え方は覚える必要がありそう

出来る事自体がECSそのままなので、予めECSの知見がある方にはとっつきやすい仕組みかと思いました。
**逆に言うとECSと言うアーキテクチャである以上、どんな方でも触るにあたっては「ECSの考え方」自体は覚える必要があるように思われました。**

ここで言う「考え方」とは**「EntityQueryで処理するEntityをフィルタリング → フィルタリングしたEntityに対し処理を適用」と言ったロジックを組むにあたって必要となる考え方**であり、根底にあるメモリ管理/キャッシュ効率と言ったゴリラ感のある話題を覚える必要があるという訳ではありません。
(かと言って最終的にどこまでの知識が必要となってくるかは見えていない所...)


## ビジュアルスクリプティングとの相性は良さそう

私がECSを学び始め、少し理解できてきたタイミングで「これって入出力がハッキリしているから特殊な組み方とか許容しなければ、ビジュアルスクリプティングと相性良さそうだよな」と思う時が有りましたが、**今回の`DOTS VS`はまさにそのイメージに近い物が来たかなー感が有りました。**

※特殊な組み方・・(書いておいて特殊かどうかわかりませんが...)例えばECS側からグローバルなシングルトンを覗きに行ったり、MonoBehaviourの参照を引っ張ってきて状態に依存させたりなど。


ただし、組み込むにあたっては上記の「ECSの考え方」自体は必要となりそうなので、ここらがどれくらい受け入れられうのかは少し気になるところです。



## 最終的にはどこまで対応されるのか？

現時点ではECSのコード生成という段階ではありますが、「`Unity2020`辺りから追加されていくDOTS周りの機能」や「[DOTS寄りになっていくTinyUnity](https://forum.unity.com/threads/oct-2019-update-on-project-tiny-roadmap.754850/)」と言った他の機能を踏まえると、まだまだ出来る事自体は増えていきそうな感じはします。

初期のフォーラムでも関連する話題は上がっており、以下のようなコメントも投稿されていたりします。
[DOTS Visual Scripting first experimental drop #20](https://forum.unity.com/threads/dots-visual-scripting-first-experimental-drop.677476/#post-4539301)










# 最後に

所々に記載してますが、**`DOTS VS`自体はまだまだ開発中のステータスです。**
将来的な変更によっては今回の記載内容と相違する点が出てくる可能性も十分にあります。

それを踏まえつつ現時点のイメージ把握程度に留めておくのが良いかもしれません。


## 今回話せなかったテーマ

今回の記事では未検証項目などもあって書けなかったテーマがあります。

## `DOTS VS`を実際に組み込むにあたっての設計について

今回は「機能と所感の話」が主であり、**「実際にこれをどう組み込んでいくのか？」**についてまでは話しきれませんでした。
将来的なバージョンアップを踏まえつつ、改めて時間が取れれば検証してみたいタイトルではあります。

後は関連事項として、Uniteの「[Unity開発ロードマップ最新情報](https://learning.unity3d.jp/3354/)」にて**「GDCのタイミングで`DOTS VS`をガッツリ使ったゲームサンプルを公開予定？」**である旨が話されていたので、そちらの方も実装の一例として気になるところではあります。

※ちなみに...コードベースにはなりますが、以前↓の記事を書いたので参考までに。
[【Unity】DOTS(ECS)を導入したゲームを作る際に設計してみた話](https://qiita.com/mao_/items/a5548f31de479c6831e4)










# 関連リンク

## チュートリアル動画

全5回に渡って「[Unity公式のSpace Shooter tutorial](https://learn.unity.com/project/space-shooter-tutorial)」をベースにDOTS Visual Scriptingで実装を行うチュートリアル動画。

- [Chapter 1: Create your first Entity and Component in ECS](https://www.youtube.com/watch?v=bptCy1ZDQ2g)
- [Chapter 2: Create ComponentSystem](https://www.youtube.com/watch?v=9EuGd2WmDvU)
- [Chapter 3： Instantiate Entity and Create MacroNode in Unity ECS](https://www.youtube.com/watch?v=ZbS2mHGdogM)
- [Chapter 4: Boundary Detection and Destroy Entity in Unity ECS with Visual Scripting Tool](https://www.youtube.com/watch?v=vNOX8ZMEauM)
- [Chapter 5: Enemy Generation and Collision Detection](https://www.youtube.com/watch?v=7C7luPCViW8)

## Forum

- [DOTS Visual Scripting 6th experimental drop](https://forum.unity.com/threads/dots-visual-scripting-6th-experimental-drop.795807/)
- [DOTS Visual Scripting 5th experimental drop](https://forum.unity.com/threads/dots-visual-scripting-5th-experimental-drop.772520/)
- [DOTS Visual Scripting 4th experimental drop](https://forum.unity.com/threads/dots-visual-scripting-4th-experimental-drop.749273/)
- [DOTS Visual Scripting 3rd experimental drop](https://forum.unity.com/threads/dots-visual-scripting-3rd-experimental-drop.727190/)
- [DOTS Visual Scripting 2nd experimental drop](https://forum.unity.com/threads/dots-visual-scripting-2nd-experimental-drop.696500/)
- [DOTS Visual Scripting first experimental drop](https://forum.unity.com/threads/dots-visual-scripting-first-experimental-drop.677476/)

## DOTS基礎

- [たのしいDOTS 〜初級から上級まで〜](https://learning.unity3d.jp/3252/)
- [大量のオブジェクトを含む広いステージでも大丈夫、そうDOTSならね](https://learning.unity3d.jp/3266/)


## Other

- [DOTS Visual Scriptingのお試しバージョンを触ってみた](https://www.slideshare.net/YuukiOgino/dots-visual-scripting)
- [【Unity】DOTS(ECS)を導入したゲームを作る際に設計してみた話](https://qiita.com/mao_/items/a5548f31de479c6831e4)
    - ※`DOTS VS`から少しズレますが...ECSをゲームに組み込む上での参考程度に