Unity + Arborで敵AIを作るならこれだけは覚えておけ！集〜ビヘイビアツリー編〜

Unityで敵AIを組む方法は様々ですが、Arborというアセットが便利です。
とても便利なアセットですが、自分で拡張して使っていく時の情報が少なく感じており、敵AIをビヘイビアツリーで組むときにポイントとなる要素をまとめました！
誰かの助けになれば幸いであります。

環境
Unity 2020.2.1f1
Arbor 3.7.8

前置きのいらない人は[本編](https://qiita.com/asiram/items/7fcf50d10f26f5ae2e87#arborで敵aiを作るならこれだけは覚えておけ集)からどうぞー

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ついにスクリプトでのAI行動をArborに載せ替えするところまできました<br><br>1枚目はbefore、yaml形式で表現していたAIデータ構造。<br>2枚目はafter、arborで同じことをするために組んだグラフ。<br><br>一見複雑ですが、挙動を使いまわし可能、組み換えの自由度アップ、把握のしやすさアップでとても良いです <a href="https://t.co/oMNFxjPiFH">pic.twitter.com/oMNFxjPiFH</a></p>&mdash; asiram@unityゲーム開発 (@asiramasiram) <a href="https://twitter.com/asiramasiram/status/1355400969555595268?ref_src=twsrc%5Etfw">January 30, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# ビヘイビアツリーとは
敵AIなどを作成する際の手法の一つです。
現在のゲームの敵AIの多くがビヘイビアツリーで組まれているそうです（参考：https://thinkit.co.jp/article/10012 ）。

- ツリー状の構造によって、次に行う行動を決定する
    - 「成功」が帰ってくるノードを順に探索し、子ノードを実行する（ArborだとSelector）
    - 「失敗」が帰ってくるまで子ノードを順に実行する（ArborだとSequencer）
    - などのノードから構成される

# Arborとは
アセットストア：[Arbor3](https://assetstore.unity.com/packages/tools/visual-scripting/arbor-3-fsm-bt-graph-editor-112239)
公式ページ：https://arbor.caitsithware.com

- Unityのアセットで、ステートマシンやビヘイビアツリーを作成することができる
- ビジュアルスクリプティング（GUIを使用して視覚的にスクリプトを組むこと）によって挙動を組んでいく
- メリット
    - GUI操作によって挙動を作成可能（視覚的に組めてわかりやすい
    - 実行時にプレビューができ、デバッグが捗る
    - 日本語[チュートリアル](https://arbor.caitsithware.com/tutorial/)・[マニュアル](https://caitsithware.com/assets/arbor/docs/ja/manual.html)・[ドキュメント](https://arbor-docs.caitsithware.com/ja/)完備！

## ちなみに入門するには？
公式ページのチュートリアルが充実しているのでオススメです！
これで十分！
https://arbor.caitsithware.com/tutorial/

# Arborで敵AIを作るならこれだけは覚えておけ！集

## ビヘイビアツリーの基本要素
実は[このページ](https://caitsithware.com/assets/arbor/docs/ja/manual/behaviourtree/nodes.html)にまとまっているのですが、実例を交えてわかりやすく解説してみます。

### コンポジットノード
#### Selector
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/d000ab54-eb86-0cad-5f5b-461717c7dfa2.png)
子ノードを左から順に実行し、子ノードが成功を返した時点で終了するノード。

例えば子ノードに、それぞれ`HPが5割以下`、`HPが８割以下`という条件判定するノードが紐づいている場合を考えましょう。
敵HPが６割であれば、`HPが5割以下`は条件を満たさない、`HPが８割以下`は条件を満たすのでそのノードを実行して終わる、といった使い方ができます。

#### Sequencer
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/b4f6bf6d-4f60-833f-3a12-1f51121b6fff.png)
子ノードを左から順に実行し、子ノードが 失敗を返した時点で終了するノード。

基本的な使い方は、紐づいた複数の子ノードを順番に実行していきたいときに使います。
例えば、`アニメーション再生`→`ダメージ適用`→`3秒待つ`などの動作(Action)を紐づけて順番に実行することができます。

### アクションノード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/cd338eda-ce9f-0481-8905-79e512160f67.png)
アクションを行うノードです。
ビヘイビアツリーを使うときは、アクションを拡張して量産することになると思います。

組み込みで入っているアクションは、`~秒待つ`・`Activeを切り替える`・`音を再生する`などなど。
敵AIに使う場合は、例えば`ダメージを与える`・`攻撃アラートを表示する`など、自分で多彩な行動を作ることになると思います。

#### アクションノードの自作
ここの[マニュアル](https://caitsithware.com/assets/arbor/docs/ja/manual/scripting/behaviourtree/actionbehaviour.html)が参考になります。
詳細には書きませんが、基本的な流れは以下のような感じです。

- ActionBehaviourを継承したクラスを作成する
- OnExecute()メソッドをオーバーライドし、挙動を自分で定義する
- OnExecute()メソッドの最後にFinishExecute()を呼んでノードを終了する
    - これやらないと永遠にそのアクションを実行し続けるので注意！毎フレーム攻撃とかになるよ！

### Decoratorスクリプト
![スクリーンショット 2021-01-30 17.59.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/87b6a526-883e-d586-af94-9da458e762cf.png)
条件判定を行って実行中ノードの中断や割り込みを行ったりするスクリプトです。
アクションノードの頻度ほどではないかもですが、こちらも自作することが多くなると思います。

上の画像では、自作の死亡フラグチェックDecoratorがアタッチされています。
敵キャラの死亡フラグが立ったら条件を満たす　という処理が書いてある感じです。

例ではアクションノードにアタッチされていますが、コンポジットノードにアタッチすることもできます。
このDecoratorが返す`成功/失敗`、アクションノードが返す`成功/失敗`でSelectorやSequencerで実行する処理が分岐します！

#### Decoratorの自作
ここの[マニュアル](https://caitsithware.com/assets/arbor/docs/ja/manual/scripting/behaviourtree/decorator.html)が参考になります。
基本的な流れは以下の通りです。

- Decoratorを継承したクラスを作る
- OnConditionCheck()メソッドをオーバーライドし、`成功/失敗`を表す`true/false`を戻り値として返す

## 自作するときに便利なやつら
### Flexible~シリーズ
Actionノードにて、`public`もしくは`[SerializeField]`な変数を追加すると、GUIのノード上で値が設定できるようになります。
が、生の型を使って定義するのはもったいない！
Flexibleシリーズがオススメです

例えば、攻撃力的な変数を定義するときにこんな感じで定義できます

```C#
// 引数は初期値
[SerializeField] private FlexibleInt power = new FlexibleInt(30);
```

こう書くと何が嬉しいのか...

- 他のOutput系ノードから値を引っ張ってこれる
- パラメータを直接参照できる
- そのまま直接値を入力可能

...と、いろんな値の設定パターンができるようになります！
こんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/373289bc-cd2d-4b05-bbb3-c1431c112a87.png)

上の例だと、Powerは`FlexibleInt`、Rangeは`FlexibleFloat`で定義してあり、標準でよく使われる型のFlexible〜が用意されています。
設定の右の▼から設定タイプを変更できます。

スクリプト内で実際の値を使用する際は、`value`を使用します。

```C#
power.value
```

### OutputSlot〜シリーズ
入力値としてFlexibleシリーズはとても便利ですが、ここぞというときに出力を行うOutputSlotを使用すると、値を渡すことができてさらに便利です。

```C#
[SerializeField] private OutputSlotFloat castTimeOutput = new OutputSlotFloat();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/a61c6044-07a4-47b3-e0d3-75f17f1fed18.png)

上の例だと、`castTimeOutput`が出力されて、Waitの`Seconds`に値が渡されるようになっています。

OutputSlot~を使用するときのメリットはこんな感じでしょうか。（）内が一番大事かも...

- 値を流せるので、手動で値を入力しなくて良い（手入力が減ってミスが防げる）

OutputSlotにはちゃんと値を入れてあげましょう。
アクションノードのOnExecute()で値を入れてあげると良いと思います。

```C#
castTimeOutput.SetValue(castTime.value);
```

## 知っていると便利なチョイテクニック
### 一回だけ行動させる
例えば、HPが〜割切ったら**一回だけ**技を発動させる。みたいなパターンです。
もちろんそういうスクリプトを組んでしまえばいいと思うんですが、汎用性を高めるためにもarborの機能で作るのがオススメです。

arborには、一つのビヘイビアツリー内で使用できるパラメーターを設定することができ、コレを使用しフラグを作成して実現させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/f2cd565e-df63-e23c-31b6-ff30f07b05c5.png)
ArborEditorウィンドウの左のパラメータメニューから作成できます。
ここから、bool型のパラメータを追加します。
パラメータが追加できたら、組み込みの`Parameter Check`Decoratorを使用します。

パラメータの赤い(型によって色は変わります)図形をドラッグして、Parameter checkにドロップすればパラメータを参照することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/b26e2b76-3989-9028-c848-fcc27653d19b.png)
このDecoratorは、パラメータをチェックして、設定値と一致する場合は`成功`を返す挙動をします。
今回は、攻撃完了フラグが`false`なら成功を返すようにしています。

次に、アクション終了後にこのフラグをtrueにする`Set Parameter`アクションノードを追加してあげれば完了です。
こちらのノードにもパラメータをドラッグ＆ドロップしましょう。
値はtrueにしたいので、Valueにチェックをするのを忘れずに。
全体だとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/b2abe864-3426-a28f-67ea-17f9aec74577.png)
これで１回だけ行動する（１回だけ成功をDecoratorが返す）ことができるようになりました！

### グループ
ノードをグループ化できます。
これをやっておかないと、ノードを複数まとめて移動したいときに辛いです。
何もない空間で右クリック、`グループを作成`から作成できます。
灰色の枠が出てきたら、グループ化させたいノード達を覆うようにサイズを変更しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/b5c642d8-87b1-9a72-3452-c4329153ed06.png)


### コメント
その名の通りコメントが残せます。
#### ノードコメント
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/f050cb97-6b57-02bc-b84e-bc87c1139a24.png)
ノードの歯車マークから追加できます。
ノードに対するコメントに使用しましょう。

#### コメントノード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/b1a177cf-1012-1dcb-17b6-ed3eba82c1b9.png)
ノードとしてコメントを残せます。
ビヘイビアツリー全体に関してのコメントや、目立たせたいコメントに使えそうです。

#### グループコメント
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/3db2730b-6441-da1b-8657-68c2a384fb91.png)
グループに対してコメントを残せます。

# まとめ
Arborで敵AIを自作するときに使えそうな事柄をまとめてみました。
これだけ知っていれば、自作のビヘイビアツリーもいい感じに組めるかと思います！

さらなるテクニックとして、挙動をまとめてプレハブ化、再利用しやすくするというのがあるんですが、チョイ複雑なので別の機会に解説できればと思います！

[追記]ビヘイビアツリーでの敵AIデータ再利用方法を解説した記事は[こちら](https://qiita.com/asiram/items/eeb800f5be9de8b57236)。