【UE4】EQSを使わずにプレイヤーを周回するような位置を求める

#いきなりブループリント

最も重要なプレイヤーの周回位置を求めている部分
![2019-01-06_14h05_19.png](https://qiita-image-store.s3.amazonaws.com/0/104467/c1b47c58-a2ca-3378-95e8-cde87e9d29a8.png)

実際にAIキャラクタを移動させる部分
![2019-01-06_14h05_37.png](https://qiita-image-store.s3.amazonaws.com/0/104467/d284c998-7719-2ac8-f601-79dd97d583fa.png)

Begin Play
![2019-01-06_14h05_45.png](https://qiita-image-store.s3.amazonaws.com/0/104467/a0f58b41-9b88-e443-8ee2-b15c56e0dcf5.png)

全体図
![2019-01-06_14h06_26.png](https://qiita-image-store.s3.amazonaws.com/0/104467/d4837f1d-81b8-bfc1-bd85-fd9eb1bb7078.png)

周回位置を求める際に必要な角度の値を0~360に収める関数
![2019-01-06_14h06_47.png](https://qiita-image-store.s3.amazonaws.com/0/104467/f6edd53f-df7b-9fa8-a184-c79292437e47.png)


##パラメータについて
イベント「Find Player Around Location」では**Make Literal Float**を使っています。
ここで指定している「45.0」や「500.0」という数値は次の図のような意味になります。
![2019-01-06_14h27_24.png](https://qiita-image-store.s3.amazonaws.com/0/104467/c77d5a1c-c349-5dfc-6d58-e91407de98a5.png)

**500.0**という数値は中心から円周？までの距離です。このとき中心位置はプレイヤーの座標なので「プレイヤーからどれほどの距離を離れるか？」という意味になります。
**45.0**はこういった言い方で正しいのかわかりませんが、円周上の頂点の間隔です。
45.0を指定しているので円周上には45度間隔で頂点が存在し、上で説明した中心から円周までの距離分、中心から引き離します。


#実際にゲームに組み込む場合の課題
プレイヤーを周回する位置をブループリントで求めてみましたが実際にこれをゲームに組み込む場合はパッと思いつく限り以下のような課題点があります。

1. 求めた目標位置が壁の向こうに設定されてしまう
2. 求めた目標位置が地面に埋まってしまっている（もしくは宙に浮いている）
3. 同様の機能を持つ敵キャラと位置がかぶってしまう

##壁の向こうに設定されてしまう
![2019-01-06_14h51_43.png](https://qiita-image-store.s3.amazonaws.com/0/104467/666a5e1e-32cd-c8ad-1006-288775661391.png)
例えば上のようにプレイヤーが壁際で一生懸命戦っている場合には目標位置が壁を挟んで向こう側に設定されてしまいます。
これを素直に実行してしまえば敵キャラクタはいきなりプレイヤーに背を向け壁の向こう側へと移動し、当たりもしない剣を一生懸命振り回します。

###解決案
この課題についての解決案として、
**現在位置から目標位置にLineTraceを飛ばし、現在位置と目標位置の間の障害物の有無をチェックする。障害物があればもう一度目標位置を計算し直す。**
です。
移動可能な位置を見つけるまで敵キャラクタはその場で佇んでしまいますがプレイヤーからしてみれば「攻撃の機会を伺っているのか？」と、うまい具合に誤解すると思いますので佇んでしまうことに対してはあまり問題にならないでしょう。

##目標位置が地面に埋まる（もしくは浮く）
![2019-01-06_15h07_15.png](https://qiita-image-store.s3.amazonaws.com/0/104467/15dc1d2b-9950-c75a-c615-3481bcd1e2fd.png)

目標位置を計算する際の基準点というのはプレイヤーにあるので目標位置の高さもプレイヤーと同じ位置になります。
よってプレイヤーが図のようなスロープ（もしくは階段）に居る場合、目標位置のいくつかは地面に埋まってしまったり宙に浮いたりします。

###解決案
このような問題については** Project Point To Navigation 関数**がバッチリ適用できます。
![2019-01-06_15h19_13.png](https://qiita-image-store.s3.amazonaws.com/0/104467/a4385cac-0ccd-a3d6-6128-dde0a6309e20.png)
これを機能させるには**Nav Mesh Bounds Volumeを必ず設置する必要があります。**
Project Point To Navigationについては次のツイートが参考になるかと思います。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">【ProjectPointToNavigation】<br>引数で渡したPointをNavMeshに投影し投影後の座標を返すナイスなノード。EQSのGeneratorにある「Pathing Grid」と同じことが出来る。<br>NavMeshが敷かれている場所に位置を補正する感じ。<br>実際に２枚めの画像右上の球体は本来階段に埋まっているが階段に沿っている。<a href="https://twitter.com/hashtag/ue4?src=hash&amp;ref_src=twsrc%5Etfw">#ue4</a> <a href="https://t.co/tKWEsNblUq">pic.twitter.com/tKWEsNblUq</a></p>&mdash; PavilionDV7 (@Dv7Pavilion) <a href="https://twitter.com/Dv7Pavilion/status/938429307034013697?ref_src=twsrc%5Etfw">2017年12月6日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Project Point To Navigation関数によって補正された新たな目標位置は次のようにきちんと地面に沿った位置になります。
![2019-01-06_15h21_29.png](https://qiita-image-store.s3.amazonaws.com/0/104467/328fc923-35c0-ad88-bd39-9bb4726f6012.png)


##同様の機能を持つ敵キャラと位置がかぶってしまう
この問題は少々厄介です。複数の敵キャラクタがプレイヤーを囲う場合出来るだけバラけてくれたほうがプレイヤーには大きくプレッシャーを掛けられます。
ですが、今回紹介した方法では目標位置に他の敵キャラクタが存在するかチェックしていないので、すでに敵キャラクタが居るにも関わらず同じ位置へと移動してしまい渋滞を起こすということが考えられます。

###解決案
この解決案についてはあまり自信がありませんが、
**Line Trace を目標位置から真上に飛ばす、**
という方法が考えられます。
求めた目標位置から真上にLineTraceを飛ばし敵キャラクタにヒットしたら、再度目標位置の計算を行うといった手順です。

#EQSはこちらを参考に
今回はキャラクタブループリントで実装しましたが
[スクウェア・エニックスにおける UNREAL ENGINE 4 を用いた人工知能技術の開発事例 33:47~](https://youtu.be/BV2GTGbSjq8?t=2027)
では、EQSを用いて同様の機能を実装しているようです。

EQSだと複雑な地形でもうまく動作しますし、変更もお手軽です。
個人的にはEQSでの実装をおすすめしたいところです。