Unity Bolt Tips2.Boltの独自ノードをC#で作成する
前回の続きです BoltのノードはUnityのC#のメソッドが利用できるのですが、自分で関数ノードを作成して再利用する事もできます。
Boltのノード作成の基礎的なメモ
MacroとEmbed
BoltのフローのソースにはMacroかEmbedを選択して利用できます。
それぞれの違いは以下の通り
項目 | Embed | Macro |
---|---|---|
関係性 | グラフはFlow Machine Component 自体に埋め込まれます | グラフはMacro AssetとしてProjectに保存され、Component から参照されます。 |
再利用性 | グラフを他のFlow Machineから再利用することはできません。 ただし、Prefab間で共有することはできます。 Component自体が削除されると、グラフも一緒に削除されます。 |
同じMacroを複数のFlowMachine間で共有することができます。 あるGameObjectのFlowMachineを削除した場合でも、Macro AssetはProjectに残ります。 |
永続性 | FlowMachineの設定を、EmbedからMacroに変更するとグラフ自体が削除されます。 | MacroからEmbedに切り替えてもグラフ自体はEmbedとして残ります。 |
Sceneの参照 | Prefabとして保存されていない場合は、Embed内のグラフはScene内のGameObjectを参照することができます。 | Macroの状態ではどのSceneにも属していないため、シーン内のGameObjectを参照することはできません。 |
Prefab | Editor上でPrefabをInstanciateした場合動作しないことがある。 | Editor上でもすべてのPrefab内で利用することができます。 |
Macroとして再利用性の高いノードをライブラリとして作成することで、効率的な開発を行うことができます。
Macro利用時のPrefabからのGameObject量産時の変数汚染
ただし、PrefabのGameObjectのFlowMachineとしてMacroを利用した場合 記述内容にも寄るかもしれませんが、 Prefabからシーンに生成されたGameObject間で変数の汚染が発生する事があります。
その場合は、PrefabのMacroをConvertボタンを押してEmbedに変換してやると、解決する場合があります。
詳細調査中なので、間違ってるかもしれません。
SuperUnit
俗にいう関数は、SuperUnitとして定義できます。
右クリック-> Add NodeでSuperUnitを作成
ダブルクリックで編集できます
SuperUnitの入り口と出口はInput/Outputを利用します。 InputノードにControl Inputs(Outputs)キーを入れる必要があります。
MacroをドラッグアンドドロップすることでSuperUnitとして利用することができる
SuperUnitの公式のマニュアルはこちらにあります。
C#で独自Boltノードを作成する
- C#でコードを書く
public class HogeTest { public void TakeDamage(int damage) { Debug.Log("Damage"); } }
SetupWizardでAssemblyを設定しておくと、ノードとして呼び出すことができます。
日本語のノードも作成可能です。
public class GraphTest : MonoBehaviour { public int 日本語ノード(int 体力,int 気力) { return 0; } }
- このスレッドのように Unitクラスを継承してBolt特有のプロパティーをきちんと設定することで、より細かい設定も可能です。
using Bolt; using Ludiq; [UnitTitle("InOutUnit")] [UnitCategory("Littlewing/Test")] public class InOutUnit : Unit { [DoNotSerialize] public ControlInput input { get; private set; } [DoNotSerialize] public ControlOutput output { get; private set; } [DoNotSerialize] public ValueInput valueIn { get; private set; } [DoNotSerialize] public ValueOutput valueOut { get; private set; } protected override void Definition() { input = ControlInput("in", Enter); output = ControlOutput("output"); valueIn = ValueInput<float>("valueIn"); valueOut = ValueOutput<float>("valueOut", ReturnFloat); Requirement(valueIn, valueOut); Succession(input, output); } public ControlOutput Enter(Flow flow) { return output; } public float ReturnFloat(Flow flow) { return flow.GetValue<float>(valueIn); } }
Outputを複数使い分ける 分岐/非同期
ControlOutput を条件に合わせて使い分けたり、Coroutine などを利用して非同期でOutputを行いたい場合は flowのreferenceを取得して、任意のタイミングでRunを実行することで出力タイミングや、出力先を変更することができます。
また、以下の例では
[NullMeansSelf]
を利用して、TransformにデフォルトでSelfを設定- float
value
にデフォルト値0.5を設定 [PortLabelHidden]
を利用して、入力Pininput
の名前を隠すtransform
とvalue
は 表示名称に変数名をそのまま利用Succession()
を利用して,Relationを明示[TypeIcon]
でアイコンの設定
などを行っています。
using Bolt; using Ludiq; using UnityEngine; [UnitOrder(0)] [UnitSurtitle("Sur Title")] [UnitSubtitle("Sub Title")] [UnitTitle("InOutUnit2")] [UnitShortTitle("Short Title")] [TypeIcon(typeof(Transform))] [UnitCategory("Littlewing/Test")] public class InOutUnit2 : Unit { [DoNotSerialize] [PortLabelHidden] public ControlInput input { get; private set; } [PortLabel("output1")] [DoNotSerialize] public ControlOutput output1 { get; private set; } [PortLabel("output2")] [DoNotSerialize] public ControlOutput output2 { get; private set; } [DoNotSerialize] [NullMeansSelf] public ValueInput transform { private set; get; } [DoNotSerialize] public ValueInput value { private set; get; } private Flow flow; private GraphReference reference; private Transform m_transform; private float m_value; private ControlOutput out1 => output1; private ControlOutput out2 => output2; protected override void Definition() { input = ControlInput(nameof(input), Enter); output1 = ControlOutput(nameof(output1)); output2 = ControlOutput(nameof(output2)); transform = ValueInput<Transform>(nameof(transform), null); value = ValueInput<float>(nameof(value), 0.5f); Succession(input, output1); Succession(input, output2); } public ControlOutput Enter(Flow flow) { this.flow = flow; reference = flow.stack.ToReference(); m_transform = flow.GetValue<Transform>(transform); m_value = flow.GetValue<float>(value); bool b = true; if (b) Out1(); else Out2(); return null; } private void Out1() { Flow.New(reference).Run(out1); // この呼び方もできる //flow.Invoke(out1); } private void Out2() { Flow.New(reference).Run(out2); // この呼び方もできる //flow.Invoke(out2); } }