見出し画像

【Unity】xNode入門②|Node同士で値を受け渡す|Buttonで足し算・引き算を切り替える

前回は、xNodeをUnityに導入し、以下を確認しました。

  • Graphを作る

  • Nodeを作る

  • InputとOutputを追加する

  • Node同士をConnectionでつなぐ

  • Graph内のNodeをC#から読み取る


ただし、前回の段階では、Node同士を線でつないでも、値が自動的に次のNodeへ渡るわけではない、というところで終わっています。

今回はその続きとして、Connectionを使ってNode同士で値を受け渡す方法を確認していきます。
さら、Buttonを使って、足し算をするか、引き算をするかを切り替える簡単なSampleを作ってみます。



1.前回のおさらい

前回は、次のようなGraphを作りました。

NumberNodeには、以下の Output を追加、

[Output]
public int output;

DisplayNodeには、以下の Input を追加しました。

[Input]
public int input;

これによって、Output Port と Input Port を Connection でつなぐことができました。

しかし、この線をつないだだけでは、

NumberNodeの10
     ↓
DisplayNodeへ自動的に10が入る

という動作にはなりません。
そこで今回は、実際に接続先から値を取得する仕組みを作っていきます。


2.xNodeで値を受け渡す

2-1.値の受け渡しフロー

xNodeでは、Output側が勝手に値を送り続けるというより、Input側が値を必要としたときに、接続先のOutputへ値を取りに行くと考えると分かりやすいです。

DisplayNode
「inputの値が欲しい」
      │
      ▼
Connectionを確認
      │
      ▼
NumberNode
「Valueを返します」
      │
      ▼
     10

この仕組みを作るときに、主に使うのが、

  • GetValue()

  • NodePort

  • GetInputValue<T>()

です。順番に確認していきます。


2-2.NumberNodeから値を返す

まず、前回作成した NumberNode.cs を修正します。
現在は、次のような形になっています。

作成済みのコードに、GetValue() を追加します。

using XNode;

public class NumberNode : Node
{
    public int value = 10;  //記述済み

    [Output]
    public int output;     //記述済み

    public override object GetValue(NodePort port)
    {
        return value;
    }
}

■ GetValueとは?

今回追加したのが、以下の関数です。

public override object GetValue(NodePort port)
{
    return value;
}

GetValue() は、そのNodeのOutputから値を取得するときに呼ばれるメソッドです。
今回のNumberNodeでは、

return value;

としているため、NumberNode の value が返されます。
たとえば、Value = 10 に設定してあれば、

Outputから値を取得 → 10を返す

という動作になります。
これでNumberNodeは「接続先から要求されたときに値を返せるNode」になりました。


■ NodePortとは?

GetValue() の引数を見ると、 NodePort port が指定されています。
NodePort は、Node についているPortの情報を表します。つまり、

public override object GetValue(NodePort port)

は、「どのPortから値を求められたのか」を確認できるようになっています。
今回の NumberNode は Output が1つしかないので、

return value;

だけで問題ありません。ただし、将来的に、

  • Output A

  • Output B

  • Output C

のように複数のOutputを持つNodeを作る場合は、どのPortから呼ばれたのかを確認して、返す値を切り替えることもできます。

今回は「NodePortはPortの情報を扱うもの」くらいに覚えておけば大丈夫です。


2-3.DisplayNodeで接続先の値を取得する

次に、前回作成した DisplayNode.cs を修正します。

using XNode;

public class DisplayNode : Node
{
    [Input]
    public int input;  //記述済み

    public int GetInputNumber()
    {
        return GetInputValue<int>("input", input);
    }
}

以下の関数を追加しました。

 public int GetInputNumber()
 {
    return GetInputValue<int>("input", input);
 }

■ GetInputValue とは?

GetInputValue<型名>()  を使うと、Input Portに接続されているNodeから値を取得 することができます。

今回の場合、以下のように記述しています。

GetInputValue<int>("input", input)

<int> は「int型の値を取得する」いう意味です。

最初の "input" は、値を取得するInput Portの名前です。
つまり、

[Input]
public int input;

の input を指定しています。

2つ目の、input は、Input Portに何も接続されていない場合に使う値です。
そのため、

  • Connectionあり → 接続先のOutputから値を取得

  • Connectionなし → DisplayNode自身のinput値を使う

という動きになります。


2-4.前回のGraphTesterで確認

前回作った GraphTester.cs を利用して、DisplayNodeが受け取った値をConsoleに表示してみます。

GraphTesterを次のように変更します。

using UnityEngine;

public class GraphTester : MonoBehaviour
{
    public SampleGraph graph;   //Graphのインスペクターから割り当てる

    void Start()
    {
        foreach (var node in graph.nodes)
        {
            if (node is DisplayNode displayNode)  //nodeがDisplayNode型の場合
            {
                Debug.Log(
                    "DisplayNodeが受け取った値:" +
                    displayNode.GetInputNumber()
                );
            }
        }
    }
}

NumberNodeの Valueを 10 に設定し、
NumberNode 〇⇔〇 DisplayNode、と接続した状態で

Play を押して、Consoleに以下のように表示されれば成功です。

これで初めて、

NumberNode Value = 10
    Connection
 DisplayNode Input = 10

という値の受け渡しができました。


2-5.Connectionを外すと

ここで、NumberNode と DisplayNode の Connection を外してみましょう。

さらに、DisplayNodeのInputに、5を設定します。

その状態でもう一度Playすると、以下のように表示されます。

これは、

GetInputValue<int>("input", input)

の2つ目の input が使われているためです。つまり、

  • 接続されている → 接続先の値を使う

  • 接続されていない → 自分に設定されている値を使う

ということです。


3.サンプル:計算システム作成

値の受け渡しが分かったところで、少し実践的なNodeを作ってみます。
今回は、2つの値を受け取って足し算する、AddNode を作ります。

3-1.足し算を行う AddNode の作成

新しくスクリプトファイル:AddNode.cs を作成し、次のように記述します。

using XNode;

public class AddNode : Node
{
    [Input] public int a;   //int型の入力ポート a
    [Input] public int b;   //int型の入力ポート b

    [Output] public int result;   //int型の出力ポート result

    public override object GetValue(NodePort port)  //ノードの実行
    {
        int valueA = GetInputValue<int>("a", a);   //入力ポート a の値を取得
        int valueB = GetInputValue<int>("b", b);   //入力ポート b の値を取得

        return valueA + valueB;   //a と b の値を足して返す
    }
}

コードの解説

AddNodeには、

[Input] public int a;

[Input] public int b;

という2つのInputがあります。

[Output] public int result;

で、計算結果を出力するPortを作っています。

GetValue() の中では以下のコードで、aとbに接続されているNodeから値を取得しています。

int valueA = GetInputValue<int>("a", a);
int valueB = GetInputValue<int>("b", b);

最後に、

return valueA + valueB;

として、足し算の結果を返しています。


3-2.引き算を行う SubtractNode の作成

次に、引き算を行うNodeも作ります。
新しく、スクリプトファイル:SubtractNode.cs を作成します。

using XNode;

public class SubtractNode : Node
{
    [Input] public int a;   //int型の入力ポート a
    [Input] public int b;   //int型の入力ポート b

    [Output] public int result;   //int型の出力ポート result

    public override object GetValue(NodePort port)  //ノードの実行
    {
        int valueA = GetInputValue<int>("a", a);   //入力ポート a の値を取得
        int valueB = GetInputValue<int>("b", b);   //入力ポート b の値を取得

        return valueA - valueB;   //a と b の値を引いて返す
    }
}

基本的な構造はAddNodeとほぼ同じです。
違うのは最後の return の部分だけです。

return valueA - valueB;

これで、

  • 足し算を行う AddNode

  • 引き算を行う SubtractNode

ができました。


3-3.Graphを組み立てる

今回は、足し算と引き算を行うSampleを作るため、新しいGraphを用意します。
新に
 CalculationGraph.cs を作成し、次のように記述します。

using UnityEngine;
using XNode;

[CreateAssetMenu]
public class CalculationGraph : NodeGraph
{
}

Projectウィンドウで右クリックし、Create → Calculation Graph からGraphアセットを作成します。

今回は、この CalculationGraph の中に、

  • NumberNode × 2

  • AddNode

  • SubtractNode

を配置します。

今回はNumberNodeを2つ用意します。
それぞれの value の値をたとえば、以下のように指定します。

  • NumberNode A Value = 10

  • NumberNode B Value = 3

さらに、AddNode と SubtractNode に対して、それぞれ次のように接続します。

  • NumberNode A の Output を、AddNode の A と SubtractNode の A に接続

  • NumberNode B の Output を、AddNode の B と SubtractNode の B に接続

イメージとしては、次のような形です。

これで同じ2つのNumberNodeを使って、

10 + 3

10 - 3

の両方を計算できるGraphになります。


3-4.Buttonを用意する

次はUnity側にButtonを作ります。
Canvasの中にButtonを2つ作成します。

1つ目のテキストを「たし算」、2つ目のテキストを「ひき算」としておきます。

この2つの Button から、xNodeの AddNode と SubtractNode を呼び分けます。


3-5.ButtonからxNodeを利用する

今回は新しく、スクリプトファイル:CalculationController.cs を作ります。

public class CalculationController : MonoBehaviour
{
    public CalculationGraph graph;  //CalculationGraph型の変数 graph

    public void Add()
    {
        foreach (var node in graph.nodes)   //graph.nodes の中身を順番に取り出す
        {
            if (node is AddNode addNode) //node が AddNode 型の場合
            {
                //addNode の result ポートの値を取得
                int result = (int)addNode.GetValue(addNode.GetOutputPort("result")); 
                Debug.Log("たし算の結果:" + result);
            }
        }
    }

    public void Subtract()
    {
        foreach (var node in graph.nodes)
        {
            if (node is SubtractNode subtractNode)
            { 
               //subtractNode の result ポートの値を取得
               int result = (int)subtractNode.GetValue(subtractNode.GetOutputPort("result"));
               Debug.Log("ひき算の結果:" + result);
            }
        }
    }
}

このスクリプトでは、

public void Add()

と、

public void Subtract()

という2つのメソッドを作っています。


Addでは、

if (node is AddNode addNode)

によってGraph内のAddNodeを探しています。そして、

addNode.GetOutputPort("result")

で、AddNodeの result Portを取得しています。
そのPortを、

addNode.GetValue(...)

へ渡すことで、AddNodeの計算結果を取得しています。

今回は、10 + 3 なので、13 が返ります。


Subtractも同じ考え方です。
Graph内からSubtractNodeを探し、

subtractNode.GetValue(...)

によって計算結果を取得します。

今回は、10 - 3 なので、7 が返ります。


3-6.ButtonのOn Clickに登録する

CalculationController.cs を、空のGameObjectなどにアタッチします。

サンプルでは、スクリプトファイルをそのままヒエラルキー上に配置して、オブジェクト化しています。
※最近のアップデートでこの機能が使えるようになったようです。

InspectorのGraph欄には、今回使用している CalculationGraph を登録します。

AddButtonを選択し、Buttonコンポーネントに以下を指定

  • On Click():CalculationControllerを登録

  • 関数:CalculationController → Add()

SubButtonを選択し、Buttonコンポーネントに以下を指定

  • On Click():CalculationControllerを登録

  • 関数:CalculationController → Subtract()

これで準備完了です。


3-7.実行してみる

UnityをPlayします。

たし算 のButtonをクリックします。Consoleに、

たし算の結果:13

と表示されれば成功です。

同様にひき算 のButtonをクリックします。Consoleに、

ひき算の結果:7

と表示されます。

これで、2つの処理を、Buttonから切り替えられるようになりました。


3-8.フローの確認

ここで今回の処理の流れを確認します。
今回のSampleでは、

  • たし算Button → Add()

  • ひき算Button → Subtract()

 という形で、どの処理を使うかはButton側で分岐しています。

一方、xNode側では、下の図のように

値や計算の関係 をGraphとして持っています。つまり、

  • Unity UI → どの処理を実行するか決める

  • xNode → その処理に必要な値やNodeの関係を管理する

という役割分担になっています。

この形を見ると、第1回で説明した「xNodeはNodeを置けば自動的にゲームが動くツールではない」という意味も分かりやすくなります。


もう1つ重要なのが、値を取得する順番です。

見た目では、

10 → AddNode
3  → AddNode

と値が流れているように見えます。
しかし、実際のイメージは少し違います。

たとえばAddButtonを押した場合、次のような流れとなります。

つまり、必要になったタイミングでConnectionをたどり、接続先の値を取得しているということです。

この考え方は、xNodeを理解するうえで非常に重要です。


18.今回のまとめ

今回は、xNodeでNode同士の値を受け渡す方法を確認しました。

ポイントは次の通りです。



次回は、今回のような単純な計算処理から一歩進めて、

上の図のような、ゲームで使えるNodeの流れや分岐について確認していきます。



▼ Unity初心者向け記事まとめはこちら


こちらのマガジン「ミニゲーム作成講座 全8回セット」でもシーンの切り替えなどを活用して、初心者でも知識ゼロから作れるミニゲーム作成講座を紹介しています。

さらにステップアップの「スマホで遊ぶジャンプゲーム 8回セット」のマガジンもぜひご活用ください。


ファイブボックスでは、スクラッチやUnityの個別指導のオンラインレッスンを行っています。
ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。

いいなと思ったら応援しよう!

ファイブボックス よろしければ応援お願いします! いただいたチップはクリエイターとしての活動費に使わせていただきます!