Unreal Engine 4 でEQSを使えるようになる

#はじめに
###投稿者の自己紹介
どうもまぬおです！UE4歴半年ちょっとの初心者です。よろしくお願いします
Qiita初投稿ですので、暖かい目でお願いします

###お知らせ
[Unreal Engine 4 (UE4) Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ue4)
この記事はUE4のアドベントカレンダーの7日目の記事です。ほかの方の投稿もぜひご覧になってください

###この記事について
この記事は、UE4にてEQSを使用できるようになるために基本的事項を解説する記事です
ボクもEQSのすべてを理解しているわけではないですが、皆さんの手助けになればと思います
また、実践というよりはEQSについて説明する内容になっています。よろしくお願いします

#各種バージョン
###UE4のバージョン
Unreal Engine 4.22.3

#EQSとは
EQSとは(Environment Query System)の略で、動的な環境においても適切な行動選択が行えるように提案されたシステムのことです

例えばAIにプレイヤーから見えない位置に位置取りしてほしいという場合に、マップ全てのプレイヤーの位置について隠れている場所を事前計算するのは非効率かつ不可能です
そういう場合にEQSの仕組みが有効に働きます
ランタイム上でリアルタイムの位置関係から有効な位置を計算するので、調整次第でどんな位置関係においても対応ができるというわけです

#EQSの仕組み
EQSは大きく分けて、ジェネレーターとテストの二つで構成されています
ジェネレーターがそれぞれの法則に従ってマップ上に候補地を置き、テストがそれぞれの候補地について点数を付けます
テストが点数をつけ終わった最終的な点数結果を鑑みて座標などを出力します
この仕組みが肝なので、画像付きでもう少し詳しく説明します
![ジェネレーター](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/78083b4f-355d-d0e0-1440-96a2aac3c3b0.png)
ジェネレーターにもいろいろな種類がありますが、この画像のジェネレーターはプレイヤーの前後左右に候補地を置くものです
ジェネレーターが候補地を置き終わったら、次はテストの出番です

ここでは二つのテストを行います。
![TutorialTest1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/e868ce58-5690-259c-018f-93a532506228.png)
まず前方向が点数の高くなるテスト(画面上が前方向です)
![TutorialTest2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/9091ec28-8757-4a9e-7f01-05f6e752833d.png)
そして、プレイヤーの目線の通らないところを省くテスト
![TutorialResult.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/545dfe2d-1a47-e224-9df5-fe3f09d84c42.png)
この二つのテストの点数を足し合わせた結果、プレイヤーの前のアイテムが選ばれることになります！

……わかりました？
簡単に言うと、リストアップしてその中をいろいろな方法で比べるって感じです。その理解で基本は万事OKです。

#UE4でのEQS
さて、実際にUE4で使ってみましょう！
といっても、実はEQSはプロジェクトを作った段階では利用できません…
設定から有効化する必要があります
![EditorPreferences.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/05099e1a-4991-c7af-0c95-100e476861fa.png)
左上のEditタブからEditor Preferenceを開きます
![EditorSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/d6ca38a8-0061-46e2-25e4-b71fbc60e989.png)
検索欄に"EQS"と打ち込み、出てきた"Environment Querying System"をONにします
![AddNew.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/ed171867-5324-b169-c808-6b27928ac9cd.png)
これでAddNewからEQSアセットを追加できるようになります
![EQSGraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/88ed450d-2a49-9857-e600-32c07b0fa95a.png)
作成したアセットを開くと、こんな画面が出てきます
ビヘイビアツリーと似ていますよね。EQSもビヘイビアツリーと同じようにノードベースで組んでいくシステムなのです
![CreateGenerator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/abf3097d-e311-7bce-a89f-413825fb2cfa.png)
ビヘイビアツリーと同じようにRootの下から矢印を引っ張ると、ジェネレーターを選ぶウィンドウが出てきます
ここで状況にあったジェネレーターを選択するわけです。ここでは"Points: Circle"を選びました
![CreatedGenerator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/660db6b3-6fda-89c0-b265-8c0da67ab241.png)
これでCircleのジェネレーターノードを追加できました。次にここにテストを追加します
![ChooseTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/5650d566-d939-241a-6b2b-cee6978330d9.png)
ジェネレーターノードを右クリックして、"Add Test"の項目から追加したいテストを選びます。ここでは"Dot"を選びました
![AddedDotTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/65691eca-822c-07e9-0147-fd2c3e9b3420.png)
テストを追加すると、このようにジェネレーターノードの中に追加したテストが表示されます
また、テストは複数追加することが可能です。必要なテストをどんどん追加しましょう！

---
基本的な操作はこれだけで終了です。簡単ですね！
ここからは具体的にそれぞれのジェネレーターとテストについて詳しく解説していきます

#ジェネレーター
今回紹介するジェネレーターは三種類。これだけ覚えておけば基本的なものはいけます。(というかCircleだけで大体いける)
###Circle
円状にアイテムを配置するジェネレーターです
![Circle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/e76e0428-57a2-ba6c-8305-c5c885908696.png)
こういうイメージです
![CircleDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/45879609-4c7c-1d17-cfe6-27fd60bc3079.png)
設定項目を説明します(よく使う項目を太字にしています)

- **Circle Radius** : 円の半径を指定します
- **Space Between** : 円状に置く方法のうち、アイテムの **幅** を指定して置く方法で使う数値です
- **Numver Of Points** : 円状に置く方法のうち、アイテムの **数** を指定して置く方法で使う数値です
- **Point On Circle Spaceing Method** : 円状に置く方法を選択します
- **┗ By Space Between** : 幅を指定して置く方法を選択します
- **┗ By Space Between** : 数を指定して置く方法を選択します
- Arc Direction : 円を置き始める方向を指定します
- Arc Angle : 円の角度を指定します
- **Circle Center** : 円の中心を指定します。Contextというもので指定するのですが、Contextはややこしいのでまた別の機会に説明したいと思います
- Ignore Any Context Actors when Generating Circle : 円を生成するときにコンテキストのアクターを無視するらしいです:thinking:(ボクは使ったことありません…わかる方はぜひ教えてください)
- Circle Center ZOffset : 生成するアイテムの高さのオフセットを指定します。地面からの高さを指定したいときに使います(目線とか)
- Trace Data : 中心からトレースして位置を調整する項目です
- Trace Mode : トレースする先のジオメトリを指定します
- ┗ None : トレースを行いません
- ┗ Navigation : ナビゲーションメッシュ上でトレースを行います。ナビメッシュのエッジでぶつかります
- ┗ Geometry : 地形上でトレースを行います。何かの障害物にぶつかります
- ┗ Navigation Over Ledge : おそらくナビゲーションと地形を両方考慮するものだと思われます
- Projection Data : 上から投影する先を指定する項目です
- Trace Mode : トレースする先のジオメトリを指定します
- ┗ None : トレースを行いません
- ┗ Navigation : ナビゲーションメッシュ上でトレースを行います。ナビメッシュのエッジでぶつかります
- ┗ Geometry : 地形上でトレースを行います。何かの障害物にぶつかります

###Donut
ドーナツ状にアイテムを配置するジェネレーターです
![Donut.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/a665fa4a-e7e8-4837-f762-329ce9d39ec8.png)
こういうイメージです
![DonutDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/e5d2a8a0-aeee-9851-d838-171766812a90.png)
設定項目を説明します(よく使う項目を太字にしています)

- **Inner Radius** : 内円の半径を指定します
- **Outer Radius** : 外円の半径を指定します
- **Number Of Rings** : 内円と外円の中にいくつ円を配置するかを指定します。1を指定すると内円の半径に一つ配置されます
- **Points Per Ring** : 円上にアイテムをいくつ置くかを指定します
- Arc Direction : 円を置き始める方向を指定します
- Arc Angle : 円の角度を指定します
- Use Speral Pattern : 渦巻き状になるように円を少しずつ回転して配置します
- **Circle Center** : ドーナツの中心を指定します。
- Projection Data : 上から投影する先を指定する項目です
- ┗ Trace Mode : トレースする先のジオメトリを指定します
- ┗ None : トレースを行いません
- ┗ Navigation : ナビゲーションメッシュ上でトレースを行います。ナビメッシュのエッジでぶつかります
- ┗ Geometry : 地形上でトレースを行います。何かの障害物にぶつかります

###Grid
グリッド状にアイテムを配置するジェネレーターです
![Grid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/d4faf5a1-e68d-4fc7-7951-ad007d6bd102.png)
こういうイメージです
![GridDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/67e27f78-c86a-9a78-cd66-4091d1cc0dfb.png)
設定項目を説明します(よく使う項目を太字にしています)

- **Grid Half Size** : グリッドの一辺の半分の長さを指定します
- **Space Between** : アイテムの間隔を指定します
- Generate Around : グリッドの中心を指定します。これもまたまたContextで指定します
- Projection Data : 上から投影する先を指定する項目です
- ┗ Trace Mode : トレースする先のジオメトリを指定します
- ┗ None : トレースを行いません
- ┗ Navigation : ナビゲーションメッシュ上でトレースを行います。ナビメッシュのエッジでぶつかります
- ┗ Geometry : 地形上でトレースを行います。何かの障害物にぶつかります

---
ジェネレーターによってアイテムの配置が大きく変わるので、状況にあったジェネレーターを選択しましょう
また、今回紹介したジェネレーターは三つですが、他にもたくさん用意されています。機会があれば紹介したいと思います。

#テスト
今回紹介するテストは三種類。これだけ覚えておけば即戦力です!

###共通設定
具体的なテストの説明に入る前に、テストには共通で指定する項目があるのでまずそれを説明します

---
####Test
![CommonTestDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/aacf7636-e4bd-ad04-9e2e-2555c38af4f6.png)
"Test Purpose"という項目はすべてのテストにあり、これによってテストの属性を決めることになります。
属性には"スコア"と"フィルター"の二つがあります。スコアが点数を加算する属性、フィルターがアイテムを選考から除外する属性です。

- **Test Purpose** : テストの属性を指定します
- **┗ Filter Only** : フィルターの属性のみをテストに付与します
- **┗ Score Only** : スコアの属性のみをテストに付与します
- **┗ Filter And Score** : フィルターとスコアを両方の属性をテストに付与します

---
####Filter
属性にFilterを含む属性を指定したときに出てくる項目です
点数型とBool型があります
![CommonFilterDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/539cecc4-230c-9ee6-3399-4ffe78bc9922.png)
これが点数型の設定です

- **Filter Type** : 除外の方法を指定します
- **┗ Minimum** : 最低値未満の点数のアイテムを除外します
- **┗ Maximum** : 最高値より上の点数のアイテムを除外します
- **┗ Range** : MinimumとMaximum両方を使用し、指定範囲外のアイテムを除外します

![CommonFilterDetailBool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/a9921ea5-c8b8-3d0d-331b-3b079d9a80b1.png)
これがBool型の設定です

- **Bool Match** : 除外するテストの結果の種類を指定します。例えばTrueの場合はテストの結果がTrueのものが除外されます

---
####Score
属性にScoreを含む属性を指定したときに出てくる項目です
これも点数型とBool型があります
![CommonScoreDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/781de5dd-eb7a-e706-e625-7cf95f8a29d1.png)
これが点数型の設定です

- Clamping : 指定した最小値と最大値でClampすることができます
- **Scoring Equation** : 点数の増え方を調整します
- ┗ Constant : 点数が"Scoring Factor"の数値になります
- **┗ Linear** : テストによる点数そのままになります
- ┗ Square : テストによる点数を二乗した数値になります
- **┗ Inverse Linear** : テストによる点数を1から引いた数値になります
- ┗ Square Root : テストによる点数を二分の一乗した数値になります
- **Scoring Factor** : テストの点数にかける重みの数値です。大きくすることでそのテストの重要度を上げることができます。
- Normalization Type : 点数を正規化する方法を選びます

![CommonScoreDetailBool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/4b7ddfc8-f4fa-eb77-cea7-f87c035e3678.png)
これがBool型の設定です。Bool型は点数がつかないため、Trueが1.0、Falseが0.0として点数付けされます

- **Scoring Factor** : テストの点数にかける重みの数値です。大きくすることでそのテストの重要度を上げることができます。

---

ここから各テスト固有の特徴について説明します
###Dot
内積を使用して、指定した方向との角度によって点数を付けます。点数型のテストです
![DotTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/4ae6444b-7962-fb5d-a8ab-1a037e5ffe4d.png)
イメージはこんな感じです。画面上方向の点数が高くなっています。
![DotTestDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/9a304e80-c010-4ae7-829a-f7368269cad8.png)
設定項目を説明します(よく使う項目を太字にしています)

- **LineA** : 内積の計算で基準となるベクトルです。これに近いほど1.0に近づきます
 - **Mode** : ベクトルを算出する方法を指定します
 - **┗ Two Points** : 二つの座標からベクトルを算出する方法です
 - **┗ Rotation** : 指定したコンテキストのアクターの前ベクトルを使用する方法です
- **LineB** : 内積の計算で対象となるベクトルです。アイテムへのベクトルを指定するのが一般的です
 - **Mode** : ベクトルを算出する方法を指定します
 - **┗ Two Points** : 二つの座標からベクトルを算出する方法です
 - **┗ Rotation** : 指定したコンテキストのアクターの前ベクトルを使用する方法です
- **Test Mode** : 内積計算を行う空間を指定します
- **┗ Dot (3D)** : 3次元空間で内積計算を行います
- **┗ Dot 2D(Heading)** : Zを無視してXY平面上で内積計算を行います
- **Absolute Value** : 絶対値を取って点数とするかどうかを指定します。これをTrueにすると、後ろの方のアイテムの点数も高くなります

###Distance
アイテムとの距離によって点数付けをします。点数型のテストです
![DistanceTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/c495c4d4-c38d-4909-c131-38e0256e7eb1.png)
イメージはこんな感じです。中心からの距離に応じて点数が変わっているのがわかります
![DistanceTestDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/ac1c48bb-d716-8cc3-4ddf-504899e0be33.png)
設定項目を説明します(よく使う項目を太字にしています)

- **Test Mode** : 距離を測定する方法を指定します
- **┗ Distance3D** : 3D空間上で距離を測定します
- **┗ Distance2D** : Zを無視してXY平面上で距離を測定します
- **┗ DistanceZ** : Z軸上で距離を測定します。いわゆる高さです。アイテムより低い場合は0になります
- **┗ DistanceZ(Absolute)** : Z軸上で距離を測定します。いわゆる高さですが、絶対距離で測定するので低い場合も距離の値はプラスになります
- **Distance To** : 距離を求める先のContextです。たいていの場合円の中心になります。

###Trace
トレースチェックを行い、ヒットした場合にTrueとするテストです。Bool型のテストです
![TraceTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/1f6921a2-9091-9439-2046-be1eebdb16b6.png)
イメージはこんな感じです。左のアイテムは壁があり、円の中心からのトレースが当たるのでTrueになっています
![TraceTestDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/87521c09-89c8-2cec-f2e5-4d08bf70e68a.png)
設定項目を説明します(よく使う項目を太字にしています)

- **Test Data** : トレースする条件を指定します
 - **Trace Channel** : トレースを行うチャンネルです。エディター設定の"Trace Channel"に設定したチャンネルが使えますデフォルトでは"Visibility"と"Camera"があります
- Trace from Context : トレースの方向を逆方向にします。裏ポリがないポリゴンを裏から貫通させたい場合などに使います
- **Context** : トレースする先のContextを指定します。たいていの場合円の中心になります

---
実際に組み込む時には、これらのテストを組み合わせてEQSの挙動を定義することになります
また、今回紹介したテストは三つですが、他にもたくさん用意されています。機会があれば紹介したいと思います。
#まとめ
いかがでしたか？
内容が膨れあがりすぎて収拾がつかなくなっていますが、ここまで読まれた方ありがとうございました！
ここで紹介したEQSの要素は氷山の一角で、まだまだ面白い要素がたくさんあります
今回紹介した3種類ずつのジェネレーターとテストでも、想像力と数値の設定次第で多種多様なEQSを作れますので、ぜひお手元のUE4で試してみてください！

#おわりに
###投稿者の感想
Qiitaは今まで記事を閲覧するだけで自分で書いたことはありませんでしたが、EQSの知見が溜まっていたのと
[Unreal Engine 4 (UE4) Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ue4)の開催を知っていい機会だと思って初投稿と相成りました。
EQSについてはまだまだ書き足りないので、この記事の評判が良かったら続くかもしれません

###お知らせ
[Unreal Engine 4 (UE4) Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ue4)
冒頭でもふれたように、この記事はUE4のアドベントカレンダーの7日目の記事になります。
明日は[Dv7Pavilion](https://qiita.com/Dv7Pavilion)さんの記事です！GOAPAIについて書かれるということで、個人的にとても興味がある内容です！楽しみにしましょう！