UE4でGPSを絡めたARアプリをつくる

#はじめに
大学の授業の関係でGPSベースのARアプリをUE4で作る機会があったので、技術的な話を共有したいと思います。
とはいえ、自分もUE4もそこまで長い期間触ってるという訳でもないですし、そもそもAR云々の前にスマホアプリを作るということが初めてですし、これがQiita初投稿でもあるので、あんまり役に立つかはわかんないんですが、
**UE4, AR, GPSの情報無さすぎだろ！**
となったので、初心者なりにつまづいたところに少し力をいれて、他の方の良記事を紹介しつつ(頼りまくって)、完成までの一通りのステップを書いてみることにみました。
ただ、もしかしたら間違った情報もあるかもしれません。そしたらすみません。
UE4の基本的な用語とか概念の段階で自分もちょくちょくつまずいたんですが、話してるとキリがないので、ここでの説明は省きます。頑張ってください。
 
環境は Windows10, UE4.26
テスト端末は Google Pixel 3 , Android 10
#目次
1. 作成したアプリの概要
2. Handheld AR テンプレート
3. プロジェクト設定
4. プラグイン
5. GPS
6. その他のブループリント

#1. 作成したアプリの概要
↓ 最終的にできたのはこんなのです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">初AR！初Qiita！<br>なのでTwitterでも言わせて！<a href="https://t.co/MB3KvUfjRb">https://t.co/MB3KvUfjRb</a> <a href="https://t.co/ECFRopiTLR">pic.twitter.com/ECFRopiTLR</a></p>&mdash; なかむらた (@isutothukue) <a href="https://twitter.com/isutothukue/status/1339896992143556609?ref_src=twsrc%5Etfw">December 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

2か月のうちに自然とのインタラクティブアートというテーマで何でもいいのでグループで1つ作ってください！
という授業がありまして、10月にグループを分けれられ、4人で何かをつくることになったわけです。
他の3人は芸術系で、僕だけ工学系？でした。
紆余曲折の後、**普段は見れない微生物をスマホ越しにARっぽく見れたらいいよね！**と他の人が提案した案が採用され、ARアプリをつくる感じになりました。
自分がUE4の練習をしたかったので、プログラムは自分がやる！と言い、デザイン(UIとかキャラデザ)は他3人に任せました。

↓ フローチャートもどきの段階ではこんな感じのイメージ
![CF9F4DA0-9D5D-48CA-82F9-8C5F736B2063.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/6309dd36-ccfb-0c92-f24a-e5bfaf03f2af.png)

40m×40mくらいの大きさの広場内を歩き回って微生物を探そう！というコンセプトだったので、GPSを利用して、予め指定した緯度経度の数値に近ければキャラクターを表示するということを目指しました。
微生物キャラクターを10体つくってくれて、連続したPNG画像をフリップブックにして2Dアニメーションの形で画面上にスポーンします。
図鑑登録に登録されたキャラはタップするとキャプションが見えるようにもしました。

#2. Handheld AR テンプレート
**テンプレートがマジでありがたかった**
最初の時点ではBPだけでは厳しいのかな？？初級者卒業レベルの僕だけど、C++も書かなきゃかな？とか思ってたのですが、全然BPで収まりました。
(結局細かいBPを理解しきらずとも、なんかやりたいことができました)(理解しきってないで記事書いてます...)

最初は自分もひたすらテンプレートをいじりました
https://docs.unrealengine.com/ja/SharingAndReleasing/XRDevelopment/AR/HandheldAR/ARQuickStart
https://docs.unrealengine.com/ja/SharingAndReleasing/XRDevelopment/AR/HandheldAR/ARHowToHitTesting
Handheldテンプレートと同じことを０から作ってくれてる下の動画もだいぶ勉強になりました。英語ですが
https://www.youtube.com/watch?v=uo16N3W-nf8

#3. プロジェクト設定
最初の時点で、iOSとAndroid両方でビルドするつもりだったんですが、iOSのビルドでつまづいたのでDeveloper登録もしたのに結局諦めました。

iOSのを作りたい人は頑張って下さい。↓ そらまめさんの記事が公式よりもわかりやすい説ある。
https://soramame-games.com/ios-win-cpp

Android : 基本公式に従ったら僕はOKでした。
https://docs.unrealengine.com/ja/SharingAndReleasing/Mobile/Android/GettingStarted

※CodeWorksforAndroidをダウンロードするのは、プロジェクトファイルからではなく、プログラムファイルからですよ！ C:\Program Files～～ で続くやつ。
#4. プラグイン
・AR関連
Builtin- Angmented Realityの場所
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/9d9b3fde-ad34-e0eb-152a-058e94fe0a34.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/a7fcb4fa-a0ff-934d-b22e-97954d8f2146.png)

・GPS関連
mobileと検索して出てきたの3つ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/9366199c-bace-ee9e-2ee9-420ec83a2ba5.png)

#5. GPS
GPSに関してはこのページがだいぶ参考になった。
https://wspri.plusload.net/?p=304

Handheld ARサンプルは**BP_ARPawn**で基本的にはブループリントを書いてたので、自分もそれに乗っかってGPSのこともBP_ARPawnに書くことにします。(GPS関連のBPならレベルブループリントとかに書いても多分大丈夫だと思います。)

- イベントBeginPlayにて`Init Location Services`,`Start Location Services`ノードでGPSを初期化して端末のGPS機能を呼び出す。(Initノード内の Accuracyで精度, Update Frequencyでミリ秒単位での更新頻度を選べます。Min Distance Filterは公式からも説明がないのでわかりません！)

- イベントTickでも他のところでもいいですが、`Get Location Services Impl`ノードで実装オブジェクトを返す。(ImplとはImplementationの略 = 実装, 実行)

- で結局何を実装するのというと、バインドしたカスタムイベント(OnLocationChanged)を実装するわけです。(これはスマホのGPS機能内を取得するイベントというかクラスというかを参照してるってことですよね？認識が違ってたら申し訳ないです。自信はないです...)
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/853e027a-a41a-85d0-3bab-30fe1485ca70.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/fa4afc61-9ccf-6168-b850-06e2f840932a.png)
自分はここで1日ぐらいつまづきました。
OnLocationChangeEventノード内のLocation Dataのピンを右クリック、`構造体ピンに分割`を押すと、情報をStringとして一つ一つ受け取れるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/1fb22e61-caed-0e66-b89a-e86b7a15c843.png)

以上がUE4でGPSを取得するのに外せないBPになります。

#6. その他のブループリント
**BP_ARPawnのBPのイベントTickの下の続き**
基本的にはHandheld ARテンプレートと同じことをしてますが、ループ処理の最初に、緯度経度のそれぞれのStringの値が後程説明するPosition Arrayの2Dベクトルの2つの数値にそれぞれ近いかどうかを判別しています。(多分もっと簡素なノードはあると思いますが)
画面をタッチして、タッチした座標にカメラで検出したジオメトリがあるか判別し、重なってたら何かをスポーンするという感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/038c5815-7978-05e3-c2a9-653254cddfc2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/132ec6fb-31a7-729a-4726-260cde0087e6.png)
ブーリアンの配列をあらかじめ作り、Input Touchイベントの最後で各キャラクターがスポーンされたらTrueを返しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/10111b01-1adc-82b2-9880-3c5b51af1cfd.png)

Position Arrayとはなんやねんという話ですが、10体いるキャラクターをどこに配置するか決めた2Dベクトルの配列の変数です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/94a40ee8-0add-a623-3d34-12a32775469f.png)
Google Colab でプロットして位置調整しつつ各ベクトルの要素を決め、Position Arrayの初期値にぶち込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/5e6f5dcf-c8fb-99ae-8174-01453891de5e.png)
同様に、それぞれの位置に表示したいキャラクターを配置する(アクタの参照先の配列をつくる)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/b01f897d-f52a-22fc-12e5-3a4dd6f9c3cb.png)
![スクリーンショット (23).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/33173aa1-9e59-519d-6b9a-c8cd9cdadbac.png)
ちなみに、キャラを全種類出しとくとこんな感じです。(これは画像ですが、コンテンツブラウザ内でも動いてるんですよ！)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/f48a52df-ba35-05e0-6c96-0cd009ba1746.png)
今回はつなげるとアニメーションになるPNG画像をつくってもらい、Flaipbook(背景透過されたままのアニメーション)をつくり、一個一個アクターに渡しました。
2Dを表示するARを作れるのかな？と最初は思ってたんですが簡単にできました。
Filpbookを作る際、以下の動画がとても参考になりました。
https://www.youtube.com/watch?v=fo3xTxEyq-w&t=496s

アクターにキャラクターのFlipbookコンポーネントを渡す際は、ビューポート内にポイッってするだけでOKでした。3Dのキャラクターのコンポーネントでも一緒っぽいですね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/5bc1697d-80da-b833-1bb7-9d3b434f86b5.png)

---
UI
UMGで３つのウィジェットブループリントを用意しました。
正直、UIの知識は埋め合わせたものなので、もっといいUI管理方法絶対あるよな...と思ってます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/da4010de-a8e8-8eef-2536-5242f58e7922.png)
・BP_MainMenuのイベントグラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/6cf1c5e4-30cc-bfa8-d6f8-6e9fd9d3b13f.png)
・BP_Watch(微生物探すときのUI)のイベントグラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/a7cb0a06-3032-44cb-6614-9659872e2933.png)
・BP_Map(図鑑てきな画面)のイベントグラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/cffde5c0-e166-d8d7-6838-76d9751a2e33.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/55cca6a9-515b-a7c3-e9e8-80ad68df6cf1.png)
BP_Mapが開かれるたびに(Constructされる度に)BP_ARPawnにあるCharaSpawnedというフラグの配列(各キャラクターがスポーンされたか)をキャスト参照しています。
Map上のそれぞれのキャラクターを配置したい場所に画像と透過したボタンとキャプションを置いてあります。
2枚目がめっちゃ汚いのは、On Clickedをまとめられなかったからです。
キャプションを非表示にするとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/80cac220-ef55-3a2e-28c7-78fee6c71022.png)


ちなみにレベルブループリントのイベントBeginPlayでBP_Mapのボタンなりキャラの画像なりを非表示にする関数をCastして実行させています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/6879f13c-c82b-f26d-17c7-b75e0a7a8be0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/6c183a08-55b7-ec7f-2d68-ce3e9b5745f6.png)


---
最後に、すごく参考になりましたありがとうございました！というページなど載せておきます
https://iloverisusan.tumblr.com/post/174043956990/handheldar
https://wspri.plusload.net/?p=394
https://wspri.plusload.net/?p=304
https://www.youtube.com/watch?v=uo16N3W-nf8!
https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442991/fd278d38-f945-cdc0-dedd-a7cb3d33ca81.png)

Qiita初投稿おわり！！！
ありがとうございました。