今回はUnityのVisual Scriptingを使ってゲームのロジックを作成していきたいと思います。
C#スクリプトを記述せずに、グラフを使って視覚的にわかりやすくスクリプトを作ることが出来ます。
デザイナーとプログラマーが連携しやすくなっているんですかね?
ボッチのわたくしには関係のないことですねぇ・・・・(´Д`)
Visual Scriptingを使ってみる
Visual Scriptingの機能はUnity2021.1以降は標準でパッケージとしてインストールされています。
それ以前のUnityのバージョンを使っている場合はUnityメニューのWindowからPackage Managerを選択し、そこからインストールしてください。
また、Visual Scriptingという名前でない場合は、UnityのAsset StoreでBOLTという名前で存在します。
BOLTは2018.4.15以降のバージョンで使用可能です。
バージョン管理
プロジェクトでパブリックリポジトリ(Git等)を使用している場合はVisual Scriptのファイルを除外する必要があります。
除外しないとVisual Scriptを違法頒布したとされます。(^_^;)
マシンとグラフ
Visual ScriptingではゲームオブジェクトにScript MachineやState Machineコンポーネントを取り付け、Graphにスクリプトグラフやステートグラフを設定して使用します。
例えばヒエラルキーにCubeを作成し、インスペクタのAdd ComponentからVisual Scripting→Script MachineやState Machineを選択して取り付けます。
Graphに設定するスクリプトグラフやステートグラフは上の各コンポーネントのNewを押して指定するフォルダに新しいグラフを作成することが出来ます。
もしくはAssetsフォルダ内で右クリックからCreate→Visual Scripting→Script GraphもしくはState Graphを選択することでも該当するグラフを作成することが出来ます。
こちらの場合は作成したグラフをScript GraphのGraph等に設定する必要があります。
例えばCubeゲームオブジェクト用にCubeという名前のScript Graphを作成した場合は以下のようにCubeのScript MachineのGraphにドラッグ&ドロップするか、Graphの○のアイコンを押して選択して設定します。
Script MachineやState MachineコンポーネントのSourceをGraphからEmbedに変更するとマシン自体にグラフが埋め込まれるようになります。グラフの再利用が出来なくなります。
EmbedとGraphの違いについてはUnityマニュアルを参照してみてください。
Visual Scriptingの基本構造
UnityメニューのWindowからVisual Scripting→Visual Scripting Graphを選択すると、ビジュアルスクリプトを作成するウインドウが開きます。
Assetsフォルダ内に作成したScript GraphやState Graphのファイルをダブルクリックするとグラフウインドウにグラフが表示されます。
ここではCubeゲームオブジェクトのScript MachineにCubeScriptというスクリプトグラフを設定し、State GraphにCubeStateというステートグラフを設定したと仮定して話を進めます。
Assetsフォルダ内に作成したCubeScriptをダブルクリックするか、CubeゲームオブジェクトのScript MachineのGraphに設定したCubeScriptをダブルクリックしてScript GraphウインドウにCubeScriptグラフを開きます。
ヒエラルキーのCubeゲームオブジェクトを選択します。
CubeScriptを開くとScript Graphウインドウでは以下のような感じで表示されます。
Graph InspectorのTitleにはグラフのタイトル、Summaryにはグラフが何をするものなのかの概要を入力します。
Trigger Inputsはこのグラフが実行されるトリガーとなる入力名
Trigger Outputsはこのグラフの最後のトリガーとなる出力名
Data Inputsはグラフの実行のトリガーがされた場合に入力される値
Data Outputsはグラフの最後に出力する値
となっています。
Input系のものはInputユニット、Output系のものはOutputユニットを使用した時に表示されます。
これらはひとまとまりの処理を行うスーパーユニット(ひと塊の処理を行うメソッドのようなもの)の入力と出力のポートの名前とデータで使います。
変数
Blackboardでは変数の設定が出来ます。
変数にはいくつかの種類があります。
グラフ変数
グラフ変数はそのグラフだけで使用する変数で、C#スクリプトで言うとアクセス修飾子がprivateなフィールドと似ています。
オブジェクト変数
オブジェクト変数はグラフが設定されたオブジェクトだけで使用する変数で、C#スクリプトで言うとアクセス修飾子がpublicなフィールドと似ています。
シーン変数
シーン変数はそのシーン全体で使用する変数です。シーン全体で変数を使いたい時に使用します。
アプリケーション変数
アプリケーション変数はこのアプリケーション全体で使用する変数です。アプリケーション(ゲーム)全体で変数を使いたい時に使用します。
セーブ変数
セーブ変数はアプリケーションが終了した後も値が保持される変数です。
Initialでは初期値、Savedでは保存されている値が表示されます。
値の保存にはPlayerPrefsを使って保存しているようです。
変数は再生モード中に変更した値は元に戻りますが、インライン(グラフ中のみで使用している値)の値は再生モードを解除しても値は変更されたままになります。
変数はドラッグ&ドロップしてグラフ上に配置出来ます。
ドラッグ&ドロップして配置した場合はGet Variableユニットになります。
ユニット
ユニットは処理の単位で、何らかの処理を実行する単位です。
グラフの何もない所で右クリックする事であいまい検索(ファジー検索)画面を開いて、ユニットを選択したり、検索窓に対象を絞った検索をしてユニットを選択することが出来ます。
その他ユニットのポートからドラッグして、接続できるユニットから選択することも出来ます。
ポート
ポートはユニットに付いている接続部分で、処理の流れを作るフローポートと値を渡すデータポートがあります。
上の例ではStartユニット(スタート時に1回実行される)からDebug Logユニットにフローが接続され、Stringに入力されている「テスト文字列」というデータがMessageに接続されています。
接続元のポートをドラッグし、接続先の該当するポートにドロップするとポートが接続されます。
Unityの実行ボタンを押すとコンソールに「テスト文字列」と表示されます。
確認したら作成したユニットは使わないので全部削除します。
グラフでの操作
ユニットの作成方法と接続方法がわかったところでグラフでのショートカットや操作方法について少し見ていきます。
グラフの最大化
Visual Scriptingのグラフウインドウはグラフの何もない所でダブルクリック、もしくはShiftキーを押しながらSpaceキーを押すことでウインドウを最大化する事が出来ます。
同じ動作をする事で元に戻せます。
ユニットの操作
ユニットはマウスの左ボタンで選択する事が出来ます。
ユニットを選択した状態でマウスの左ボタンを押してドラッグするとユニットを移動出来ます。
ユニットを選択した状態でCtrl+Dキーで複製、Deleteキーで削除、Ctrl+Xで切り取り、Ctrl+Vキーで貼り付け、Ctrl+Aキーで全ユニットの選択が出来ます。
ユニットを選択した状態で右クリックでメニューを表示して同様の処理が出来ます。
グラフの何もない所でマウスの左ボタンを押してドラッグすると四角い枠が出て、複数のユニットを選択することが出来ます。
グループの作成
Ctrlキーを押しながらマウスの左ボタンを押してドラッグするとグループを作成することが出来ます。
グループは処理の説明をする為のコメントの役割をします。
グループを選択した状態でグラフのGraph Inspectorでグループの色を変更することが出来ます。
グループ名はグラフのグループ名が表示されている部分をクリックして変更するか、Graph Inspectorのタイトル部分を押して変更することが出来ます。
スクリプトグラフ
スクリプトグラフはC#スクリプトで作っていたような機能(例えばプレイヤー操作スクリプト)を作る物です。
C#スクリプトの場合はStartメソッド(登場した時1回)やUpdateメソッド(毎フレーム)を使って、何らかの処理を実行していましたが、スクリプトグラフでも同じようにStartユニットやUpdateユニットを使って同じように流れを作って何らかの機能を作成するということが出来ます。
上の場合はグラフの何もない所で右クリックし、検索窓でStartで検索しStartユニットを配置し、同じようにして検索窓でUpdateユニットを配置したものです。
Startは最初に1回、Updateは毎フレーム呼ばれるイベントなのでそこからフローを繋げばC#スクリプトを作成した時にデフォルトで作成されているStartメソッドやUpdateメソッドと同じような処理を作ることが出来ます。
状態グラフ
状態グラフは特定の状態にある時にゲームオブジェクトがどのように動作するかの状態と流れを作る物です。
CubeStateグラフを開きます。
グラフの何もない所で右クリックをするとCreate Script Stateで状態を作成出来ます。
Create Script Stateで状態を作成すると以下のように表示されます。
On Enter State(この状態になった時)、On Exit State(この状態から離脱した時)、Update(この状態の時に毎フレーム)というイベントを含んだStartという状態が作られました。
Startという状態名は仮の名前なのでGraph InspectorのStartの部分を押して名前を変更することが出来ます。
Start状態をダブルクリックすると以下のようにOn Enter State、On Exit State、Updateというイベントのユニットがあり、そこからフローを繋げて処理を作ることが出来ます。
必要のないイベントがあればユニットを選択し、削除します。
例えばUpdateユニットが必要なければ選択してDeleteキーを押します。
Start状態からCubeStateを選択し、元の状態を表示しているグラフに戻ります。
Start状態は初期状態です。
初期状態にしたい状態のユニットを選択し、右クリックからToggle Startを選択し、ユニットの上部が緑色になると、その状態が初期状態になります。
Start状態から他の状態に遷移し、状態によって処理を分けたい事があります。
例えばキャラクターが健康な状態とダメージを負っている状態で処理を分けたい時等です。
そんな時はStart状態ユニットを選択し、右クリックからMake Transitionを選択し、矢印が出たらマウスの左ボタンを押して決定します。
上のように新しい状態が作成されました。
状態が変更されるには条件があります。
その条件が状態と状態の間にある黄色い部分の所で、ここをダブルクリックします。
Trigger Transitionというユニットがありますが、これにフローを繋がるとこの遷移が行われます。
状態グラフはどの状態にある時にこういう処理をしたい!という時に使いますが、状態グラフの状態の中身はスクリプトグラフなので、今の状態に合わせて処理を実行させたい時は便利です。
無理に状態グラフを使わずともスクリプトグラフ内だけで自分で状態を管理して同じようなことが出来るので、無理に使う必要はないのかも?
スーパーユニットの作成
スーパーユニットは特定の処理を行うユニットを一まとめにしたもので、入力と出力を設定出来るので、C#スクリプトのメソッドのようなものです。
スーパーユニットは通常のScript GraphをAssetsフォルダに作成し、InputとOutputユニットを追加して作る(再利用が可能)か、スクリプトグラフウインドウ内で何もない所で右クリックし、検索窓にsuperと入力してSuper Unitを選択して作ります。
(注)他のユニットのポートをドラッグした場合はSuper Unitが出てきません。
Assetsフォルダに作ったScript GraphはVisual Scriptingグラフ内にドラッグ&ドロップして使う事が出来るので便利です。
Super Unitユニットをダブルクリックすると以下のようにInputとOutputというユニットがあります(ない場合は自分で追加します)。
デフォルトではInputとOutputにはフローポートもデータポートもありません。
InputユニットやOutputユニットを選択し、Graph Inspectorでフローポートとデータポートの設定をする必要があります。
試しにInputユニットを選択し、Graph InspectorのTrigger Inputsの+を押し、KeyにinputFlowと入力します。
これがフローポートの名前になります。
次にData Inputsの+を押し、KeyにinputValueと入力し、TypeにIntegerを入力します。
これがデータポートになります。
同じようにOutputユニットにも設定します。
このスーパーユニットでは受け取ったint型の数値に5を足して、それを呼び出し元に返すという処理にしたいと思います。
そこでAddユニット(Scalarのやつ)を追加し、InputからOutputにフローポートを繋ぎます。
またInput→Add→Outputにそれぞれデータポートを繋ぎます。
スーパーユニットの中身は以下のようになりました。
スクリプトグラフでこのスーパーユニットを使った流れを作ってみます。
CubeScriptスクリプトグラフのStartユニットから今作成したSuper Unitにフローポートを繋ぎInput ValueにはInteger Literalユニットを繋ぎ、Integer Literalには3を入力します。
さらにSuper UnitからDebug Logユニットにフローを繋ぎResult ValueのデータをDebug LogのMessageに繋ぎます。
Unityを実行するとコンソールに8が表示されます。
CubeScriptスクリプトグラフの中身はもう使わないので削除しておきます。
作成したSuper Unitも使わないので削除します。
追加のユニットタイプやアセンブリを使用する
Visual Scriptingで使用する型のタイプはIntやFloat等はデフォルトで定義されています。
Package Managerを使ってVisual Effectパッケージをインストールし、Visual Scriptingで使用したいと思ったとします。
ですがVisual Effectの型のタイプは設定されていないので、ユニットとして登場しません。
こういった場合はUnityのメニューのEditからProject Settingsを選択し、Visual Scriptingタブを選択し、Visual Effectのタイプを追加する必要があります。
ただし、タイプの追加の前にその定義されているアセンブリを追加する必要があるので、まずはそちらを行います。
最初にVisual Effectの定義が含まれるUnityEngine.VFXを設定します。
Node Libraryを押し、スクロールして下の+を押し、UnityEngine.VFXModuleを設定します。
次に、Type Optionsの下の方へ移動し、+を押して新しい枠を追加し、Visual Effect等のタイプを追加します。
これで設定が出来ましたが、最後にNode Library項目の下にあるRegenerate Unitsボタンを押します。
このボタンを押すとVisual Effect関連のユニットが使えるようになります。
自前のユニットを更新した時等にコンソールにRegenerate Unitsボタンを押してくださいというエラーが出る事があるので、その時はまた押してください。
自前のユニットを作成する
C#スクリプトを使って自前のユニットを作成することが出来ます。
詳細についてはUnityマニュアルをご覧ください。
新しいユニットの定義
新しいユニットを作成します。
1 2 3 4 5 6 7 8 9 10 | using Unity.VisualScripting; public class MyNode : Unit { protected override void Definition() { } } |
usingディレクティブでUnity.VisualScriptingを指定します。
Unitクラスを継承して新しいクラスを作成します。
UnitクラスはDefinitionメソッドをオーバーライドする必要があるのでoverrideキーワードを使ってメソッドのオーバーライドをします。
ここまで出来たらUnityメニューのEditからProject Settingsを選択し、Visual ScriptingタブのRegenerate Unitsボタンを押して反映します。
これでスクリプトグラフ内で右クリックするとMyNodeユニットが一番下に項目として現れます。
入力トリガーポートと出力トリガーポートの作成
このままだとユニットとしては使えないので、入力トリガーポート、出力トリガーポートを作成します。
1 2 3 4 5 6 | [DoNotSerialize] public ControlInput inputTrigger; [DoNotSerialize] public ControlOutput outputTrigger; |
ControlInputが入力トリガーポート、ControlOutputが出力トリガーポートになります。
次にDefinitionメソッド内でインスタンスの生成をします。
1 2 3 4 5 6 7 8 | protected override void Definition() { inputTrigger = ControlInput("inputTrigger", flow => { return outputTrigger; }); outputTrigger = ControlOutput("outputTrigger"); } |
入力トリガーポートにはInputTriggerという名前を表示し、フローが出力トリガーポートに流れます。
出力トリガーポートにはOutputTriggerと表示されます。
入力データポートと出力データポートの作成
次に入力データポートと出力データポートを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | [DoNotSerialize] public ValueInput inputValue1; [DoNotSerialize] public ValueInput inputValue2; [DoNotSerialize] public ValueOutput output; [DoNotSerialize] public int resultValue; protected override void Definition() { inputTrigger = ControlInput("inputTrigger", flow => { resultValue = flow.GetValue<int>(inputValue1) + flow.GetValue<int>(inputValue2); return outputTrigger; }); outputTrigger = ControlOutput("outputTrigger"); inputValue1 = ValueInput<int>("inputValue1", 1); inputValue2 = ValueInput<int>("inputValue2"); output = ValueOutput<int>("resultValue", flow => { return resultValue; }); } |
ValueInput型は入力データポート、ValueOutput型は出力データポートになります。
inputTriggerのインスタンス生成時の処理の実行でflow.GetValueを使って入力データの二つを足してresultValueに値を入れるという処理をさせます。
outputには計算した結果のresultValueが返されます。
リレーションの追加
機能としては出来ましたが、スクリプトグラフのRelationsを押した時に既存のユニットの場合は関係性の表示がされます。
スクリプトグラフのRelationsを押していない時は以下のようになりますが、
押した場合は以下のような関係性を表す表示がされます。
MyNodeユニットでも同じように関係性を表示するようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | protected override void Definition() { inputTrigger = ControlInput("inputTrigger", flow => { resultValue = flow.GetValue<int>(inputValue1) + flow.GetValue<int>(inputValue2); return outputTrigger; }); outputTrigger = ControlOutput("outputTrigger"); inputValue1 = ValueInput<int>("inputValue1", 1); inputValue2 = ValueInput<int>("inputValue2"); output = ValueOutput<int>("resultValue", flow => { return resultValue; }); Requirement(inputValue1, inputTrigger); Requirement(inputValue2, inputTrigger); Succession(inputTrigger, outputTrigger); Assignment(inputTrigger, output); } |
Requirementで入力トリガーポートで必要なデータを設定します。
Successionで入力トリガーポートから出力トリガーポートまでのパスを設定します。
Assignmentで入力トリガーがされた時にデータ出力までのパスを表示します。
これでMyNodeユニットが出来たので、簡単なサンプルを作成します。
ゲームオブジェクトが登場したら1回だけMyNodeユニットを実行します。
MyNodeユニットでは入力データポートが二つあり、1と5を入力データとします。
MyNodeユニットで実行するのは二つの入力データを足してResult Valueに出力するので、その結果をDebug Logユニットを使ってコンソールに表示します。
Unityを実行するとコンソールに6が表示されます。
CubeScriptスクリプトグラフ内のユニットはもう使わないので全削除します。
既存のスクリプトの実行
既に作成済みのC#スクリプトのメソッドを実行することも出来ます。
例えば以下のように静的なクラスと静的なメソッドを定義します。
1 2 3 4 5 6 7 8 9 10 11 12 | using System.Collections; using System.Collections.Generic; using UnityEngine; public static class MyScript { public static int MyAddMethod(int intValue1, int intValue2) { return intValue1 + intValue2; } } |
このMyScriptクラスのMyAddMethodは二つのint型の入力を受け取り、足した数値を返すメソッドです。
スクリプトを保存したらUnityメニューのEditからProject SettingsのVisual ScriptingタブのType Optionsに今作成したMyScriptを追加し、Regenerate Unitsボタンを押します。
終わったらスクリプトグラフ内の何もない所で右クリックし、検索窓にMyScriptを入力し、MyAddMethodメソッドを選択します。
ユニットが表示されるので以下のようにCubeScriptスクリプトに作成するとコンソールに20が表示されます。
ここまでは静的クラスの静的メソッドをユニットとして使っているのでインスタンス化せずに実行出来ました。
MonoBehavioourクラスを継承したクラスをゲームオブジェクトに取り付けてそれを実行することも出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyScript : MonoBehaviour { public int MyAddMethod(int intValue1, int intValue2) { return intValue1 + intValue2; } } |
スクリプトが出来たらCubeゲームオブジェクトに取り付けます。
CubeScriptスクリプトグラフでMyScriptのMyAddMethodを選択し、適当にユニットを繋げてUnityを実行します。
MyScriptはCubeゲームオブジェクトに取り付けたので、参照ではSelfとし自身のゲームオブジェクトに取り付けられたMyScriptを取得しています。
Script Graphを使ったサンプル
スクリプトグラフとステートグラフの概要が分かったところで、最後に変数とScript Graphを使って簡単なサンプルを作成してみます。
サンプルの概要としてはCubeゲームオブジェクトがX軸の正の方向に一定のスピードで移動し、その先にあるGoalAreaと接触したらコンソールにゴールと表示します。
サンプルで使用するゲームオブジェクトの作成と設定
既にCubeゲームオブジェクトにScript Machineコンポーネントを取り付け、GraphにCubeScriptスクリプトグラフを取り付けているので、これらを使います。
Cubeゲームオブジェクトのインスペクタは以下のようになっています。
Transformをデフォルト値にし、Cubeは衝突せず検知だけするのでBox ColliderのIs Triggerにチェックを入れます。
また検知する際にRigidbodyが必要になるので、Add ComponentボタンからRigidbodyコンポーネントを取り付け、重力は使わないのでUse Gravityのチェックを外します。
Script MachineのGraphにはCubeScriptスクリプトグラフが設定されていることとします。
VariablesはVisual Scriptingでオブジェクト変数を使うと自動で設定されるコンポーネントで、ヒエラルキーにはシーン変数用のScene Variablesというゲームオブジェクトも自動で作られます。
次にヒエラルキーに新しくCubeを作成し、名前をGoalAreaとします。
GoalAreaのインスペクタは以下のようにします。
新しくGoalAreaというタグを作成し、GoalAreaゲームオブジェクトに設定します。
TransformのPositionのXを5にします。
GoalAreaは衝突させないのでBox ColliderのIs Triggerにチェックを入れます。
今回のサンプルではCube自身でGoalAreaに侵入したかどうかを判定するのでGoalAreaゲームオブジェクトはGoalAreaタグを設定し、コライダが衝突しないようにするだけです。
CubeScriptグラフの中身を作成する
CubeScriptスクリプトグラフの中身を作成していきます。
ここまででCubeScriptには色々なユニットを配置していましたが、全て削除しておきます。
スクリプトグラフ内でCtrl+Aキーを押すと全ユニットを選択出来るので、その状態でDeleteキーを押すと全削除出来ます。
変数の作成
Cubeは一定のスピードで移動させるのでオブジェクト変数としてspeedを定義し、インスペクタで数値を設定出来るようにします。
ヒエラルキーでCubeゲームオブジェクトを選択したら、CubeScriptスクリプトグラフのObjectを押し、名前にspeedと入力し、+を押します。
TypeにはFloatを選択し、Valueには2を設定します。
ヒエラルキーのCubeゲームオブジェクトを選択すると、インスペクタのVariablesにSpeedが表示され値を変更出来るのが確認出来ます。
オブジェクト変数はこのオブジェクトが持つ変数で、C#スクリプトのpublicフィールドと同じと考えることが出来ます。
次に、CubeゲームオブジェクトはGoalAreaに接触したら移動をやめるので、接触したかどうかを判定する変数を作ります。
CubeScriptでGraphを押し、名前にgoalと入力し+を押したらTypeをBooleanにします。
goalはグラフ変数なのでこのグラフのみで使用する変数になりC#スクリプトのprivateなフィールドと同じです。
CubeScriptにユニットを作成する
変数が出来たので次はユニットを作成し、Cubeゲームオブジェクトの動きを作成していきます。
Cubeゲームオブジェクトは毎フレームX軸の正の方向に移動させる必要があるので、Updateユニットを作成し、毎フレーム実行されるようにします。
Cubeが移動するのはグラフ変数のgoalがfalseの時(ゴールしていない時)だけなのでGraphのgoalをグラフにドラッグ&ドロップして配置します。
Updateユニットからifユニットを作成して繋げ、グラフ変数のgoalがfalseの時にTransformのTranslateユニットにフローを繋ぎます。
Translateはゲームオブジェクトを移動させるユニットです。
CubeはX軸の正の方向に移動させるのでTranslateのXに移動値を入れるようにします。
Cubeの移動スピードはオブジェクト変数のspeedで設定していますが、実行する環境によってFPSが変わってくるためGet Delta Timeユニットを掛けて1フレームの移動値を合わせます。
その値をTranslateのXに接続します。
これでCubeは一定のスピードでX軸の正の方向に移動するようになりました。
次にCubeがGoalAreaに接触したかどうかを判定する必要があります。
CubeゲームオブジェクトがGoalAreaと接触したかどうかは、Cubeゲームオブジェクトが持つコライダがGoalAreaのコライダと接触したかどうかを判定することで判断できます。
そこで、CubeScriptのUpdateユニットの流れを作成した下あたりにOn Trigger Enterユニットを作成し、Selfを指定して自身が持つコライダが他のコライダと接触した時に実行されるようにします。
Compare Tagユニットを使って接触した相手方のコライダのタグがGoalAreaであるかどうかを判断し、ifユニットでTrueだった時にSet Variableユニットを使ってグラフ変数のgoalをtrueにします。
ゴールした時はDebug Logユニットを使って「ゴール」という文字列をコンソールに表示します。
終わりに
Visual Scriptingを使用するとC#スクリプトを自分で作成する必要がなく視覚的に確認しながら処理の流れやデータの確認が出来ますね。
ただグラフの処理が多くなるとそれだけユニットも膨大になってC#スクリプトで処理を作るよりも流れが分かり辛くなる可能性も否めませんね。
しかし、機能は単機能にする。という考えを実装していけば膨大なユニットを持つグラフにはならないかもしれません。
Visual Scriptingではあいまい検索でユニットを項目事に選べるので、どんな機能があるのか?またどうやって使うのか?がわかりやすいかもしれません。
実行時にデータや流れも確認出来るのもいいですね。(^_^)v
今回はCubeゲームオブジェクトをGoalAreaまで移動させるというサンプルを作成しましたが、次回はキャラクターの移動機能をVisual Scriptingを使用して作成してみたいと思います。