ネイティブでiOSのARアプリを作るためのTIPS

![Group 9.png](https://qiita-image-store.s3.amazonaws.com/0/186277/fcb6b20f-36d4-3501-4a46-8cb93ab0757a.png)

# 概要
ARKitアドベントカレンダー2018 2日目は @kaoryuuu がお送りします！

今回はネイティブアプリでARKitでの開発してきた際に必要そうな内容を紹介したいと思います。

# はじめに
ARKit、触ったことある方いますでしょうか。「興味はあるけど難しそう」と印象を持っている方は多いと思います。
個人的な主観ですが、ARKit自体はそこまで難しいものではありません。3DモデルをAR空間上に表示させたりするだけなら多分1日も要らないです。ただ、 ARをユーザー体験として落とし込むのは非常に難しいと自分は思っております。
Appleのサンプルアプリやドキュメントを使いながら、良いアプリつくるための知見を共有していきます。

## ARKitの基礎を知りたい
(宣伝)こちらを参考にしていただけるとざっくりどのようなものか掴めるかと思います。
[ARKitを扱う際の心構えとTips](https://qiita.com/kaoryuuu/items/184dbd0fc9c9017dd7d2)

以前、チートシート的な物も作成したので参考にしていただけると幸いです。
![AEKitCheatSheet.png](https://qiita-image-store.s3.amazonaws.com/0/186277/2b739e69-f253-373b-acba-06965d30d01c.png)


### サンプルコードから学ぶ
ARKitでの開発にはApple公式のサンプルを参考にするとだいたいやりたいことが載っています。
自分がつくりたいものに合わせて見てみると良いかと思います。
https://developer.apple.com/documentation/arkit

・ [Building Your First AR Experience](https://developer.apple.com/documentation/arkit/building_your_first_ar_experience)
・ [Creating a Persistent AR Experience]( https://developer.apple.com/documentation/arkit/creating_a_persistent_ar_experience)
・ [Handling 3D Interaction and UI Controls in Augmented Reality](https://developer.apple.com/documentation/arkit/handling_3d_interaction_and_ui_controls_in_augmented_reality)

#### 複数人でARやりたい！ 
・[Creating a Multiuser AR Experience](https://developer.apple.com/documentation/arkit/creating_a_multiuser_ar_experience)

#### ネイティブでゲーム作りたい！
ゲームだったらUnityの方が正直楽だと思います。。。が一応。
・ [SwiftShot: Creating a Game for Augmented Reality](https://developer.apple.com/documentation/arkit/swiftshot_creating_a_game_for_augmented_reality)

#### ARKitと何か組み合わせてみたい
・ [ARKit+Vision](https://developer.apple.com/documentation/arkit/using_vision_in_real_time_with_arkit)
こちらのプロジェクトでは実際にMLモデルを読み込んでAR空間上に認識したラベルを表示するものが公開されております
https://github.com/hanleyweng/CoreML-in-ARKit

すでに学習済みのCore MLのデータもAppleが提供しているのでまずはこれを使ってみるとよいでしょう
https://developer.apple.com/jp/machine-learning/build-run-models/

・[ARKit+Core Location](https://github.com/ProjectDent/ARKit-CoreLocation)
・[Creating an Immersive AR Experience with Audio](https://developer.apple.com/documentation/arkit/creating_an_immersive_ar_experience_with_audio)
・[Adding Realistic Reflections to an AR Experience](https://developer.apple.com/documentation/arkit/adding_realistic_reflections_to_an_ar_experience)

## Haptic Feedback
### UIFeedbackGenerator

ブルっと端末が震えるやつです。
アプリインストール時などに「ピコーン」と鳴ると同時に少し振動するのが気持ち良いですよね(?)

ARを扱う上でHaptic Feedbackは非常に良い働きをしてくれます。
AR上でのオブジェクトの配置やタップ判定などに、「あ、今このオブジェクトを触っているんだ」感をユーザーに伝える手段として活用できます。

参考：https://techblog.zozo.com/entry/ios_ui_feedbcak_generator

## ARTrackingState 
https://developer.apple.com/documentation/arkit/artrackingstate?language=objc

現在カメラに写っているフレームから、トラッキングの状態を取得することができます

### NotAvailable
> Camera position tracking is not available.

トラッキングが利用できない状態。

(例えば、カメラが何かに覆われて映し出されている画面が真っ黒になっているとか)

### Limited(ARCamera.TrackingState.Reason)
> Tracking is available, but the quality of results is questionable.

利用できるけど結果は微妙かも。。
また、その理由も取得できます。(後述参照)

### Normal
> Camera position tracking is providing optimal results.

### ARCamera.TrackingState.Reason
どんなエラーなのか、例えばInsufficientFeaturesは画面に映し出されている特徴量が少ないという理由になっています。iOS12のMeasureアプリでは特徴点が少なかった際に画面全体に端末を動かすようアニメーションをオーバーレイさせています。

```ARCamera.swift
public enum Reason {

    /** Tracking is limited due to initialization in progress. */
    case initializing

    /** Tracking is limited due to a excessive motion of the camera. */
    case excessiveMotion

    /** Tracking is limited due to a lack of features visible to the camera. */
    case insufficientFeatures

    /** Tracking is limited due to a relocalization in progress. */
    @available(iOS 11.3, *)
    case relocalizing
}
```

### 使い所 ~UseCase~

公式アプリでもトラッキングの状態が悪いと正しく認識させるように促すようなアニメーションを表示させたり、
注意が促されていますね。

![aaaa.gif](https://qiita-image-store.s3.amazonaws.com/0/186277/ce56d082-abb8-d792-fd43-1d8eae57426d.gif)


```ViewController.swift
extension ViewController: ARSCNViewDelegate {
    // カメラのトラッキング状態が変わった際に呼ばれる
    func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
        statusViewController.showTrackingQualityInfo(for: camera.trackingState, autoHide: true)
    
        switch camera.trackingState {
        case .notAvailable, .limited:
            // ここで何かしらの情報を出したり、動作を制限させてあげる
        case .normal:
            // 正常
        }
    }
```

## ARAnchor
### name
https://developer.apple.com/documentation/arkit/aranchor/2873672-name?language=objc

iOS12からARAnchorにnameを付与できるようになりました

```ViewController.swift
let anchor = ARAnchor(name: "hoge", transform: float4x4)
```

※Anchorは文字のごとく、位置を固定化させるようなイメージです。
位置を変えたい場合は直接Anchorの座標を変更するのではなく、NodeのPositionを変更するなどしたほうが良いです。

### 使い所 ~UseCase~

```ViewController.swift
func setObject() {
    // 任意の場所に何か表示させたい
    let anchor = ARAnchor(name: "hoge", transform: float4x4)
    sceneView.session.add(anchor: anchor)
}

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let anchorType = AnchorType(name: "hoge") else {
        return
    }
    // 追加されたものがhogeAnchorの場合だけ処理させたい
}
```

## SCNNode

### CategoryBitMask
詳しい内容は記事はこちらの記事がわかりやすいです。
[SceneKitにおける物体衝突検知を理解する](https://qiita.com/noby111/items/319c727e9527cfbb0692)

HitTest時のOptionに設定することで特定のBitMaskに対応したものだけを認識させることができます。

### TimingModeでEasing
https://developer.apple.com/documentation/scenekit/scnactiontimingmode
アニメーションにEasingをつけることも可能です

```SCNNode.swift
let foo = SCNAction()
foo.timingMode = .easeIn //linear, easeOut, easeInEaseOut
```

### オブジェクトをカメラの方向に向ける
```SCNNode.swift
let constraint = SCNBillboardConstraint()
constraint.freeAxes = SCNBillboardAxis.Y
constraints = [constraint]
```

## 最後に
何か間違い等ありましたらコメントをいただけると幸いです！