BehaviorTreeノードの使い方

前回までの作業がまだ済んでいない場合は、基本的な使い方から前回までのチュートリアルを先に済ませてください。

今回からチュートリアルで使用するLogic Toolkitのバージョンを1.10.0に更新しました。
更新がまだお済みでない場合は先に更新してから続きをお読みください。

今回はBehavior Treeノードを使用してみましょう
内容は以下の通りです。

  • Logic Behaviorコンポーネントの付いたGameObjectを作成する。
  • Logic Editorウィンドウでグラフを開いて編集する。
  • Behavior Tree ExecuteでBehaviorTreeノードを実行。
  • Sequenceで子のBehaviorTreeノードを順番に実行。
  • Selectorで子のBehaviorTreeノードを選択的に実行。
  • DecoratorでBehaviorTreeノードを実行するか判定。

シーンを新規作成

他のシーンを開いていると混乱の元となりますので、シーンを新規作成しましょう

  • メニューからFile > New Sceneを選択
  • New SceneウィンドウでBasic (URP)を選択
  • Createボタンをクリック

Logic Behavior付きGameObject作成

シーンにLogic Behaviorコンポーネントが追加された状態のGameObjectを作成します。

  • Hierarchyの+ボタンをクリック
  • メニューからLogic Toolkit > Logic Behaviorを選択
  • 名前はそのままEnterキーで確定。

Logic Editorウィンドウで開く

Logic BehaviorコンポーネントのグラフなどはLogic Editorウィンドウで編集します。
ウィンドウが表示されていない場合は、GameObjectを選択してInspectorの「Edit」ボタンをクリックすることで表示されます。

  • Logic Behaviorオブジェクトを選択
  • InspectorウィンドウのLogic BehaviorコンポーネントのEditボタンをクリック

すでにLogic Editorウィンドウが表示されている場合は、HierarchyウィンドウでのGameObject選択と連動してグラフが切り替わるようになります。

Behavior Tree ExecuteのTaskノードを作成

Behavior Treeノードを実行するには、BehaviorTreeノードを実行するためのCompositeComponentという種類のコンポーネントを使用します。
ここでは、単一のBehaviorTreeノードを実行するBehavior Tree Executeを使用してみましょう。

  • Startノードの実行出力ポートをドラッグ
  • 右側付近でドロップし、ノード作成メニューを開く
  • Scriptsタブを選択
  • 一覧からTasks > Behavior Trees > Composites > Behavior Tree Executeを選択
  • ノード名はそのままEnterキーで確定

CompositeComponentについて

TaskComponentの一種で、子のBehavior Treeノードを実行するためのコンポーネントです。
ひとまず「Behavior Treeを実行するために使用するもの」と覚えてください。

今回はTaskノードに使用しましたが、他のTaskComponentと同様にStateノードなどでも使用できます。

SequenceのBehavior Treeノードを作成

SequenceというCompositeComponentを使用するBehavior Treeノードを作成します。

  • Behavior Tree ExecuteのExecuteポートをドラッグ
  • 右側付近でドロップし、ノード作成メニューを開く
  • Scriptsタブを選択
  • 一覧からBehavior Trees > Composites > Sequenceを選択
  • ノード名はそのままEnterキーで確定

Behavior TreeノードとTaskComponentについて

作成したBehavior TreeノードとTaskComponentについて説明します。

Behavior Treeノードは、行動木と言われる挙動制御を行うためのノードです。
入力ポートの接続先(以降、便宜的に親ノードと呼びます。今回はBehavior Tree Execute)からノードが実行され、実行が完了したら結果を親ノードに返します。
「どのような処理を行うか」や「実行した結果の成否」は使用するTaskComponentによって定義されます。

今回はSequenceを使用し、Childrenポートから接続したBehavior Treeをさらに実行します。
詳細は以下のSequenceについてに記載します。

Decoratorsは、ノードを実行するかどうかなどの判定を行うコンポーネントを追加できます
(後で使用します)

Servicesはノードが実行中に追加で処理を行うコンポーネントを追加できます。
(今回は使用しません)

行動木(ビヘイビアツリー)について

状況を判断しどのような行動を行うかを決定するための木構造を行動木(Behavior Tree: ビヘイビアツリー)と呼びます。
ゲーム分野では主にNPCの挙動を組むのに活用されています。

例えば、以下のような敵NPCの挙動が考えれらます。

  • Selector
    • Selector 【条件: 自身のHPが30%以下の場合】
      • 逃げる 【条件: プレイヤーが近くにいる場合】
      • 座る(自然回復する)
    • Sequence 【条件: プレイヤーが視界内に入った】
      • 近づく
      • 攻撃する
    • 巡回する

このように木構造で定義し状況判断によって行動を決定します。

SequenceとSelectorについての詳細は後述します。

ビヘイビアツリーワイヤーについて

Behavior Tree ExecuteのExecuteポートとBehavior Treeノードのビヘイビアツリー入力ポートビヘイビアツリーワイヤーによって接続されてします。
このビヘイビアツリーワイヤーは、親側のポートからBehavior Treeノードを実行するためのワイヤーです。

ビヘイビアツリーワイヤーは子ノード側が必ずBehavior Treeノードとなります。
子ノードがどのような順番で実行されるかは親側のCompositeComponentの種類によって変わります。

Sequenceについて

SequenceはChildrenポートの接続先ノード(以降、子ノードと呼びます)を上から順に実行します。
子ノードの実行結果によって次の子ノードを実行するかなどの挙動が変わります。

子ノードの実行結果挙動
成功次の子ノードを実行します。
次の子ノードがない場合は実行を終了し、親ノードに成功を返します。
失敗実行を終了し、親ノードに失敗を返します。

Sequenceは「いずれかの子ノードが失敗しない限り順番に実行していく」と覚えてください。

Wait For SecondsのBehavior Treeノードを作成

Behavior TreeノードにはCompositeComponent以外のコンポーネントも使用できます。
毎度おなじみWait For Secondsで数秒待つ動作をBehavior Treeノードで作成してみましょう。

  • SequenceのChildrenポートをドラッグ
  • 右側付近でドロップし、ノード作成メニューを開く
  • Scriptsタブを選択
  • 検索欄にWait For Secondsと入力
  • 一覧からWait For Seconds (Behavior Tree) を選択
  • ノード名はそのままEnterキーで確定

Wait For Secondsは完了時に成功を返すため、親ノードのSequenceはそのまま次のノードを実行します。
(次に実行されるノードはこの後作ります)

Wait For Secondsの設定

1秒待つように設定します。

  • Secondsフィールド1に設定

SelectorのBehavior Treeノードを作成

Wait For Secondsで指定秒数待機した後は、SequenceのChildrenポートの次の接続先が実行されます。
Sequenceの子ノードにSelectorというCompositeComponentを追加してみましょう。

  • SequenceのChildrenポートをドラッグ
  • Wait For Secondsのノード下付近でドロップし、ノード作成メニューを開く
  • Scriptsタブを選択
  • 一覧からBehavior Trees > Composites > Selectorを選択
  • ノード名はそのままEnterキーで確定

Selectorについて

SelectorはChildrenポートの接続先ノード(以降、子ノードと呼びます)を上から順に実行します。
子ノードの実行結果によって次の子ノードを実行するかなどの挙動が変わります。

子ノードの実行結果挙動
成功実行を終了し、親ノードに成功を返します。
失敗次の子ノードを実行します。
次の子ノードがない場合は実行を終了し、親ノードに失敗を返します。

Selectorは「いずれかの子ノードが成功しない限り順番に実行していく」と覚えてください。

Idle TaskのBehavior Treeノードを作成

Selectorの子になにもしないIdle Taskを使用するBehavior Treeノードを作成します。

  • SelectorのChildrenポートをドラッグ
  • 右側付近でドロップし、ノード作成メニューを開く
  • Scriptsタブを選択
  • 検索欄にIdle Taskと入力
  • 一覧からIdle Task (Behavior Tree)を選択
  • ノード名はそのままEnterキーで確定

このIdle Taskは何もしないため存在理由が分からないかもしれませんが、とくにBehavior Treeノードでは役立ちます。
ビヘイビアツリーの考えでは原則ノードの末端は「なにかしら動作を行う」のですが、場合によっては「【なにもしない】をしたい」ときもあります。

DecoratorsにCondition Evaluateを追加

Idle Taskのノードを実行するかどうかをDecoratorで判定してみましょう。
ここでは、Condition EvaluateをDecoratorとして使用します。

  • Idle Taskを使用しているBehavior TreeノードのDecoratorsの+ボタンをクリック
  • ノードコンポーネント追加メニューからEvaluators > Condition Evaluateを選択

Decoratorについて

DecoratorはBehavior Treeノードが実行されるかどうかの判定などを行うためのコンポーネントです。
実行判定のDecoratorの場合は、判定結果がtrueの場合は実行され、falseの場合は実行されずに親ノードに失敗を返します。

Abort Flags

DecoratorにはAbort Flagsという中断フラグがあります。
このフラグによって、実行途中の中断や割り込みなどが行えるようになります。

設定項目説明
Nothing中断しない
(実行開始時の判定は行われます)
Self自ノードが実行中に、判定結果がfalseになった場合は実行を中断して失敗を返します
Lower Priority自ノードよりも優先度が低いノード(自ノードよりも下にあるノード)が実行中に、判定結果がtrueになった場合は自ノードが割り込んで実行開始します
(親がSelectorの場合のみ使用できます)

フラグ形式となっているため、SelfとLower Priorityを組み合わせることも可能です。

もう一つIdle TaskのBehavior Treeノードを作成

作成したIdle TaskはCondition Evaluateによって実行するか判定が行われますが、Conditionがfalseであるため実行されずに失敗を親のSelectorに返します。
失敗が返されたSelectorは次の子ノードを実行します。

そこで次に実行される子ノードとして、もう一つIdle TaskのBehavior Treeノードを作成します。

  • SelectorのChildrenポートをドラッグ
  • 既存のIdle Taskのノード下付近でドロップし、ノード作成メニューを開く
  • Scriptsタブを選択
  • 検索欄にIdle Taskと入力
  • 一覧からIdle Task (Behavior Tree)を選択
  • ノード名はそのままEnterキーで確定

プレイして確認

ここまで作成するとグラフは以下のようになっています。

プレイボタンを押して挙動を確認します。

  • Wait For Secondsで1秒待機することを確認
  • 2個目のIdle Taskのノードが実行されていることを確認
  • プレイしたままCondition EvaluateのAbort FlagsとConditionを変更して動作を確認する
    • Abort FlagsにLower Priorityを設定する
    • Conditionをオンにして、1個目のIdle Taskのノードが実行されることを確認
    • Conditionをオフにして、引き続き1個目のIdle Taskのノードが実行されていることを確認
    • Abort FlagsにSelfを設定する(Everythingになる)と2個目のIdle Taskのノードが実行されることを確認

補足

Behavior Treeノード以外でもSequenceやSelectorが使える

SequenceやSelectorも大元はTaskComponentですので、Behavior Tree Executeと同様にBehavior Treeノード以外でも使用できます。
例えばStateノードでも直接SequenceやSelectorが使用できます。

TaskComponentの実行結果(成功/失敗)

TaskComponentには実行結果の成功や失敗を返す機能がもとから備わっています。
Behavior TreeノードでTaskComponentを使用すると、実行結果をBehavior Treeノードの結果として親ノードに返します。

例えば
「攻撃対象へ移動するタスク : 移動完了したら成功を返す。移動中に対象が死亡した場合は移動を中断して失敗を返す」
というTaskComponentスクリプトを作成したとします。
このスクリプトをBehavior Treeノードで使用すると結果がそのままBehavior Treeノードの実行結果になります。
実行結果とSelectorやSequenceを組み合わせると「成功したら攻撃する」、「失敗したら巡回に戻って索敵する」などの挙動が組めるようになります。

もちろんDecoratorで「対象が生存しているか判定する」などの組み方でも問題ありません。
さまざまな挙動に対してどちらが組みやすいか、いろいろ考えてみてください。

次回

次回は「Execute Taskによる実行フローの階層化」を行います。

試用版でお試しの方は、お気に召しましたらアセットストアにてご購入ください。