UnityのVisual Scripting(旧Bolt)でScript Machineのカスタムユニットを作成する方法をまとめました。
- はじめに
- ログ出力を行うユニットを作成する
- 加算するユニットを作る
- 1秒後にログ出力する、コルーチン用のユニットを作る
- ユニットのヘッダで値を設定できるフィールドを表示する
- ユニットのアイコンを設定する
- 関連
- 参考
Unity2021.1.11f1
Visual Scripting 1.6.1
はじめに
Visual ScriptingはUnityでビジュアルスクリプティングを可能にするための公式パッケージです。
元々Boltという名前のサードパーティライブラリでしたが、Unityが買収して公式化・無料化・改名されました。
この辺りの話は以下の記事にまとめていますので、必要に応じて参照してください。
インストールはPackage Managerを用いて行います。
Package Managerの使い方は以下の記事にまとめていますので、必要に応じて参照してください。
本記事ではVisual ScriptingのScript Machine(旧Flow Machine)においてカスタムユニットを作成する方法についてまとめます。
ログ出力を行うユニットを作成する
早速カスタムユニットを作成していきます。
まずはログ出力だけを行うユニットを作成してみます。
using Unity.VisualScripting; using UnityEngine; [UnitTitle("Example01")] // ユニットの検索に使われる名前 [UnitShortTitle("Example01")] // ユニットの表示に使われる名前 [UnitSurtitle("surtitle")] // ユニットの表示名の上に表示される [UnitSubtitle("subtitle")] // ユニットの表示名の下に表示される [UnitCategory("Test")] // カテゴリ [UnitOrder(1)] // カテゴリ内での表示順 public class Example01Unit : Unit { [DoNotSerialize] [PortLabelHidden] public ControlInput Input { get; private set; } [DoNotSerialize] [PortLabelHidden] public ControlOutput Output { get; private set; } [DoNotSerialize] public ValueInput Value { get; private set; } protected override void Definition() { Input = ControlInput(nameof(Input), OnEnter); Output = ControlOutput(nameof(Output)); Value = ValueInput<string>(nameof(Value)); // string型の入力値ポートを作成する // 入力と出力のリレーションを設定する Requirement(Value, Input); // InputがトリガーされたときにValueが取得される Succession(Input, Output); // OutputがトリガーされたときにInputがトリガーされる } private ControlOutput OnEnter(Flow flow) { // 処理をしてControlOutputを返す var value = flow.GetValue<string>(Value); Debug.Log(value); return Output; } }
ここまではほぼボイラープレートで、説明はコメントに書いた通りです。
Definition()
で行っている入出力のリレーション構築に関しては、デバッグの用途で必要になります。
例えば上記の例では、ValueとInputとのリレーションが構築されているため、Valueポートに何も接続されていなかったら以下のように警告が表示されます。
もしリレーションを設定していなかった場合、警告は表示されずランタイムエラーとなります。
リレーションについては詳しくはマニュアルの以下のページを参照してください。
このように新しいユニットを作ったらProject Settings > Visual Scripting > Regenerate Units
を実行します。
これでログ出力だけを行うユニットを作成できました。
加算するユニットを作る
次に値を加算するユニットを作ります。
前節のユニットと違い後続のユニットに値を受け渡すユニットとなります。
using Unity.VisualScripting; [UnitTitle("Example02")] [UnitShortTitle("Example02")] [UnitCategory("Test")] public class Example02Unit : Unit { [DoNotSerialize] public ValueInput Value1 { get; private set; } [DoNotSerialize] public ValueInput Value2 { get; private set; } [DoNotSerialize] public ValueOutput Result { get; private set; } protected override void Definition() { // ポートを作成 Value1 = ValueInput<float>(nameof(Value1)); Value2 = ValueInput(nameof(Value2), 0.0f); // デフォルト値を指定することも可能 // 計算を行うメソッドを第二引数に渡して出力ポートを作成 Result = ValueOutput(nameof(Result), Add).Predictable(); // PredictableをつけるとEdit ModeでAddを実行してエラーが起きないかチェックする // リレーションを作成 Requirement(Value1, Result); Requirement(Value2, Result); } private float Add(Flow flow) { var value1 = flow.GetValue<float>(Value1); var value2 = flow.GetValue<float>(Value1); return value1 + value2; } }
大体の説明はコメントに書いた通りです。
またRegenerate Unitsを行うことでこのユニットが使えるようになります。
1秒後にログ出力する、コルーチン用のユニットを作る
さてUnityのVisual Scriptingではコルーチンに対応したユニットを作成できます。
そこで次に1秒後にログ出力する、コルーチン用のユニットを作成します。
using System.Collections; using Unity.VisualScripting; using UnityEngine; [UnitTitle("Example03")] [UnitShortTitle("Example03")] [UnitCategory("Test")] public class Example03Unit : Unit { [DoNotSerialize] [PortLabelHidden] public ControlInput Input { get; private set; } [DoNotSerialize] [PortLabelHidden] public ControlOutput Output { get; private set; } [DoNotSerialize] public ValueInput Value { get; private set; } protected override void Definition() { // ControlInputではなくControlInputCoroutineを使う Input = ControlInputCoroutine(nameof(Input), OnEnterRoutine); Output = ControlOutput(nameof(Output)); Value = ValueInput<string>(nameof(Value)); Requirement(Value, Input); Succession(Input, Output); } private IEnumerator OnEnterRoutine(Flow flow) { // 1秒後にログ出力する yield return new WaitForSeconds(1.0f); var value = flow.GetValue<string>(Value); Debug.Log(value); // Outputを返すこと yield return Output; } }
説明はコメントの通りです。
ControlInputの代わりにControlInputCoroutineを使っているのがポイントです。
コルーチンのチェックボックスにチェックを入れたうえでこのユニットを使用すると動作が確認できます。
ユニットのヘッダで値を設定できるフィールドを表示する
さて次にユニットのヘッダ部分から値を設定できるようにしてみます。
ヘッダ部分に入力フィールドを表示するには以下のようにUnitHeaderInspectableアトリビュートを付けたプロパティを使います。
[Inspectable, UnitHeaderInspectable("Header Example")] public string HeaderExample { get; private set; }
すると以下のようにユニットのヘッダから値を設定できるようになります。
ユニットのアイコンを設定する
次にカスタムユニットにアイコンを設定してみます。
まず他のユニットと同じアイコンを設定するには、カスタムユニットクラスに以下のアトリビュートを付けます。
[TypeIcon(typeof(Timer))] // 指定した型のユニットと同じアイコンが使われる
完全に独自のテクスチャを使いたい場合には以下のようにUnitDescriptorを定義することもできます。
using Unity.VisualScripting; using UnityEditor; using UnityEngine; [Descriptor(typeof(Example01Unit))] public class Example01UnitDescriptor : UnitDescriptor<Example01Unit> { private EditorTexture _icon; public Example01UnitDescriptor(Example01Unit unit) : base(unit) { var iconTex = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Example/tex_star.png"); _icon = EditorTexture.Single(iconTex); } protected override EditorTexture DefinedIcon() { return _icon; } }