[UE4] VRでミニマップを表示する

# はじめに
VRのアプリを作成した時に、小さくて動き回るキャラクターを探すのが大変だったため、VR上で表示できるミニマップを作成してみました。UE4.25.1を使用しています。動作確認はOulusQuestで実施していますが、特にOulusQuestに依存する処理はないので他のヘッドセットでも多分問題ないです。完成イメージは以下動画のような感じです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ニワトリを探しやすくするためにミニマップを作成してみた<br>手の位置に表示するようにしたのだけれど、見ている景色の向きと地図の向きを合わせるのに苦労した <a href="https://t.co/tbbnWxhC3H">pic.twitter.com/tbbnWxhC3H</a></p>&mdash; 高浜 (@SatoshiTakahama) <a href="https://twitter.com/SatoshiTakahama/status/1281925059263688705?ref_src=twsrc%5Etfw">July 11, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 概略仕様
・ミニマップは左手のコントローラに固定表示
・ミニマップ上の表示の向きは、見ている景色の向きと一致させる
・ミニマップ上にはプレイヤーとキャラクター(今回はニワトリ)を表示する
・キャラクターの表示は、スポーン時に追加、消滅したときに削除されるようにする

# 作り方
## 準備
・プロジェクトはVRテンプレートを使用します。[前回記事](https://qiita.com/Satoshi_Takahama/items/092f2145f31d8ee8143c)でキャラクターを追加しているので、その状態から開始する前提とします。

## GameModeのBP作成
今回、GameModeは、ミニマップへの参照を手のコントローラーに渡すために使用します。
・コンテンツブラウザ上で、新規追加=>ブループリントクラスを選択するとダイアログが表示されるので、親クラスとしてGameModeを選択しOKを押します。
・生成されたBPの名前をBP_GameModeMiniMapに変更し、編集画面を起動します。
・BP_GameModeMiniMapに変数を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/e11ed4a3-dcde-f025-a27f-b92441529360.png)
・ワールドセッティングでBP_GameModeMiniMapを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/05b3d66d-43da-d510-48b5-70234bd5d80a.png)

## RenderTarget作成
ミニマップに映す画像の置き場所としてRenderTargetを作成します。
・コンテンツブラウザ上で、新規追加=>マテリアル・テクスチャ=>描写ターゲットを選択して生成し、名前をRT_RenderTargetに変更します。

## SceneCapture2DのBP作成
ミニマップに映す画像を取得するカメラを作成します。
・コンテンツブラウザ上で、新規追加=>ブループリントクラスを選択するとダイアログが表示されるので、親クラスとしてSceneCapture2Dを選択しOKを押します。
・生成されたBPの名前をBP_SceneCapture2Dに変更し、編集画面を起動します。
・CaptureComponent2Dの設定を以下のように変更します。OrthoWidthの値はミニマップに映す範囲に影響するので、範囲を変更したい場合は調整してください。この値は後でミニマップにアイコンを配置するときの計算に使用するので、変更する場合はそちらも合わせてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/5498dddd-6722-ffc7-7618-0c8785c858b8.png)
・BP_SceneCapture2Dに変数を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/6660e671-bd61-99a3-29f1-c5dd10795666.png)
・BP_SceneCapture2Dに以下のようにノードを追加します。起動時にタグをつけたPlayerActorへの参照を取得し、先ほど作成した変数に格納する動作になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/c66d4a30-b260-5f3b-a32f-22188a2f401f.png)
・さらにBP_SceneCapture2Dに以下のようにノードを追加します。こちらはPlayerActorの上空に配置したカメラの位置を追従させる動作になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/07fc19da-52ff-1b79-d712-a334b3662734.png)
・アウトライナでPlayerActorを選択し(VRテンプレートで名前を変更していなければVRPawnです)、タグをつけておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/9ffc94d2-b9b7-5d8c-d5ba-a006731e912d.png)

## Material作成
・コンテンツブラウザ上で、新規追加=>マテリアルを選択して生成し、名前をM_MiniMapに変更します。
・M_MiniMapの編集画面を起動し、マテリアルの設定を変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/8baff4c4-c64b-daad-c49f-80640a3f65ef.png)
・M_MiniMapに以下のようにノードを追加します。カメラから取得した画像RenderTargetを円形にくりぬいて、周囲に黒い縁を作成する流れになります。画面上でLerpと表示されているノードは実際にはLinear Interpolateという名前なのでご注意ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/8dfaf77e-11e1-a0ed-7fe8-a048379d3196.png)
・TextureSampleノードにRenderTargetを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/7af4db48-36ef-1a7f-35bc-fee7c0b7892e.png)

## ミニマップのWidgetの作成
・コンテンツブラウザ上で、新規追加=>ユーザーインターフェース=>ウィジェットブループリントを選択して生成し、名前をWBP_MiniMapに変更します。
・WBP_MiniMapの編集画面を起動します。CanvasPanelの子要素としてOverlayを追加、Overlayの子要素としてImageを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/66fe5606-8e51-836e-a496-8793cfb9bddd.png)
・Overlayの位置とサイズを調整します。また、ミニマップの画像が反転されているのを戻すため、Scaleの設定も変えます。グラフから参照できるように、IsVariableにチェックを入れておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/01f62d87-fc37-6858-285c-85753fe5ed1b.png)
・ImageにM_MiniMapを設定してミニマップの画像が表示されるようにし、あとはサイズを調整します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/3c6bfe80-a235-d4b0-7cfa-8f6b607dc652.png)

## アイコンの作成
ミニマップ上でプレイヤーとキャラクターの位置を示すアイコンを作成します。
・アイコンに使用する画像は、ペイント3D等で作成しPNG形式のファイルを用意しておきます。プレイヤー用とキャラクター用の２種類を準備し(それぞれのファイル名はPlayerとPOIとします)、作成したファイルをエクスプローラーからコンテンツブラウザ上にドラッグ＆ドロップして、UE4で使用できるようにします。
・コンテンツブラウザ上で、新規追加=>ユーザーインターフェース=>ウィジェットブループリントを選択して生成し、名前をWBP_PlayerIconに変更します。
・WBP_PlayerIconの編集画面を起動します。CanvasPanelを削除しOverlayを追加、Overlayの子要素としてImageを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/d17b7529-362e-4628-0079-66d39fb6a588.png)
・ImageにPlayerを設定してプレイヤーのアイコンが表示されるようにし、あとはサイズを調整します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/e1f39941-8b2d-d144-a348-9eb248756663.png)
・コンテンツブラウザ上で、新規追加=>ユーザーインターフェース=>ウィジェットブループリントを選択して生成し、名前をWBP_POIに変更します。
・WBP_POIの編集画面を起動します。CanvasPanelを削除しOverlayを追加、Overlayの子要素としてImageを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/1ebec5cb-988a-ce83-c854-9730c51aeba7.png)
・ImageにPOIを設定してキャラクターのアイコンが表示されるようにし、あとはサイズを調整します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/95fd0842-060d-9820-4100-2e0f302286a1.png)

## ミニマップにアイコンを追加
・WBP_MiniMapの編集画面を起動し、画面右上のグラフをクリックしてWBP_MiniMapの編集モードを切り替えます。
・WBP_MiniMapに変数PlayerIconWidgetRef、PlayerActorRef、POI_WidgetRefArrayを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/2eff1de8-bdcc-d2f5-5222-66361b0f59d1.png)
・WBP_MiniMapに関数AddPlayerIconを追加し、ノードを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/28cc245d-6352-0c5f-9d09-e0175af13215.png)
・WBP_MiniMapに関数AddPOIを追加し、インプットを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/76fcb02b-9f26-f0ea-f85e-2cedf53da834.png)
・AddPOIにノードを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/246518bb-a640-20a5-c4cd-ba62a8f2c829.png)
・WBP_MiniMapのイベントグラフにノードを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/d0ab9dd1-cf32-6504-2513-ef47391a1254.png)

## ミニマップ上のアイコンをキャラクター位置に合わせる
・WBP_MiniMapに関数CalcDistAngleを追加し、インプットとアウトプットを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/b83e3837-aeb2-e664-7ea0-1a6a368ff0b8.png)
・CalcDistAngleにノードを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/90b9cc00-aea5-728f-28a0-f69039320a37.png)
・WBP_MiniMapに関数ConvertToMiniMapDistを追加し、インプットとアウトプットを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/f91e05e9-0549-e321-3280-5f2e19d882d3.png)
・ConvertToMiniMapDistにノードを以下のように追加します。500は"ミニマップのWidget"の作業で設定したミニマップの画像サイズ、100000は"SceneCapture2DのBP作成"の作業で設定したOrthoWidthの値なので、設定を調整するときは合わせる必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/13df6cf9-9adb-6494-0dfa-030210321c13.png)
・WBP_MiniMapのイベントグラフにノードを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/bf2e5d87-9f58-d4c9-783f-db887cc8580f.png)

## キャラクター生成時にアイコンを追加
・コンテンツブラウザ上で、新規追加=>ブループリントクラスを選択し、表示されるダイアログで親クラスとしてActorComponentを選択してOKを押します。
・生成されたBPの名前をAC_POIに変更し、編集画面を起動します。
・AC_POIにノードを以下のように追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/85874183-6765-1bdf-309b-027dbf2e0173.png)
・キャラクターのブループリントAI_ChickenCharacter(これは前回記事で作成したものですが、マップに表示したいキャラクターを選んでください)に新規コンポーネント追加でAC_POIを追加します。

## 左手のコントローラにミニマップを追加
・コンテンツブラウザ上で、新規追加=>ブループリントクラスを選択し、表示されるダイアログで親クラスとしてBP_MotionControllerを選択してOKを押します。
・生成されたBPの名前をBP_MotionController_Leftに変更し、編集画面を起動します。
・新規コンポーネント追加でScean ComponentとWidget Componentを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/fc28d8de-8d14-4efc-44ca-2ac5b4f6fbdb.png)
・Scean Componentは位置調整のため以下のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/a2320e7a-db7e-02b9-984f-ae7fa8a8bc14.png)
・Widget Componentには以下のようにミニマップのWidgetを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/638c145b-a466-6e04-bd7b-70ebbea08c20.png)
・BP_MotionController_Leftのイベントグラフにノードを以下のように追加します。これはミニマップの向きを調整するためのものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/1cd17bcf-7911-a946-27e2-d1a969c59b6f.png)

## プレイヤーの設定
・プレイヤーのBP(VRテンプレートで名前を変更していなければVRPawnです)の編集画面を起動します。
・イベントグラフで左手のコントローラを生成している箇所を探して、BP_MotionControllerからBP_MotionController_Leftに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/b79adade-0952-2f63-f84e-08afe556e930.png)
・新規コンポーネント追加でBP_SceneCaputure2Dを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/0c8f5967-19e8-73f0-4e34-77d14f7249ca.png)
・BP_SceneCaputure2Dの設定を以下のようにしてカメラの向きと位置を調整します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/cbf292a4-0a7d-020b-6eaf-d8e3bd9bea91.png)

## 完成
ここまで作成した状態で実行すると、VR上でミニマップが表示されているはずです。

# 参考資料
・[[UE4] SceneCapture2DでMinimapを作成する]
(https://qiita.com/kanurapoison/items/1a192227b3b44bd42562)
ミニマップの作り方はこちらを参考にさせていただきました。
・[[UE4]Widget Interactionコンポーネントを使ってVRで触れるUIを制作してみる！]
(https://historia.co.jp/archives/5895/)
手のコントローラーにWidgetをつける方法はこちらを参考にさせていただきました。