【ARKit】社長が好きすぎて駆逐するアプリを作った話 その1. FPS視点への固定編

## 何を作ったか！（どう駆逐したか）

簡単にご説明すると
「__ultra soulのリズムに乗せてAR空間上の社長を射撃し、爆破し、ロケランで吹っ飛ばした__」
です！！！

その狂行の様子を10秒動画にまとめました↓

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【ARKit】社長が好きすぎて駆逐するアプリを作った話<br><br>B&#39;zのultra soulに乗せて社長を駆逐してみました♪<br><br>解説記事はこちら↓<a href="https://t.co/tVG3PXRiCa">https://t.co/tVG3PXRiCa</a><a href="https://twitter.com/hashtag/Swift?src=hash&amp;ref_src=twsrc%5Etfw">#Swift</a> <a href="https://twitter.com/hashtag/ARKit?src=hash&amp;ref_src=twsrc%5Etfw">#ARKit</a> <a href="https://twitter.com/hashtag/iOS?src=hash&amp;ref_src=twsrc%5Etfw">#iOS</a> <a href="https://twitter.com/hashtag/%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA?src=hash&amp;ref_src=twsrc%5Etfw">#アプリ開発</a> <a href="https://twitter.com/hashtag/%E5%80%8B%E4%BA%BA%E9%96%8B%E7%99%BA?src=hash&amp;ref_src=twsrc%5Etfw">#個人開発</a> <a href="https://t.co/S9sKniXGBi">pic.twitter.com/S9sKniXGBi</a></p>&mdash; ウルトラ深瀬 (@UltraFukase) <a href="https://twitter.com/ultrafukase/status/1336913118174146561?ref_src=twsrc%5Etfw">December 10, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## はじめに

どうも！B'z大好き芸人のウルトラ深瀬です！（誰だ）
今回はiOSのARKitでFPS風ゲームを作り、社長を駆逐したお話です！（解説あり）


## 苦労した3選
　[①FPS視点への固定](https://qiita.com/Takamasa_Fukase/items/87f267bd0e3a952300ba)　　 [②ゆらゆらモーション](https://qiita.com/Takamasa_Fukase/items/8f1f5225ddb4e0b3e4a5)　　 [③特殊効果（爆発など）](https://qiita.com/Takamasa_Fukase/items/78ea8ea26bf0e40fd526)
![gifFPS実演 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/a31349cc-fa13-7de3-cdd2-bbebe5d52d15.gif)　![gifゆらゆら実演 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/6c21d872-bdca-c646-45be-bfef13f0e861.gif)　![gif特殊効果実演 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/b313270c-370c-6a9a-57ac-5307665fa2cb.gif)


今回はARKitでこれを作る過程で、ググっても出なくて個人的に苦労したこの3選について執筆してみることにしました。

長くなりそうなので3つの回に分けさせていただきます。

__当記事は「その1. FPS視点への固定編」です。__

なお、理解が間違っている箇所などあるかもしれませんので、その際はご教授いただけますと幸いです！

## FPS視点への固定に必要な3ステップ
おおまかな流れは下記の通りです。

①エディタ上でピストルの位置と角度を自由に調整
↓
②ピストルを空のノードでラッピング
↓
③ラッピングしたノードを常にカメラと同じ位置に移動させ続ける


### その前に
今回使用したピストルの3Dオブジェクトは[こちらのサイト](https://www.turbosquid.com/3d-models/3d-model-colt-1911/1005639)でDLすることができます（無料会員登録が必要です）
右下の「Download」ボタンを押して進み、
赤で囲った3つのファイルをダウンロードしましょう。
モデルの表面に貼り付けるテクスチャ画像なども含まれています。
![スクリーンショット 2020-12-08 10.47.25.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/f9fec7c5-dc92-1eec-d860-834bccf8558f.jpeg)
（やけにリアルだと思ったらテクスチャに実銃の画像がそのまま使用されていました笑）
![スクリーンショット 2020-12-06 21.28.55.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/abb1e470-ca52-6d4e-3aea-3edc5af14311.jpeg)


別でお好きな3Dモデルを探す場合はなるべく.dae拡張子のファイルを使用しましょう。

XCodeプロジェクトへの3Dファイルの入れ方は[こちらの記事](https://qiita.com/yyokii/items/667998a28d46b93fb1e3)が参考になりますので、割愛させていただきます。


## ①エディタ上でピストルの位置と角度を調整
（カメラから見て右下あたりに斜めの角度で映る位置を目指します。何回か試して調整するといいです。）
![pistol解説.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/5c620a03-8fbd-e989-203a-20ca83312ae9.jpeg)


## ②ピストルを空のノードでラッピング
エディタ右上の「＋」からEmptyNodeを選択し、Pistolノードの上にドラッグ&ドロップ、Pistolノードの親になるようにします。
![空ノード追加.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/dafae8d7-00d3-23a9-c9aa-2d549867a938.jpeg)
__これにより①で調整した値が空のノードにとってはデフォルトの値となります。__

![parentNode解説.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/204c7ec7-fb05-5279-914a-e32319ffb7ae.jpeg)



## ③ラッピングしたノードを常にカメラと同じ位置に移動させ続ける

実際には下記の2ステップに分かれています。
③-1. euler（角度）を常にカメラに向けさせ続ける（オイラーと読みます）
③-2. position（座標）をカメラと同じに移動させ続ける

詳しく見ていきます↓

__③-1. euler（角度）を常にカメラに向けさせ続ける__

こちらは公式でSCNBillboardConstraint()というカメラ目線をキープする制約が用意されていますので、
ノードの生成時に一度制約をつけるだけでOKです！
制約をつけるのはpistol本体ではなくラッピングしているparentNodeな点にご注意。

```swift
        //scnファイルの読み込み
        let scene = SCNScene(named: "art.scnassets/M1911.scn")

        //注意:scnのファイル名ではなく、Identity欄のnameを指定する
        if let parentNode = (scene?.rootNode.childNode(withName: "parentNode", recursively: false)) {
            
            //常にカメラ目線になる制約
            let billBoardConstraint = SCNBillboardConstraint()
            
            //注意：制約はピストルをラッピングしているparentNodeにつける！
            parentNode.constraints = [billBoardConstraint]

            //生成したピストルを画面に反映
            self.sceneView.scene.rootNode.addChildNode(parentNode)

        }
```


__③-2. position（座標）をカメラと同じに移動させ続ける__

描画のために常に更新されるDelegateメソッド内で、position移動を行います。

```swift

//常に更新され続けるdelegateメソッド（0.02秒毎くらいに呼ばれる）
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        
        
        if let pistol = sceneView.scene.rootNode.childNode(withName: "parentNode", recursively: false) {
            
            //Pistolをラップしている空のオブジェクトを常にカメラと同じPositionに移動させ続ける
            pistol.position = sceneView.pointOfView?.position ?? SCNVector3()
            
        }
        
        
    }
```


## どういう仕組みか
今回はピストルを空のノードでラッピングしたり、カメラ目線制約を親のノードにつけたりと一見不思議なことをしていますが、
個人的には太陽系とその周りの惑星（月など）のイメージでの理解がしっくりきています。

### 月と太陽系でのイメージ

ピストル　→　月
ラップしている空の親ノード　→　太陽系（見えないまとまり）
と考えます。
太陽を中心とした太陽系の外側に月（ピストル）がぶら下がってるイメージです。

__イメージSTART↓
③-2の処理で、太陽系の中心（太陽）点がカメラと同じ位置に動きつづけるようになります。
↓
ではカメラ（端末）が動くとどうなるか？
↓
ちゃんとついてきてくれます。ピストルに近づこうとするとピストルは遠ざかり、ピストルから離れようとするとついてきます。
↓
ではカメラ（端末）が右を向くとどうなるか？
↓
ピストルはついてきてくれません。
↓
これは、太陽系の座標移動に付随してピストルがついてきても、太陽系の角度は変わってくれていないからです。
カメラが右を向いたら、太陽系もクルッと右に回す必要があります。
↓
そのために③-1のカメラ目線制約をつけています。__

以上です！！！
（分かりにくかったらこの例えは忘れてください笑）
ご不明点がございましたらコメント欄からお願いいたします♪

## 駆逐アプリの続きはこちら
その2　→[__【ARKit】社長が好きすぎて駆逐するアプリを作ったはなし その2. ゆらゆらモーション編__](https://qiita.com/Takamasa_Fukase/items/8f1f5225ddb4e0b3e4a5)
その3　→[__【ARKit】社長が好きすぎて駆逐するアプリを作った話 その3. 爆発の作り方編__](https://qiita.com/Takamasa_Fukase/items/78ea8ea26bf0e40fd526)

## さいごに
続きまして、アルサーガパートナーズ Advent Calendar 2020 11日目の記事は再び @mizukicker さんのご登場です :tada:

## おまけ
今回の駆逐アプリの元ネタになった個人ゲームアプリ「AR-GunMan」がAppStoreにて配信中です♪
https://apps.apple.com/jp/app/ar-gunman/id1542082005
![AR-GunMan_icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521005/30270504-6914-9ad9-e03a-633870054e96.png)