【UE5】BehaviorTreeを使うときに気を付けるべきこと２点

# 本記事について
:::note warn
本記事執筆時点でのUEバージョンは5.3.2となります。
:::

現在UEのAI機能について勉強をしているのですが、
色々と調べていく中でBehaviorTreeを使ってAIキャラクターを作る時に
気を付けるべきことを残していこうと思います

記載している内容はすべて独学のものであり、
実際のゲーム開発の現場などではまた違った認識があったりするかもしれませんが、
その辺りは暖かい目で見ていただけると幸いです
（間違っている箇所などありましたら、コメントなどでご指摘いただけると嬉しいです）

# BehaviorTreeを使うときに気を付けるべきこと2点
BehaviorTreeを使うときに気を付けるべきこととして、以下の２点が挙げられます

1. <font color="Red">**BehaviorTreeを肥大化させない**</font>
2. <font color="Red">**BehaviorTreeの内部で状態管理をしない**</font>


## BehaviorTreeを肥大化させない
### 肥大化してしまった際のデメリット
色々とデメリットはあると思いますが、以下の２点が挙げられます
* <font color="Red">**可読性が低くなる**</font>
* <font color="Red">**保守が難しくなる**</font>

### 肥大化の例
例えば以下の画像のようなBehaviorTreeを見てください
（無意味に配置していっただけなので中身はスルーしてください）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311487/cea6a202-31ee-41d5-9a5b-6f06f0bd3dbe.png)

これはテストのために作ったものなので適当ですが、
更に各ノードにDecoratorやServiceが付いたり、各ノードの変数状況を見ていったり…

<br>
これらの全体像を把握するだけでも相当な時間がかかってしまいます
<br>
<br>

このBehaviorTreeの生産者ならまだいいかもしれませんが、
それ以外の人が見たらどうでしょうか…？

~~私だったらいったん犬の画像とかに癒しを求めに行きます~~
<br>

個人開発ならいいかもしれませんが、チーム制作の場では一人一人のモチベーションは
重要になってくるので、見やすく触りやすいものを心がけていきたいところです


### 肥大化しないようにするには？

簡単なところとしては、<font color="Blue">**機能ごとにBehaviorTreeを分割する**</font>ことが良いかなと思います
<br>

前述したように、肥大化してしまうと簡単な調整もしづらくなってしまい、
BehaviorTreeを使いまわすことも難しくなってしまいます

なので、共通する処理の部分は個別でBehaviorTreeを作成し、
「RunBehaviorTree」などでBehaviorTree上から別のBehaviorTreeを呼び出すようにする、
といった形にすればBehaviorTreeをよりコンパクトに出来るかと思います
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311487/18238cd3-baff-d278-bfc1-0f5937e76bfd.png">


## BehaviorTreeの内部で状態管理をしない
ここに関してはUEのDevCommunityでも言及されている内容になります（※以下リンク先）
[Common issues with Behavior Trees and things you should competely avoid.](https://dev.epicgames.com/community/learning/tutorials/L9vK/unreal-engine-common-issues-with-behavior-trees-and-things-you-should-competely-avoid)

### BehaviorTreeでは別のノードへジャンプが出来ない
BehaviorTreeはRootからの実行順序が決まっており、
基本的に<font color="Red">**特定のノードから別のノードへジャンプするといったことは出来ません**</font>

そのため、状態を変化させて対応する行動を切り替えるといったことをするには向いていません

実行順序については、以下画像の黄色枠のように表示されています
※[公式ドキュメント](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/behavior-tree-in-unreal-engine---overview?application_version=5.3)より引用
<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311487/866b84a7-dcda-fc48-0b57-e41feb23250f.png">

### 特定のノードから別のノードへジャンプできない例

例えば、簡単に以下仕様のAIキャラクターを作るとします
* プレイヤーを見つけていない時　⇒　適当な場所を歩く
* プレイヤーを見つけている時　　⇒　プレイヤーを追いかける 
* 何もせずその場で留まる

上記の内容をBehaviorTreeで作ってみたものが以下画像の内容になります
<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311487/1c24c01e-d00f-e167-6f8a-1abf06ded5e5.png">

| 実行番号 | ノード名 | 詳細 |
|:-:|:-|:-| 
| 0 | Selector  | 繋がれているノードを左から順番にチェックし実行できるものがあれば、それを実行する |
| 1 |  BTDecorator_IsTargetFound | IsTargetFoundというbool変数がFalseになっていればこのノードを実行する |
| 2 | Sequence  | 繋がれているノードを左から順番に処理する |
| 3 | BTService_FindLocation  | 移動できる座標を計算しBlackboardの変数にセットする |
| 4 | MoveTo  | Blackboardの変数から取得した座標へ移動する |
| 5 |  Wait | 設定された時間だけ待つ |
| 6 |  BTTask_MoveToTargetActor | Blackboardの変数から取得したActorを追いかける |
| 7 |  Wait | 設定された時間だけ待つ |

このBehaviorTreeを使い、番号２～５の処理中にプレイヤーを見つけた時、
即座にプレイヤーを追いかける行動に移したいとします
<br>

方法としては番号２のSequenceにBlackboardBasedConditionを追加し、
IsTargetFoundの値が変わった時にキャンセルさせるといった仕組みにすれば問題なく動作します
(具体的な設定方法などは割愛します)
<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311487/052c38fd-697a-7361-402e-33fdfd07e386.png">
<br>

では、番号２～５の処理中にプレイヤーを見つけた時、
**番号７のWaitのノード処理へ飛ばしたい**、となったらどうするでしょうか？
<br>

同じように番号２のところにDecoratorを追加してキャンセルさせても、
次の処理は番号６のBTTask_MoveToTargetActorになってしまうため、
<font color="Red">**番号７のWaitへ直接ジャンプさせるということが出来ません**</font>

<br>
もちろん、BTTask_MoveToTargetActorにDecoratorを追加するなどして<br>
通らないようにすることは出来ますが番号７の処理を飛ばすことは出来ないため、<br>
必ずチェック処理が走ることになってしまいます

### 仮に状態管理を実装する場合

では、これらを踏まえてBehaviorTreeで状態管理をするとしたらどうなるでしょうか？

例えば、某ステルスゲームに出てきそうな**巡回する警備兵**みたいな敵を作ろうとした時、
以下のようなBehaviorTreeを作るとします
（具体的な中身は省略します）
* **敵の状態**
    * 番号１：プレイヤー未発見状態（適当に移動させる）
    * 番号２：警戒状態（銃を構え辺りを見回しながら移動させる）
    * 番号３：プレイヤー発見状態（プレイヤーを追いかけ、近づいたら攻撃する）
<br>

この敵において、
<font color="Green">**番号１の「未発見状態」**</font>の時に目の前にプレイヤーが現れた場合は<br>即座に<font color="Green">**番号３の「発見状態」**</font>へ移行させたい、
ということをしたくなったとします
<br>

前回のテストと同様に、
番号１にいるところでキャンセル処理を追加して番号３へ向かわせようとしますが、
<font color="Red">**必ず「警戒状態にするべきか？」というチェック処理は通る**</font>ことになってしまいます
<br>

どの状態へ移行するか分かっているのに、
わざわざ「警戒状態に出来るか？」というチェック処理が入るのは明らかに無駄です
<br>

チェック処理が軽いものなら気にならないかもしれませんが、
複雑な計算処理が走るような場合には無視できない内容になってしまうため、
不要であればなるべく走らせないようにしたいところです

### 改めて状態管理に向かない理由

ちょっと強引な例えになってしまったかもしれませんが、
<font color="Red">**BehaviorTreeは処理順番が決まっており処理フローを意図的に操作することは難しい**</font>ため、
状態管理のようなことは向いていません

### 状態管理のようなことをしたくなったらどうする？

もし状態管理のようなことが必要になったら、
<font color="Blue">**StateTreeとBehaviorTreeを組み合わせて活用する**</font>など考えるべきかもしれません

※公式ドキュメント：[StateTreeの概要](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/overview-of-state-tree-in-unreal-engine?application_version=5.3)

# 最後に
拙い文章になってしまい申し訳ないですが、ここまで見ていただきありがとうございます

AIについては勉強を始めたばかりで、
UEのBehaviorTreeやStateTreeについてこれから更に勉強を進めていこうと思いますので、
間違っている箇所などありましたらコメントにてご指摘を頂けると嬉しいです

# 参考資料
以下のドキュメントや動画を参考にさせていただきました

https://dev.epicgames.com/documentation/ja-jp/unreal-engine/behavior-tree-in-unreal-engine---overview?application_version=5.3

https://dev.epicgames.com/community/learning/tutorials/L9vK/unreal-engine-common-issues-with-behavior-trees-and-things-you-should-competely-avoid

https://www.youtube.com/watch?v=Qq_xX1JCreI