【UE4】AIMoveToノードのゴールに指定する型による動作の違いについて

#はじめに
お堅いタイトルですが、さっくり読めますのでザザッと読み流していただければ。

UAIを目的地に移動する際に頻出するノードとして**AI Move Toノード**もしくは**Move To Location or Actorノード**があります。

![2018-02-14_21h39_22.png](https://qiita-image-store.s3.amazonaws.com/0/104467/710c90cc-9752-bab5-0e11-844208b73812.png)

これらのノードにはゴールを指定する際に**Vector型**と**Actor Reference型**の2つが指定出来ますが、実はVector型でゴールを指定した場合とActor Reference型でゴールを指定した場合で動作にちょっとした違いがあります。

この記事では、それぞれの型による動作の違いによる問題点と解決策を出してみます。

#テストコード
処理を以下のように組み、ゲームプレイデバッガを利用して移動経路を表示します。

![2018-02-14_22h03_11.png](https://qiita-image-store.s3.amazonaws.com/0/104467/fd12dd7e-f489-beae-290d-f6e401fe53ec.png)

#結果
| Actor Reference型 | Vector型 |
|:-:|:-:|
| ![AIMoveTo-GoalActor.gif](https://qiita-image-store.s3.amazonaws.com/0/104467/f7cc100c-65db-0565-6cfd-14ca019a9172.gif)  | ![AIMoveTo-GoalLocation.gif](https://qiita-image-store.s3.amazonaws.com/0/104467/94762807-13b0-81f0-78ff-ac62b8ecd306.gif)  |

#それぞれの動作の違い
見比べてみると、動作の違いがよく分かりますね。
両者の違いは**移動経路の更新タイミング**にあります。

ゴールをActor Reference型で指定した場合、プレイヤーが動くとAIの**移動経路は即座に更新**され、常にプレイヤーを追いかけるような動作をしています。

それに対しVector型で指定した場合は**一度ゴールに指定された位置に移動するまで**移動経路は更新されず、ゴール到達後に更新します。

この問題は「プレイヤーを追い回す敵」や「プレイヤーに追従する味方」等のAIであったり、特に**EQSを使用して移動先が頻繁に大きく変化する**ような場合では結構目立ってしまいます。*ゲームのジャンルやデザインによっては全く目立たないこともあります。*

#解決策
それぞれの動作の違いで厄介なのはゴールをVector型にした場合だと思います。
幸いなことにこの問題の解決策は非常に簡単です。

![2018-02-14_23h44_32.png](https://qiita-image-store.s3.amazonaws.com/0/104467/5f89adde-8b3a-d3bb-ade2-faeaee636dbd.png)

**Simple Move to Locationノード**を使います。

Tickイベントで毎フレーム呼び出すのはちょっと嫌だなという方はSet Timer by EventやTick Intervalを調整して呼び出し間隔を調整すると気持ちが楽になるかもしれません。

Simple Move to Locationで目的地に到達したかを判断したい場合は以下のようにします。
目的地を表す変数を用意しVector同士のEqualやNot Equalで目的地と自身の位置をチェックするだけです。

![2018-02-14_23h58_16.png](https://qiita-image-store.s3.amazonaws.com/0/104467/3baf1db0-9c45-cf7b-5fb4-7e6cca446bc4.png)

#おわりに
この問題は

>[スクウェア・エニックスにおける UNREAL ENGINE 4 を用いた人工知能技術の開発事例](https://www.youtube.com/watch?v=BV2GTGbSjq8)

こちらを参考にEQSとBehavior Treeを作成している時に現れました。

AIの振る舞い~~やBehavior Treeの組み方~~によっては、この問題が顕著に現れることもありますので、その時はこの記事が参考になれば幸いです。


#追記 : Behavior TreeのMove Toノードの場合
Behavior TreeのMove Toタスクも中身はMove To Location or Actorと同一なので同様の問題を抱えています。Behavior Treeでこの問題を回避する際に自分は以下のようにMove Toタスクを自作していました。

![2018-02-15_20h45_27.png](https://qiita-image-store.s3.amazonaws.com/0/104467/0b3d114f-6676-4b8d-6d60-df434f123888.png)

この記事を投稿しTwitterで共有したところ、このような返信がありました。


<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr">Event Tick呼ばなくてもMove Toタスクのここにチェック入れるとVector型でも更新してくれますよ🙂 <a href="https://t.co/Pu4jCQpghI">pic.twitter.com/Pu4jCQpghI</a></p>&mdash; 物理コットン (@physicalcotton) <a href="https://twitter.com/physicalcotton/status/964082484659957760?ref_src=twsrc%5Etfw">2018年2月15日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


目から鱗が落ちました。それはもうボロボロと落ちまくりました。
詳細表示の部分は全くのノーマークでしたので、ロクに調べてもおらず問題回避にはMove Toタスクを自作するしかないと思っていましたが、この方法であれば非常に手軽に問題を回避出来ます。

##Observed Blackboard Value Tolerance
**Observed Blackboard Value Toleranceオプション**が有効化されると現在の目標位置を表すBlackboardKeyと前フレームの目標位置（更新ごとに記録される）の距離をチェックし、Observed Blackboard Value Toleranceよりも大きく距離が離れていれば、現在の移動を中止し新しい目標位置へと再度移動するようです。*(該当する処理はエンジンコードのBTTask_MoveTo.cppの226行目から253行目にあります)*

Observed Blackboard Value Toleranceのツールチップには以下のように書かれています。（google翻訳使用）

>タスクがBBキーで表される場所の変更に反応すると予想される場合、このプロパティを使用してメカニズムの感度を調整することができます。 値はAcceptableRadiusより小さくすることをお勧めします

オプションの初期値が**4.75**という妙な値なのは、このような理由のようです。

#おわりに
情報を提供してくださった物理コットン様のお陰でより良い解決策を提示することが出来たと思います。Observed Blackboard Value Toleranceについての情報をくださった物理コットン様には深く感謝いたします。