この記事は書きかけです。2022/12/17~

Behavior Treeアドオン「Beehave」について

Godotアドオンです。Godot3, Godot4で使用できます。

Behavior Tree(以下BT)のGodot実装といえます。

BTノードのみで構成したシーンをつくっておき、使用したいシーンで、対象にしたいノードにBTシーンをひもづけます。

複雑な行動パターンを持ったステートマシンのような使い方をします。

汎用的なアクション・条件をBTノードで作成することで、複雑な行動パターンをいい感じに管理することができます。

一見むずかしそうですが、慣れると複雑なゲームAIをかんたんに扱えるようになります。

内部でやっていることは_processで条件分岐して関数を呼び出しているだけやろという心意気で触ってみることをおすすめします。


Behavior Treeとは

Behaviour trees are a modular way to build AI logic for your game. For simple AI, behaviour trees are definitely overkill, however, for more complex AI interactions, behaviour trees can help you to better manage changes and re-use logic across all NPCs.

Behavior Treeは、ゲームのAIロジックを構築するためのモジュール化された方法です。

単純なAIの場合、Behavior Treeは確かにやりすぎですが、より複雑なAIの場合、Behavior Treeは変更をうまく管理し、すべてのNPCでロジックを再利用するのに役立ちます。

https://github.com/bitbrain/beehave/tree/godot-3.x

はじめに、Behavior Tree についてはこのスライドに目を通すのが良いです。(Unityですが)

https://www.slideshare.net/torisoup/unity-behavior-treeai


BeehaveのBehavior Tree 各Nodeの解説

ドキュメントを日本語訳したものに簡単な解説をつけました。

Leaf

末端のBTノードです。

アクションを実行するBTノードと、条件を判定するBTノードの2つです。

アクション内容や条件判定などの処理は自分でコードを書いて作成します。

Beehave内のクラスをextendsしたスクリプトファイルを作成します。

ConditionLeaf コンディション

ConditionLeaf

条件判定をするBTノードです。

このノードはシンプルなほど良く、単一の条件に応じてSUCCESSFAILUREを返します。

複数の条件をチェックするようなノードを作ると、再利用が難しくなるため、避けます。

また、しょっちゅう呼び出される想定のものは、パフォーマンスにも注意する必要があります。

毎フレームget_node()などはしないようにし、変数にキャッシュするなどがよいかもです。

  1. コンディションのコード 例IsVisibleCondition.gd
class_name IsVisibleCondition
extends ConditionLeaf

func tick(actor, blackboard):
  if actor.visible:
    return SUCCESS
  return FAILURE

ActionLeaf アクション

アクションを実行します。

この場合、RUNNINGというコードを返します。

  1. コンディションのコード 例MakeVisibleAction.gd
class_name MakeVisibleAction
extends ActionLeaf

func tick(actor, blackboard):
  if actor.visible:
    return FAILURE
  actor.visible = true
  return SUCCESS

Blackboard

Blackboardは、複数のBTノード間でデータの保存とアクセスを行うために使用できるオブジェクトです。

BTノードからは、tick(actor, blackboard)関数の引数 blackboard からアクセスします。

他のデータアクセス方法として、actorのプロパティや関数を通じてアクセスする方法もあります……が、書く場所が散らかってしまうのでBTノードのみで完結するデータに限定しておくのが無難そうな気がします。


Composite コンポジット

Compositeは子BTノードを特定の方法で実行するBTノードです。

条件とアクションを使ったノードツリーを作成するためには、Compositeノードを組み合わせて、条件・アクションを実行する。

Selector セレクター

Selectorはその子のそれぞれを実行しようとし、子のうちの1つがSUCCESSステータスコードを報告した場合にSUCCESSステータスコードを報告します。

すべての子がFAILUREステータスコードを報告した場合、このノードはFAILUREステータスコードも返します。

このノードは、たとえそのうちの1つが現在すでにRUNNING中であっても、1つのtick()実行ごとにすべての子BTノードを処理しようとします。


Selector Star セレクタースター

Selector StarノードはSelectorと似ていますが、子BTノードの1つが現在RUNNING状態の場合、前に実行されたすべての子BTノードをスキップします。

これは、実行時間に関係なく、一度に一つのアクションしか実行されないようにしたい場合に使用します。


Sequence シーケンス

Sequenceは、その子BTノードを上から順にすべてを実行します。

子BTノードのすべてがSUCCESSを返した場合に、SequenceSUCCESSを返します。

子BTノードがFAILUREを返した場合に、SequenceFAILUREを返します。その後の子BTノードは実行されません。

実行する子BTノードがRUNNING中の状態でも関係なく、1つのtick()実行ごとにそのすべての子を実行しようとします。(Sequence Starとの差)


Sequence Star シーケンススター

Sequence StarノードはSequenceと似ていますが、子BTノードの1つが現在RUNNING状態の場合、その前に成功した子BTノードをすべてスキップします。
これは、実行時間に関係なく、一度に1つのアクションしか実行されないようにしたい場合に使用します。



Decorator

Decoratorは、上記の他のノードと組み合わせて使用することができるノードです。

Failer フェイラー

Failerは常にFAILUREステータスコードを返します。

Succeeder サクシーダー

Succeederは必ずSUCCESSのステータスコードを返します。

Inverter インバーター

Inverterは、子機がSUCCESSのステータスコードを返した場合はFAILUREを、子機がFAILUREのステータスコードを返した場合はSUCCESSを返します。

Limiter リミッター

Limiterは、子機をx回実行します。最大刻み回数に達すると、FAILUREステータスコードを返します。