デコレータによる中断と割り込み


今回は、ビヘイビアツリーのデコレータによるノードの中断と割り込みを行う方法について説明します。

事前知識

このチュートリアルには以下の知識が必要です。

チュートリアルプロジェクトの準備

今回のチュートリアルを実践するプロジェクトを準備しましょう。

動作環境

このチュートリアルは、以下の環境で作成しております。

Unity 2019.4.40f1
Arbor 3.9.0

Arbor3の準備

まだ購入していない場合はアセットストアで購入する必要があります。
購入する場合は以下リンクからアセットの商品ページが開けます。

チュートリアル用プロジェクト作成

チュートリアル用にプロジェクトを作成します。

プロジェクト名 Arbor_BT_AbortFlags
テンプレート 3D
インポートするアセット Arbor 3: FSM & BT Graph Editor

プロジェクトの作成やArborのインポートについては「Arborを使用するための準備」を参照してください。

ビヘイビアツリーを使用する準備

ビヘイビアツリーを使用するために、BehaviourTreeコンポーネント付きのGameObjectを作成します。

BehaviourTreeコンポーネント付きGameObjectの作成

  • Hierarchyの+ボタンをクリック。
  • メニューから「Arbor > BehaviourTree」を選択。
GIF

BehaviourTreeコンポーネントをArborEditorで開く

BehaviourTreeコンポーネントのグラフの編集はArbor Editorウィンドウで行います。

  • Hierarchyから作成したBehaviourTreeオブジェクトを選択。
  • InspectorウィンドウのBehaviourTreeコンポーネントにある「Open Editor」ボタンをクリック。
GIF

実行中のノードを中断する

ノードを中断できると柔軟に行動するNPCが作れるようになります。

例えば、NPCがアイテムを拾うために移動中に別のキャラクターが先に拾ってしまった場合、移動完了を待たずにほかの行動に切り替わってほしいときはよくあります。
このような中断の条件判定はデコレータで行います。

それではノードの中断を行うビヘイビアツリーを組んでみましょう。

Selectorコンポジットノードの作成

Rootの子ノードにSelectorコンポジットノードを作成します。

  • Rootノード下部のスロット(以下、子スロットと呼ぶ)をドラッグ。
  • Rootノードより下のほうでドロップ。
  • メニューから「コンポジット作成」を選択。
  • コンポジット選択ウィンドウから「Selector」を選択。
  • 名前はそのままEnterキーで確定。
GIF

Idleアクションノードの作成

Selectorの子ノードにIdleアクションノードを作成します。

Idleは、何も行わないアクションです。
デコレータなどで中断されない限り実行され実行され続けます。
詳しくはArborドキュメント「Idle」を参照してください。

  • Selectorノードの子スロットをドラッグ。
  • Selectorノードより下のほうでドロップ。
  • メニューから「アクション作成」を選択。
  • アクション選択ウィンドウから「Idle」を選択。
  • 名前を「Idle1」に変更してEnterキーで確定。
GIF

Idle1ノードを複製

Selectorの子ノードにもう一つIdleアクションを追加します。

  • Idle1ノードの設定アイコンをクリック。
  • メニューから「複製」を選択。
  • 複製したIdle1ノード(右側のノード)の名前部分をダブルクリック。
  • 名前を「Idle2」に変更してEnterキーで確定。
GIF

CalculatorCheckデコレータの追加

Idle1ノードにCalculatorCheckデコレータを追加します。

  • Idle1ノードのヘッダ部とアクションのタイトルバーとの隙間中央あたりをマウスでポイント。
  • 表示された「デコレータ挿入」ボタンをクリック。
  • メニューから「デコレータ追加」を選択。
  • デコレータ選択ウィンドウから「CalculatorCheck」を選択。
GIF

AbortFlagsについて

CalculatorCheckを追加すると、デコレータ欄の上部に「Nothing」というドロップダウンが表示されています。

これがデコレータの判定結果によりノードの中断や割り込みを行うための設定です。

この設定をArborではAbortFlags(中断フラグ)と呼びます。
設定できるフラグの種類は以下の通りです。

  • Self
    自ノードもしくはその配下のノードが実行中に条件判定を行う。
    条件判定の結果がtrueである間は実行を継続し、falseになったら中断し親ノードへ失敗を返す。
  • LowerPriority
    自ノードよりも低優先度のノードが実行中に条件判定を行う。
    条件判定の結果がtrueである場合は実行中ノードを中断し自ノードを実行開始する。

AbortFlagsはビットフラグ形式となっており、SelfとLowerPriorityを両方組み合わせて設定できます。
また、AbortFlagsに関係なくノードが実行される際に条件判定が一度行われ、判定結果がfalseであれば実行されることなく親ノードへ失敗を返します。

AbortFlagsの設定

今回は、Idle1ノードが実行中に中断したいため、AbortFlagsをSelfに設定します。

  • CalculatorCheckのAbortFlagsドロップダウンをクリック。
  • メニューから「Self」のみを選択。
GIF

CalculatorCheckの設定

AbortFlags以外の設定を行います。

  • Condition Listの+ボタンをクリック。
  • メニューから「Bool」を選択。
  • Bool Value 1のチェックをオンにする。
GIF

動作を確認しよう

プレイして動作を確認してみましょう。

まずは、何も操作せずに動作を見てみましょう。

Idle1のみが実行され続ける動作になっています。
この動作になる流れは以下の通りです。

  • 最初にRootノードが実行される。
    Rootノードは一つしかない子ノードを強制的に実行する。
  • Selectorノードが実行される。
    Selectorノードは子ノードのうち一番左から順に実行する。
  • Idle1ノードのCalculatorCheckで実行の条件判定を行う。
    Bool値が一致しているためアクションを実行する。
  • Idle1ノードでIdleアクションは何もしない。
    成功失敗も返さないので、Idle1ノードが実行され続ける。

それでは、プレイしたままCalculatorCheckのBool Value 1のチェックをオフにしてみましょう。

GIF

今度はIdle2ノードが実行されるようになりました。
動作の流れは以下のように変わりました。

  • Idle1ノードが実行中にCalculatorCheckの条件がfalseになる。
  • CalculatorCheckのAbortFlagsがSelfなので、自ノードを失敗として返す。
  • SelectorはIdle1ノードの結果が失敗だったため、右隣のノードを実行する。
  • Idle2ノードが実行される。
    Idleアクションは何も処理することなく実行され続ける。

これで、AbortFlagsがSelfの時、デコレータの判定結果がfalseになったときに実行中のノードを中断する動作が確認できました。

ノードの割り込み

ある行動中でも優先度の高い行動を割り込んで行うとより柔軟な行動をするNPCができます。

たとえば、警備員は普段は経路巡回しているかもしれませんし、警備室で待機中かもしれません。
しかしどのような行動であっても、不審者が見つかり次第最優先で追跡を行ってほしいです。

割り込みはこれまで作っていたビヘイビアツリーを少し変更するだけで実現できます。

AbortFlagsの変更

CalculatorCheckのAbortFlagsをLowerPriorityに設定します。

  • CalculatorCheckのAbortFlagsドロップダウンをクリック。
  • メニューから「Nothing」を選択(「Self」を解除)
  • 再度CalculatorCheckのAbortFlagsドロップダウンをクリック。
  • メニューから「LowerPriority」を選択。
GIF

CalculatorCheckの変更

最初に優先度の低いIdle2ノードを先に実行させたいため、CalculatorCheckの条件を一致しないように変更します。

  • Bool Value 1のチェックをオフ。

動作確認しよう

プレイして動作を確認してみましょう。

まずは、何も操作せずに動作を見てみましょう。

Idle2ノードのみが実行され続けます。

この動作の流れは以下の通りです。

  • 最初にRootノードが実行される。
    Rootノードは一つしかない子ノードを強制的に実行する。
  • Selectorノードが実行される。
    Selectorノードは子ノードのうち一番左から順に実行する。
  • Idle1ノードのCalculatorCheckで実行の条件判定を行う。
    Bool値が一致していないため結果はfalse、Idle1ノードを実行することなく親ノードに失敗を返す。
  • SelectorはIdle1ノードの結果が失敗だったため、右隣のノードを実行する。
  • Idle2ノードが実行される。
    Idleアクションは何も処理することなく実行され続ける。

それでは、プレイしたままCalculatorCheckのBool Value 1のチェックをオンにしてみましょう。

GIF

今度はIdle1ノードが実行されるようになりました。
動作の流れは以下のように変わりました。

  • Idle2ノードが実行中はIdle1ノードのCalculatorCheckが再評価される。
  • Idle1ノードのCalculatorCheckの判定結果がtrueになる。
    実行されているIdle2ノードはIdle1ノードよりも優先度が低いため、AbortFlagsのLowerPriorityの設定に従って、Idle1ノードを割り込ませる。
  • Idle2ノードが中断される。
  • Idle1ノードが割り込みで実行される。
    紐づけられたIdleアクションが実行される。
  • Idleアクションは何もしない。
    成功失敗も返さないので、Idle1ノードが実行され続ける。

低優先度のノードよりも高い優先度のノードを割り込ませて実行できました。

中断と割り込みの組み合わせ

AbortFlagsのSelfとLowerPriorityは組み合わせて使えます。

組み合わせると、同じ条件で割り込みと中断が行えるようになります。

例えば、警備員は不審者がいれば追跡し、いなくなれば巡回したり警備室に戻るでしょう。

AbortFlagsの変更

CalculatorCheckのAbortFlagsにSelfとLowerPriorityの両方を付けます。

  • CalculatorCheckのAbortFlagsドロップダウンをクリック。
  • メニューから「Self」を選択。
    「Self」と「LowerPriority」の両方が選択される。
    (「Everything」を選択しても同じです。)
GIF

動作確認

プレイして動作を確認してみましょう。

CalculatorCheckのBool Value 1のチェックのオン/オフを切り替えてみてください。

GIF

動作の流れは以下にようになります。

  • CalculatorCheckの判定結果がtrueの時、Idle1が実行される。
  • CalculatorCheckの判定結果がfalseの時、Idle2が実行される。

CalculatorCheckの判定結果が切り替わるごとに、すぐに中断や割り込みが行われているのが確認できました。

LowerPriorityの補足

LowerPriorityはその名の通り、優先度の低いノードを中断し優先度の高いノードを割り込んで実行する設定です。
つまりLowerPriorityが設定されているノードよりも優先度が低ければ、何個あってもどんな処理内容であっても優先度が高いノードに割り込まれる可能性があることを意味します。

例えば以下のようなビヘイビアツリーを組んだとします。

動作の流れは以下のようになります。

  • Idle1の条件がfalseの場合は、Waitノードを10秒実行し、次にIdle2ノードが実行されます。
  • Idle1の条件がtrueになると、WaitノードかIdle2ノードが実行中かにかかわらずIdle1が割り込んで実行されます。

警備員の例でいうと、Idle1が追跡アクション、WaitとIdle2が巡回アクションや待機アクションになります。
巡回か待機のいずれかであっても不審者の発見により追跡アクションが最優先で実行されます。

LowerPriorityが設定されたノードから見てどのノードを中断するかは、ノードの優先度を見比べるとわかりやすいです。

  • Root : 0
  • Selector : 1
  • Idle1 : 2
  • Sequencer : 3
  • Wait : 4
  • Idle2 : 5

つまりIdle1の優先度2よりも低い(数値が大きい)ノードのSequencer、Wait、Idle2はIdle1に割り込まれることになります。

おわり

以上でArborチュートリアル「デコレータによる中断と割り込み」は完了です。

チュートリアル完了をツイート

他のチュートリアルを見る