AIPerceptionとBPでPlayer追跡AIを作成する

# 目的
AIPerceptionの情報は調べればあるものの一つにまとまっているものが無かったので勉強ついでにまとめてみました。
公式のBehaviourTreeのクイックスタートガイドにつぎ足しで作成しているため大部分はそちらを参考にしていただく内容になってます。とりあえず視覚を持ったAIを作りたいだけの方は直接ガイドを参照してください。

https://dev.epicgames.com/documentation/ja-jp/unreal-engine/behavior-tree-in-unreal-engine---quick-start-guide

# 対象
- BPで開発をしていてAIに視覚、聴覚を簡単に持たせたい方
- BehaviourTreeの基本的な知識がある方
- AIPerceptionを少し理解して使いたい方

# バージョン
UnrealEngine 5.3.2

# 最初に
最初にどのようなAIを作っているか動画で見たほうが分かりやすいと思うので完成形を載せておきます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/ukFAFoAl81A?si=yYTniNCzJxop_119" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# AIPerceptionとは
大概AIControllerにコンポーネントとして実装され、外部から刺激(Stimulus)があった時にイベントを発火します。何も設定しないまま実装した場合、基本的にレベル上にいるポーンを視覚刺激の対象として自動登録し、発見などのイベントを作成することができます。
新たに聴覚などの刺激を与えたい場合はAIPerceptionStimuliSourceコンポーネントを実装したアクタを作成し、刺激を通知するイベントを作成して使用します。
また、ここの刺激については拡張しだいで様々な種類の刺激を作成可能なのですがBPでは現状できません。

# AIPerceptionをBPで扱う利点と欠点
明確な利点と欠点があります。事前に言っておくと、特にパフォーマンスにこだわらない場合は十分な機能が扱えます。
### 利点
- 計算式を作ることなく視覚の範囲や距離をAIControllerに実装できる
- 従来のPawnSensingはここ最近ではアップデートが行われてないらしく、今後はAIPerceptionが主流になっていく可能性が高い
- 視界外になったときも扱える（PawnSensingは検出イベントのみ扱える）
### 欠点
- 公式から出ているドキュメントがほとんどない(2024/8月時点)
- 現在はc++で拡張したクラスを使用することが前提の機能なのでそこまで複雑なことがBPではできない
- 前二つから、なんとなく公式でやってたからでやっている準備が多い

# 使い方

### 前準備
#### BehaviourTreeの中身
視覚のみを実装したい方は公式のBTを参考にしてください。
今回は聴覚と忘れる処理を使用したいためブランチを追加しています。

聴覚
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/9b747740-6a92-f7d5-00b2-68dc3ab3c2ef.png)

忘れたときの処理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/09981c03-7dd3-4f62-13ee-f869403ebdd5.png)

全体
緑→チュートリアルまま
赤→忘れたときの処理
黄色→聴覚
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/e628fa97-1706-0627-5fc0-8bff5af26b75.png)



追加した部分のみ解説します。
- 聴覚
1. BlackboardにSoundLocationというVector値を追加します
1. チュートリアルのHasLineOfSightと同じようにSoundLocationがセットされたときブランチを実行するDecorationを設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/11b02c60-0ec6-e558-9d39-1ef7911cc36a.png)
1. 到着後パトロール行動に戻ってほしいのでSoundLocationのKey値をクリアするタスクを作成しておきます(BTT_ClearBBValue)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/325bd892-1d5f-4fe0-e592-76346761ab8f.png)
BlackboardKeyをインスタンス編集可能にし、SoundLocationを指定してください。

- 忘れたときの処理
1. BlackboardにStimulusForgottenというBool値を追加します
1. Decorationを設定します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/67585c5b-3309-6426-f866-5f235b91187a.png)
1. 一定時間後にStimulusForgottenをリセットするタスクを作成し、Waitの後に置いておきます(BTT_ResetStimulusForgotten)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/13484f76-068d-e6eb-63fb-34128d905827.png)



#### 最適化
プロジェクトファイル下のConfigフォルダ内にある**DefaultGame.ini**に以下を入力します。
```
[/Script/AIModule.AISense_Sight]
bAutoRegisterAllPawnsAsSources = false
```
これによりAIPerceptionが自動でレベル内にあるPawnBPをすべて視覚のターゲットとして登録するのを防ぐことができます。
代わりに追いかけられるPawnBPには**AIPerceptionStimuliSource**コンポーネントを追加し次のように設定します。（例えば敵に追いかけられるPlayerに設定）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/066d2b46-96ee-408f-b12a-845d66181875.png)
これで視覚で追ってほしいPawnのみをAIPerceptionに登録できました。
BPでは最適化をする手段が少ないので出来ることはやっておくべきです。

#### 監視対象を忘れるようになる設定
AIPerceptionにはMaxAgeという項目でどれぐらいの時間までターゲットを監視しているかを設定できます。
しかし先ほどと同じConfigファイル内の**DefaultEngine.ini**に以下の記述をしないとこの機能自体が有効にならないので記述しておきます。
```
[/Script/AIModule.AISystem]
bForgetStaleActors=True
```

#### 音を出すアクタの作成
AIPerceptionへ音の刺激を渡すには専用のコンポーネントを実装したクラスからイベントを実行する必要があります。
1. 適当なActorBPを作成し**AIPerceptionStimuliSource**コンポーネントを追加します。
1. 設定は以下のようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/c75fc615-967b-a4b1-839f-579e9ac9f803.png)
これでAIPerceptionに聴覚の監視対象として登録できました。
1. ReportNoiseEventで聴覚刺激を通知できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/4273ee87-73ce-b07b-87f8-5753205e10a7.png)




### 実装
AIPerceptionの機能は基本的にAIController内で実装していきます。
#### AIPerceptionコンポーネントの設定
AIControllerにAIPerceptionコンポーネントを追加、SensesConfigの＋ボタンを押し監視する項目を追加します。追加したら以下のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/fb360077-b0da-cdd2-bac1-7e6d2559a519.png)


編集している値
- AISense_Sight
視覚を実装、聴覚はAIHearingconfig
- SightRadius
対象を見つける距離、お好きな値で
- LoseSightRadius
対象を見失いまでの距離、お好きな値で
-Detection by Artificial
視覚で監視する対象を選ぶ。BPだとここを自由にカスタムできないので全部選択しておき、BPの方で処理する。
- MaxAge
AIPerceptionが監視対象を忘れるまでの時間。0にしておくと忘れないようになる。視覚の場合視野内にいると毎回更新されているためあまり意味がない。今回は聴覚用。
前述のDefaultEngine.iniへの記述がないとこの機能は無効になるため注意。

（聴覚の方も似たような項目なので画像のみで割愛）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/755ba30f-9506-ab20-0cf3-e0558feb4151.png)

#### 知覚イベントの実装
**OnTargetPerceptionUpdated**を使用します。AIControllerのイベントグラフにてコンポーネントからこの関数をオーバーライドしBlackboardへ検知したActorやVectorを登録するようにします。

視覚への刺激か聴覚化の刺激化を判別
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/2b6f55b0-0021-4026-eb82-174e7fd67eb1.png)
聴覚の処理。上画像のBranchのFalse処理、画像内Branchには上画像のBreakAIStimulusをつないでいる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/f076a14a-2c82-8bc9-4c0a-3602661103da.png)

各種関数の説明
- GetSenseClassForStimulus
受け取った刺激のクラスを取得できる。今回はAISenseSightならTrueにしている。
- BreakAIStimulus
受け取った刺激の状態を監視できる。SuccessfullySensedは監視できているか否かを取得でき、刺激の対象が視界外に行くとfalseになる。また、前述のMaxAgeを設定していると一定時間後falseになるようにもできる。
聴覚処理内では刺激が検知された場所のVector値を使用している。

今回は視覚と聴覚のみ実装しているため、受け取った刺激クラスが視覚の場合はそれがPlayerTagを持っているかの確認をして後の処理へ回す。聴覚の場合はVector値をBlackboardに登録する。
分かりづらいと思うので全体図を貼っておきます。

https://blueprintue.com/blueprint/ig3_h30o/

#### 刺激を忘れたときのイベントの実装
**OnTargetPerceptionForgotten**を使用する。
コンポーネントからオーバーライドし、以下のように実装。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366802/e7131895-7074-2442-d712-01d8a5a1a37f.png)
Forgottenイベントが発火次第BlackboardのStimulusForgottenをTrueにする。

おわり
# まとめ
かなり広い内容で解説してしまったので要点をまとめますと

- DefaultEngine.ini,DefaultGame.iniに設定を追加
- 以下のようにコンポーネントを追加し設定を行う
AIController : AIPerception
Player,音を出すアクタ : AIPerceptionStimuliSource
- AIControllerからAIPerceptionのイベントを使用してBlackboardのKey値を更新する

# 参考
PawnSensingかAIPerceptionかを使うべきかについてのフォーラム


https://forums.unrealengine.com/t/pawn-sensing-vs-ai-perception/361144/4

AIPerceptionも使用している公式のBehaviourTreeチュートリアル動画

<iframe width="560" height="315" src="https://www.youtube.com/embed/iY1jnFvHgbE?si=e1lxSQg3ailv7BQ9" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

刺激を忘れるための設定について

https://forums.unrealengine.com/t/ue5-3-c-ontargetperceptionforgotten-delegate-not-firing/1786713/6

AIについてまとまっているスライド

https://www.docswell.com/s/alwei/KM98L5-2022-05-12-152906/1


# 最後に
元々BehaviourTreeを使用したAIは各種BPをまたいで作成するものなのでわかりづらいのに加えて元々のチュートリアルを参照してもらうことが前提でしたので余計わかりづらくなったと思います（今回はその内容を書くととんでもないボリュームになってしまうので省略せざるおえませんでした。。。。）
何か不明な点があればコメントでよろしくお願いいたします。