【UE4】敵が発見するまでの時間を表現してみる

#はじめに
ステルスゲームでは敵キャラクターがプレイヤーを認識したとしても即座に「プレイヤー発見状態」に移らず、あえて「見なかった事にする」ことでプレイヤーに数秒間の猶予を与える、といったシステムが組み込まれています。

この「実際に発見されるまでの猶予時間」はゲーム中では、よくインジケーターやゲージで表現されます。

> ![tom-clancys-splinter-cell-blacklist-.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104467/e9570c2c-bc6d-f6fe-7b07-54c5f55125f1.jpeg)
> インジケータとゲージが組み合わさって視覚的に表現された発見猶予時間（赤枠）
> [Tom Clancy's Splinter Cell: Blacklist](https://www.ubisoft.com/en-us/game/splinter-cell-blacklist) より引用

そして、この猶予時間というのは敵キャラクターのプレイヤーとの距離で変動します。敵キャラクターの視野内ギリギリで見つかった場合は実際に発見したとされるまで10秒程掛かりますが、敵キャラクターの手が届く範囲で見つかってしまった場合は0.1秒程で実際に発見したとされます。

これをUE4で再現してみようというのが今回の記事の内容です。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">記事用（見つけるまでの時間をプログレスバーで表現するやつ）<a href="https://twitter.com/hashtag/UE4?src=hash&amp;ref_src=twsrc%5Etfw">#UE4</a> <a href="https://twitter.com/hashtag/UE4Study?src=hash&amp;ref_src=twsrc%5Etfw">#UE4Study</a> <a href="https://t.co/t4GiJpIWrl">pic.twitter.com/t4GiJpIWrl</a></p>&mdash; PavilionDV7 (@Dv7Pavilion) <a href="https://twitter.com/Dv7Pavilion/status/1156173345156554752?ref_src=twsrc%5Etfw">July 30, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#プロジェクトはこちら
作成バージョン UE4.22.3
https://1drv.ms/u/s!Au-8FqgREBKZiBv3Z4E12tWBifTy?e=AX2djI

#解説
##WBP_ThreatBar
最初に紹介するのは記事の中でも肝となるWBP_ThreatBarです。
「はじめに」の方で紹介した「実際に発見されるまでの猶予時間の視覚的な表現」は、このウィジェットブループリントで全て実行されます。
![2019-07-30_21h44_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104467/59161768-513a-4d53-310e-26ce06caa387.png)

---

###認識したら猶予時間を徐々に減らす
![2019-07-30_21h44_16_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104467/1b7a67e7-86f1-d5c0-3010-2fae81cc4e8b.png)

前半部分では **実際に発見するまでの猶予時間をパーセントに変換** する計算が行われます。
UE4のウィジェットブループリントにある **Progress Bar** やマテリアルでのゲージではパーセントでの数値指定になりますので、この計算は必須です。

####ベースの増加速度を求める
「Target / Time to target」とコメントされている部分では **変数CurrentThreatPercentが目標時間で「１」になるための増加速度** を求めています。
画像では「1 / 20」としていますが、これは「0~1に変化するまで20秒掛けるための増加速度」を求めていることになります。
この計算は小学校でならった距離・時間・速さの公式と同じです。

>目標とする数値 / 目標とする時間 = 目標とする速度

ここで求めた増加速度は **敵に発見された距離が丁度視野範囲と同じとき** の増加速度になります。
1/20のあとにデルタ秒を掛けていますが、これはどのフレームレートでも（30FPS、60FPSでも）きちんと20秒の時間を掛けれるようにするためです。

####敵との距離に応じた増加速度の調節
ベースの目標時間を求める計算の下では、敵との距離に応じて増加速度を変動させています。
Ease関数のAには基本の速度倍率が指定され、Bには最高速度倍率が指定されています。それぞれ「最も敵キャラから遠い、かつ視野範囲内の場合」、「最も敵キャラから近い、かつ視野範囲内場合」の速度倍率になります。

Alphaには **敵キャラとプレイヤーとの距離をパーセントで表した数値** が指定されます。この数値の計算はとても単純で

> 敵キャラとプレイヤーとの距離 / 敵キャラの視野範囲 - 1

で求めています。
-1をしているのは「敵キャラとプレイヤーとの距離 / 敵キャラの視野範囲」だけだと「距離が近いほど値が小さく、遠い程値が大きく」なってしまうので、その結果を反転するために-1しています。
これにより **「距離が近いほど値が大きく、遠いほど値が小さく」** なりEase関数のAlphaに渡す数値として最適なものとなります。

Ease関数を使うことでUE4が提供する様々な補間カーブを利用する事ができます。
これにより **目の良いキャラと目の悪いキャラで補間カーブを分けたり、昼間のステージと夜間のステージで分けたり** することで、よりリアルで柔軟な視野表現が出来ます。

###見失ったら猶予時間を徐々に増やす
![2019-07-30_22h35_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104467/42cde87d-8067-a0f6-8bba-a48e3dcc92c6.png)

猶予時間を徐々に減らすとは逆の計算をしています。
敵キャラクターは変数CurrentThreatPercentが0になると「完全に見失った」状態になります。
そしてCurrentThreatPercentは「5秒間かけて0になる」ように減少速度を求め、CurrentThreatPercentから引いていきます。

WBP_ThreatBarの解説はこれで終わりです。あとは敵キャラクターに「発見した」「見失った」「完全に見失った」場合の処理を読んだり、Progress BarやマテリアルにCurrentThreatPercentをセットしているだけです。

##AIC_NPC
AIC_NPCにはAI Perceptionコンポーネントを追加し、AI Perceptionが知覚する度にBP_NPCを通じてWBP_ThreatBarに「猶予時間に応じてProgress Barの数値を増やせ～」「Progress Barの数値を減らせ～」と指示します。

##BP_NPC
こちらはWidgetコンポーネントを追加してWBP_ThreatBarを表示させています。
いくつか関数を実装していますが、どちらもAIC_NPCの同名の関数を呼び出しているだけです。

#おわりに
発見までの猶予時間というのはゲームAIのリアルな振る舞いに大きく寄与するものだと思います。
どの距離でも見えれば即座に戦闘状態というのも悪くはないですが、猶予時間を設定することで「ん？何か見えたぞ...」とセリフやアニメーションを再生したり、適度な緊張感をプレイヤーに与えることが出来ます。

そろそろぷちコンの時期でもあります。実装も簡単なので、もしステルスゲームに挑戦されるかたは是非この記事を参考にしていただければ幸いです。