【UE4】L4DのAIDirectorを真似てメタAIを作ってみる その２

#続きものです
ここの内容は[こちら](http://qiita.com/Dv7Pavilion/items/cd263f844a56cb168180)の続きとなっておりますので併せてお読みください。

#ここでの内容
前回はEQSを用いて
- プレイヤーから遠い位置
- プレイヤーから見えない位置
- プレイヤーの元へとたどり着ける位置
の3要件を満たす敵の出現処理を実装しました。
今回は**プレイヤーの感情強度（Emotional Intensity）**というパラメータを用意して動的な出現頻度調整を実装していきます。

#必要な機能のリストアップ
動的な出現頻度調整処理に必要な機能をリストアップします。

－感情強度（Emotional Intensity）に基づく敵の出現頻度動的調整
－感情強度が上がる要因は以下の２つ
－－敵からダメージを受けた
－－プレイヤーの近くで敵が死亡した（プレイヤーが追い詰められている）
－感情強度が最大になると3～5秒間感情強度を最大に維持し続ける
－維持後は徐々に感情強度を下げていく（この間も要因による感情強度上昇あり）
－感情強度が下がりきった後の30～45秒間は敵の出現は行わない

L4DのAIDirectorには感情強度を元にした**４つのステート（状態）**があります。
**Build Up State** － 感情強度が上昇する状態
**Sustain Peek State** － 感情強度が最大になり数秒間それを維持する状態
**Peek Fade State** － 維持後の徐々に感情強度を減衰させる状態
**Relax State** － 感情強度が下がりきった後の数十秒間敵を出現させない状態
一見リストアップした機能は出現処理に比べると複雑に見えるかもしれませんが各機能は状態ごとに分ける事が出来ます。
列挙した各状態に合わせた処理を実装することで意外とシンプルに実装出来ます。

#Build Up State の実装
ここから各状態ごとに実装していきますが、その前に各状態を表す列挙型を用意します。
**コンテンツブラウザの新規追加 > ブループリント > 列挙型**を選択します。名前は**ENUM_PaceState**とします。
![27.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/e69a12b1-23c3-f04f-d0fc-283ce5371374.png)
ENUM_PaceStateを開くとエニュメレーターとあり右端に**新規ボタン**がありますのでボタンを**4回**押します。
すると以下のようになるはずです。
![28.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/24c59efe-0dc1-114e-23ea-522c67ab1a57.png)
上から順に名前を
**BuildUp**
**SustainPeek**
**PeekFade**
**Relax**
と変更します。
以上で各状態を表す列挙型の用意が出来ました。

次はAIDirectorのコントローラーブループリントを開きます。（前回で敵の出現処理を実装したブループリントです）
カスタムイベントを追加します。名前は**Update State**とします。
イベント Tickと繋げるために**イベントを OnQueryFinishedEvent とバインド**のピンを引っ張り**Update State**と検索し選択します。
それが出来たら**ENUM_PaceState型**の変数を作成します。名前は**PaceState**とします。
変数を作成出来たらPaceState変数をブループリント上にドラッグ＆ドロップして**ゲット**を選択します。
ゲットしたPaceStateのピンを引っ張り**Switch**と検索欄に打ち込みます。候補に**ENUM_PaceState をオン**と出ますのでそれを選択します。
![29.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/6c8b0812-eb99-b9ff-de36-b44b2a0f0703.png)
以下のようになればOKです。
![30.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/0865bed7-f0b3-ef33-b5e7-e34cc284f1db.png)

次はBuildUp状態の処理を実装するためにカスタムイベントの追加と必要な変数を追加していきます。
カスタムイベントの追加を選択し名前を**BuildUp**とします。
Float型の変数を２つ追加します。名前をそれぞれ**SurviverIntensity**、**MaxSurviverIntensity**とします。
![31.png](https://qiita-image-store.s3.amazonaws.com/0/104467/8f9ff4a1-ac34-5581-d9b6-7a325e0060e1.png)
MaxSurviverIntensityですが**デフォルト値**を**100.0**に変更してください。

さてBuildUp状態ですが、この状態の仕事は**上昇するプレイヤー感情強度の監視**という役目を担います。常に感情強度をチェックし、それが最大まで達すると**Sustain Peek状態**へと自身の状態を変更するというお仕事になります。
では、感情強度が最大まで達したらSustainPeek状態へと変化するようにします。
カスタムイベントBuildUpのピンを引っ張り**ブランチ**を選択します。
SurviverIntensityとMaxSurviverIntensityを2つとも**ゲット**して、SurviverIntensityのピンを引っ張り**>=**と打ち込み選択します。
>=ノードの余った所にMaxSurviverIntensityを繋ぎ、返り値ピンをブランチの**Condition**に繋ぎます。
ブランチノードの**真ピン**を引っ張り**Set PaceState**と打ち込み選択します。
新たにセットする状態は**Sustain Peek**です。
すると以下の様な形になるはずです。
![32.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/b6ede054-0697-96e9-a9e3-bc5cb83b0068.png)
以上でBuildUp状態の実装は終了です。内容はかなりシンプルですね。常にブランチでチェックしてTRUEなら状態を変えるというだけです。
それと忘れずにカスタムイベントUpdateStateの方にカスタムイベントBuildUpを呼び出すようにしてください。
![33.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/65dd895e-bbbc-fb62-30ae-cadb58540327.png)

##Intensityの上昇
BuildUp状態の実装が終わったので次は実際にIntensityを上げる処理を実装していきます。
Intensityが上昇する要因は以下の２つです。
- **敵からダメージをもらう**
- **敵死亡時のプレイヤーとの距離の近さ**
まずは敵からダメージをもらうとIntensityが上昇するという処理を実装します。
AIDirectorのコントローラーブループリントに新しく**関数を追加**します。名前は**AddIntensity**とします。
AddIntensity関数の実装は以下の通りです。
![34.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/04bca966-99f0-cad1-aa1f-c9faf51f2fba.png)
MinFloatを通すことでIntensityの最大値である100を超えないようにしています。
次にプレイヤーである**TopDownCharacter**ブループリントを開きます。
イベントグラフの下の方に**AnyDamage**イベントがあるはずです。
AnyDamageイベントが呼ばれるとAIDirectorのコントローラーブループリントのAddIntensity関数を呼び出すようにします。
![35.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/b8d67d68-61ec-ec74-cac2-cafb24e59787.png)
AddIntensity関数のValueを0から変更することを忘れないで下さい。
これでプレイヤーが敵からのダメージを受けるとIntensityが上昇するようになりました。
実際に敵が攻撃をする処理は下地プロジェクトを落とされた方なら既に実装されています。
実装内容は簡単で敵がプレイヤーに一定距離以内に近づくとApplyDamageを呼び出しているだけです。

次に敵死亡時のプレイヤーとの距離の近さでIntensityが上昇するという処理を実装します。
敵キャラのブループリント**BP_Infected**を開くとイベントグラフの下の方にOnComponentHitイベントがあります。幾つかの処理がピンで繋がっているので、一番最後のDeadノードから以下のように処理を追加します。
![36.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/c5748f22-dfb3-c79a-8fb8-4494011d1b58.png)
敵とプレイヤーの距離を反比例の式を通す事で**死亡時にプレイヤーに近いほど多くのIntensityが生じる**という処理が実装出来ます。

##Build Up状態の動作確認
BuildUp状態の実装と2つの要因によるIntensityの上昇の実装が出来ましたので動作確認をしてみます。
AIDirectorのコントローラーブループリントにPrintStringでIntensityの値とSustain Peek状態に遷移した時に「Peek !!」と表示するようにします。
![37.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/d68af0c4-29cd-1f80-1bdf-25989929ab42.png)
画像のように実装出来たら実際にプレイをして、動作確認をします。
敵が最接近すると値が1ずつ上がり、敵を倒した距離に応じて上昇値が変化します。
そしてIntensityが100に達した時画面に「Peek !!」の文字が表示されればOKです。
![38.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/6828d2ee-d30a-8801-9c60-ac3c223db046.png)

#Sustain Peek状態の実装
Sustain Peek状態はBuild Up状態で最大にまで達した感情強度を数秒間最大値を維持し続ける仕事をします。
感情強度が最大まで達した時L4Dでは50~100体ほどのゾンビが常にレベル上に存在するようにスポーンしプレイヤーを襲います。
こちらでも同じように感情強度がピークに達した時3~5秒間レベル上に常に50体は存在するようにスポーンさせるようにします。

Build Up状態の手順と同様に**カスタムイベントを追加**します。名前は**Sustain Peek**とします。ここで新しく変数を追加します。**Float型**で名前は**Peek Duration**とします。**デフォルト値**は**5**にしてください。PeekDurationはピーク状態の維持を何秒間続けるかを表す変数です。
イベントと変数が追加出来たら以下のように実装します。
![39.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/aaa9b82f-de59-6998-8b03-632bf3058f01.png)
Sustain Peekイベントに入るとまずはブランチを通ります。Peek Durationのデフォルト値は5なので真の処理に移行します。そこではSurviver IntensityをMax Surviver Intensityの値に置き換え1秒毎にPeek Durationを1ずつ減らすといった内容です。ちなみに**--ノード**は**Decrement**と打てば候補に上がります。
Peek Durationが0未満になるとブランチの偽に移行します。これはPeek Duration秒経過したということを意味しますのでPeek Durationにデフォルト値5をセットしてPeek StateをPeek Fadeにセットします。

続いて**常にレベル上に指定数の敵を存在させる出現処理**を実装します。
カスタムイベントを追加し名前を**Enemy Spawning**としてください。
そして以下のように実装します。
![40.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/d5402372-d53a-c767-ffae-6e5da64a9263.png)
内容は単純です。呼ばれる度に敵キャラを検索しその個数を指定したMax Spawnと比較します。
真であればMax Spawnから検索結果の個数分引き、不足分を求めてForLoopでSpawn AIFrom Classを呼び出し出現させます。

以上でSustain Peek状態とレベル上に常に指定数敵が存在するようにするという処理が出来ましたので呼び出し部分に追加していきます。
以下のようになります。
![41.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/395d962e-2aeb-fb96-404c-18cb05898577.png)
線で囲まれている部分が追加部分です。
Sustain Peekの処理の追加に伴ってBuild Up状態の後にもEnemy Spawningを呼び出しています。これによりOnQueryFinishedEvent_イベント0に書いていた敵の出現処理が不要になります。
![42.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/15bd0a35-0a98-1e3d-7ce6-7013f793a088.png)
線で囲まれていた部分は繋がっていましたが、それを切りました。

では実際にプレイしてみて動作確認をします。
初めは3体が連なってこちらに向かって移動し、攻撃をしてきます。
こちらが敵を倒したり攻撃を受けたりするうちにIntensityが上昇し最大まで達した時、一気に大量の敵がこちらへ向かってくるはずです。
![43.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/0fd03658-21da-813d-1164-fe0c4a6a11ca.png)
![44.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/90b5b2c9-412d-5908-fe00-7540b544760d.png)
AIDirectorはPeek時には5秒間敵の総数を最大に維持し続けるので、倒しても倒してもワラワラとプレイヤーの元へと集まってきます。しかしPeekが終わるとパタッと敵が出現せず最後にはレベル上に敵が存在しない状態になります。

#Peek Fade状態の実装
Build UpとSustain Peekを実装したことでなかなか面白いゲーム展開が出来たのでは無いでしょうか。しかし、これだけの内容だと敵は常に出現しプレイヤーを追いかけ続けてしまいます。倒しても倒しても無限に湧き続けるという状態はプレイヤーに対し大きなストレスを与えてしまいます。L4Dではそういった**大きなストレスを与え続けない**ようにしています。それが、これから実装するPeek Fade状態とRelax状態です。

ではPeek Fade状態を実装していきます。
Peek Fadeの内容はかなり単純です。Intensityが0になるまでIntensityを減算し続けるだけです。
まずはIntensityを減算する処理を作ります。関数を追加し名前を**SubIntensity**とします。内容は以下の通りです。
![45.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/23f7b66e-5e45-9347-5308-3690705b916a.png)
AddIntensity関数の実装と反対ですね。Max Floatを通すことでIntensityが0未満になることを防いでいます。
次にカスタムイベントを追加します。名前を**Peek Fade**とします。
内容は以下の通りです。
![46.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/5e0ce3da-4131-3b57-ffe9-a33f964fbced.png)
SubIntensityでIntensityを0.1ずつ減算し0以下になった時にPace StateをRelaxに遷移させています。
これでPeek Fade状態の実装が終わりましたので呼び出し部分に追加していきます。
![47.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/59333515-b779-36ca-48cf-5372b639c50a.png)
線で囲まれた部分が追加部分です。

実際に動作確認をしてIntensityの値を確認すると徐々に減っていくのがわかると思います。
Sustain Peekが終了した時点で敵の出現はさせていないので変化したのが実感しづらいですが、そんなものです。徐々に敵の出現数を減らすという処理を入れるとより実感出来るかもしれません。

#Relax状態の実装
Peek FadeでIntensityを減らす処理が出来ましたので次はIntensityが減りきった時の状態であるRelax状態を実装していきます。
Relax状態では指定秒数間は敵の出現処理を一切行なわず、プレイヤーの感情強度も上昇しません。
L4Dでも敵の出現がパタッと止んで静かになるタイミングがあります。その時レベル上にはAIDirectorが生成した敵とは別に事前にランダムで生成されているぼーっとしてたり道端に座り込んでいたりするゾンビが存在しているだけです。

ではRelax状態を実装していきます。
カスタムイベントを追加します。名前は**Relax**とします。続いて**Float型の変数**を追加します。名前は**Relax Duration**とします。
カスタムイベントRelaxは以下のようになります。
![48.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/18642a21-e6ae-ed54-62c6-a0881ea02e27.png)
Sustain Peekとほぼ同じ実装内容ですね。異なる点は使用する変数がRelax Durationになっている所とSurviver Intensityを常に0にセットする所、Pace StateがBuild Upにセットされている所の3点です。

Relax状態の実装が終わった所で呼び出し部分にも処理を追加します。
![49.PNG](https://qiita-image-store.s3.amazonaws.com/0/104467/517c65bf-d2d3-2a71-a1c9-695e203e70e8.png)
線で囲まれている部分が追加された部分です。

動作確認をするとPeekが過ぎた後しばらくの間敵の出現が止み、再度敵がポツポツと出現するというのを繰り返すようになります。

以上でプレイヤーの感情強度による動的な敵出現頻度調整の実装が終了しました。

#まとめ
[その１](http://qiita.com/Dv7Pavilion/items/cd263f844a56cb168180)ではEQSを用いて敵の出現位置を求め、ここでは敵の出現頻度を動的に調整する処理を実装しました。
これで最初のほうで定義した要件の2つを満たすメタAIが実装できました。
実際のL4Dとは仕様がかなり単純になっているので想像していた結果とは異なる結果となってしまったかもしれません。
L4Dを遊んでみるとよりダイナミックでドラマチックな内容となっていますのでプレイ動画を見るなり実際に買ってAIDirectorに注目しながら遊んでみるとより面白いかもしれません。

お疲れ様でした。