ARKit 3.0のサンプルコード集『ARKit-Invader』を公開しました

# はじめに
drama(@1901drama)です。
WWDC2019から早3ヶ月。ついにiOS13がリリースされ、NDA制約がなくなったので
**ARKit 3.0のサンプルコード集 『ARKit-Invader』**を公開しました！

*[- ARKit-Invader (GitHub)- ](https://github.com/1901drama/ARKit-Invader)
[<img width="350" alt="arkit-invader_icon.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/d4b09e3f-8973-621e-5d05-b35071bb8c57.png">](https://github.com/1901drama/ARKit-Invader)*

昨年AR開発を始めた時に、@shu223さんの「[ARKit-Sampler](https://qiita.com/shu223/items/e55e1426c9e036254761)」や@k-boyさんの「[ARKit-Emperor](https://qiita.com/k-boy/items/760f8b3add6ea283f41c)」に大分助けられたので、今回は自作してみました。AR開発に貢献出来れば幸いです！
（しかし、Sampler,Emperorに続く良い名前が思いつきませんでした。）



# 内容
以下の機能のサンプルコードです。

・**[Coaching UI](https://qiita.com/items/58bce4a1dcea30740678#coaching-ui)** 
・**[People Occlusion 2D](https://qiita.com/items/58bce4a1dcea30740678#people-occlusion-2d)**
・**[People Occlusion 3D](https://qiita.com/items/58bce4a1dcea30740678#people-occlusion-3d)**
・**[Motion Capture 2D](https://qiita.com/items/58bce4a1dcea30740678#motion-capture-2d)**
・**[Motion Capture 3D](https://qiita.com/items/58bce4a1dcea30740678#motion-capture-3d)**
・**[Multiple Face Tracking](https://qiita.com/items/58bce4a1dcea30740678#multiple-face-tracking)**
・**[Simultaneous Front and Back Camera](https://qiita.com/items/58bce4a1dcea30740678#simultaneous-front-and-back-camera)**
・**[Collaborative Session](https://qiita.com/items/58bce4a1dcea30740678#collaborative-session)**




## Coaching UI
環境データ取得用のアニメーションを表示させる機能。
![CoachingUI.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/462957d8-66a8-c202-0cd2-233129600956.gif)
ARKit3からデフォルトで用意してくれるようになりました。（ARCoreはもともとあった）
目的の環境データを取得完了するまで、"ARCoachingOverlayView"というUIViewのアニメーションを表示出来ます。

```CoachingUI_ViewController.swift
//どのデータを取得するか（ARKit2まで同様です。例として床にしてます。）
configuration.planDetection = .horizontal

//完了条件
coachingOverlay.goal = .horizontalPlane
//データが取れなくなった時に自動的に再表示させる（継続的にARを利用する場合は推奨）
coachingOverlay.activatesAutomatically = true
```

```swift
//ゴール(coachingOverlay.goal)の種類は以下から選択出来ます。
case anyPlane
case horizontalPlane
case tracking
case verticalPlane
```

ARに直接関係はありませんが、ARCoachingOverlayViewをそのまま追加(addSubview)すると左上に表示されるので、真ん中に表示させたい場合は以下などを追記してください。

```swift
coachingOverlay.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
    coachingOverlay.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    coachingOverlay.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    coachingOverlay.widthAnchor.constraint(equalTo: view.widthAnchor),
    coachingOverlay.heightAnchor.constraint(equalTo: view.heightAnchor)
])
```

【Sample】[CoachingUI_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/CoachingUI_ViewController.swift)
【Document】[ARCoachingOverlayView](https://developer.apple.com/documentation/arkit/arcoachingoverlayview)



## People Occlusion 2D
ユーザーの身体を、3Dオブジェクトの手前に表示させる機能。
`A12 chip(xR,xS)以上の端末でしか利用出来ません。`
![PeopleOcclusion2D.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/0b94fe55-7315-95d3-1035-ba2af8945a3d.gif)

```PeopleOcclusion2D_ViewController.swift
let configuration = ARWorldTrackingConfiguration()
configuration.frameSemantics = .personSegmentation
sceneView.session.run(configuration)
```
簡単ですね！
後述の3D版より軽量ですが、常に手前に表示するだけなので シチュエーションは限られます。
写った人間のサイズと場所を切り取って、上から画像で表示し続けている感じです。（[ARMatteGenerator](https://developer.apple.com/documentation/arkit/armattegenerator)の技術を流用してる気がします。）

【Sample】[PeopleOcclusion2D_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/PeopleOcclusion2D_ViewController.swift)
【Document】[personSegmentation](https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics/3089125-personsegmentation)



## People Occlusion 3D
ユーザーの身体と3Dオブジェクトの前後関係を、反映・表示させる機能。
`A12 chip(xR,xS)以上の端末でしか利用出来ません。`
![PeopleOcclusion3D.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/06f2e9ea-c472-d616-d7bc-e9dcf47df9b0.gif)

```PeopleOcclusion3D_ViewController.swift
let configuration = ARWorldTrackingConfiguration()
configuration.frameSemantics = .personSegmentationWithDepth
sceneView.session.run(configuration)
```

こちらも簡単。
People Occulusion 2Dの ".personSegmentation" が、".personSegmentationWithDepth" になっただけです。
RealityKit/ARView版では他の実装方法もあります。

【Sample】[PeopleOcclusion3D_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/PeopleOcclusion3D_ViewController.swift)
【Document】[personSegmentationWithDepth](https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics/3194576-personsegmentationwithdepth)



## Motion Capture 2D
ユーザーの身体/関節の位置を画面上の位置座標で取得出来る機能。
`A12 chip(xR,xS)以上の端末でしか利用出来ません。`
![MotionCapture2D.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/ca19840a-bb1f-1671-330b-f9e0d2b6d480.gif)
([参考：ラジオ体操第一・実演](https://www.youtube.com/watch?v=_YZZfaMGEOU))

```MotionCapture2D_ViewController.swift
let configuration = ARWorldTrackingConfiguration()
configuration.frameSemantics = .bodyDetection
arView.session.run(configuration)
```
尚、用途は少なそうですが、3Dの方で使う"ARBodyTrackingConfiguration"にinsertしても使えます。

```Swift
let configuration = ARBodyTrackingConfiguration()
configuration.frameSemantics.insert(.bodyDetection)
arView.session.run(configuration)
```
ARSCNViewではなくARView(RealityKit）使用。
Sampleは結果を分かりやすくする為に、取得した位置を●で可視化させています。
（可視化するものが思いつかず、@nichyouさんの[記事](https://qiita.com/nichyou/items/400429330ddec7b4cde4)を参考にさせて頂きました。）

【Sample】[MotionCapture2D_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/MotionCapture2D_ViewController.swift)
【Document】[bodyDetection](https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics/3214027-bodydetection)



## Motion Capture 3D
ユーザーの身体/関節の位置を空間座標で取得出来る機能。
`A12 chip(xR,xS)以上の端末でしか利用出来ません。`
![MotionCapture3D.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/bc24f8af-af62-602e-02ea-5dd315ce85c7.gif)
([参考：ラジオ体操第一・実演](https://www.youtube.com/watch?v=_YZZfaMGEOU))

```MotionCapture3D_ViewController.swift
let configuration = ARBodyTrackingConfiguration()
arView.session.run(configuration)
```
ARSCNViewではなくARView(RealityKit）使用。
Sampleは結果を分かりやすくする為に、取得した動きをロボットに反映させています。
既に[公式のサンプル](https://developer.apple.com/documentation/arkit/capturing_body_motion_in_3d)があるのでこちらも是非。

【Sample】[MotionCapture3D_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/MotionCapture3D_ViewController.swift)
【Document】[ARBodyTrackingConfiguration](https://developer.apple.com/documentation/arkit/arbodytrackingconfiguration)




## Multiple Face Tracking
３人までの顔の場所と表情を同時追跡する機能。
`A12 chip(xR,xS)以上の端末でしか利用出来ません。`
![MultipleFaceTracking.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/55ceba6e-e614-709a-9cbe-b1f57a28019d.gif)
以前から複数の顔認識（anchor idでの区別）はある程度出来てたっぽいのですが、追跡は1人しか出来てませんでした。ARKit3では3人まで同時追跡出来ます。
（ARKit2では、ImageTrackingも20枚くらい登録出来ましたが追跡は4,5枚が限界でした...）

```MultipleFaceTracking_ViewController.swift
let configuration = ARFaceTrackingConfiguration()
configuration.maximumNumberOfTrackedFaces = 3
sceneView.session.run(configuration)
```
Sampleは顔を認識した順に、黄→青→赤のテクスチャを顔に貼ります。（検知した顔の数÷３の余り数で、色のサイクル判定してます。）
セッションが切れない限りは、一回アウトしても再検知時に同じテクスチャが貼られます。
もし固有人物まで判断したい場合は、検知した顔をFrameで撮ってAI判定させ、結果をFaceAnchorに追加するのが良さそうです。

【Sample】[MultipleFaceTracking_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/MultipleFaceTracking_ViewController.swift)
【Document】[maximumNumberOfTrackedFaces](https://developer.apple.com/documentation/arkit/arfacetrackingconfiguration/3192187-maximumnumberoftrackedfaces)




## Simultaneous Front and Back Camera
手前側のカメラと背面側のカメラで同時にARを利用出来る機能。
`A12 chip(xR,xS)以上の端末でしか利用出来ません。`
![SimultaneousCamera.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/6f552c74-09b7-9f70-4b69-08d6ecff16a4.gif)
一方で取得した環境データをもう一方のシーンに反映することが可能になりました。
自分の表情に合わせて会話するキャラクターとか作れそうですね。

```SimultaneousCamera_ViewController.swift
let configuration = ARWorldTrackingConfiguration()
configuration.userFaceTrackingEnabled = true
sceneView.session.run(configuration)
```
↓逆に手前カメラ(顔用)に背面カメラで取得したデータを利用したい場合

```Swift
let configuration = ARFaceTrackingConfiguration()
configuration.isWorldTrackingEnabled = true
sceneView.session.run(configuration)
```
Sampleは手前カメラに映る顔の表情を 背面カメラ側の仮面オブジェクトに反映させています。

【Sample】[SimultaneousCamera_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/SimultaneousCamera_ViewController.swift)
【Document】[supportsUserFaceTracking](https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/3223421-supportsuserfacetracking)




## Collaborative Session
他のユーザーとのAR体験の共有を補助する機能
![CollaborativeSessions.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/642d01b0-8316-ed4f-2a13-046d77c5df24.gif)

```CollaborativeSessions_ViewController.swift
let configuration = ARWorldTrackingConfiguration()
configuration.isCollaborationEnabled = true
sceneView.session.run(configuration)
```
```Swift
//送信する時
func session(_ session: ARSession, didOutputCollaborationData data:ARSession.CollaborationData) {
    if let collaborationDataEncoded = try? NSKeyedArchiver.archivedData(withRootObject: data, requiringSecureCoding: true){
        self.sendToAllPeers(collaborationDataEncoded)
    }
}

//受信する時
func receivedData(_ data:Data, from peer: MCPeerID) {
    if let collaborationData = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARSession.CollaborationData.self, from: data){
        self.sceneView.session.update(with: collaborationData)
    }
}
```

```Swift
//自分が追加したアンカーか他の誰かが追加したアンカーかを判断(addAnchor時など)
if anchor.sessionIdentifier == self.sceneView.session.identifier {
    //My own anchor was added
} else {
    //Another one's anchor was added
}
```

また、[ARParticipantAnchor](https://developer.apple.com/documentation/arkit/arparticipantanchor#overview)で、接続相手の空間座標を可視化出来ます。

```Swift
if anchor is ARParticipantAnchor {
    //Visualize the opponent's location
}
```

Multipeer Connectivityを利用したので、あんましシンプル化出来ませんでした...。
尚、CollaborationDataをData型に変換して送受信していますが、そのままでも送受信可能とのこと。この場合MultipeerConnectivityで使用している関数は変更してください。

Sampleはタッチした場所にオブジェクトを配置しています。
自分で追加したものは黄色、相手が追加したものは緑色、各端末の場所には赤色のオブジェクトが表示されます。

【補足】
[<img width="400" alt="cs.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/e2ff0818-3687-ba28-23e9-0c613736d11d.png">](https://devstreaming-cdn.apple.com/videos/wwdc/2019/610bh5ws7sw7lxoo/610/610_building_collaborative_ar_experiences.pdf?)
※5人以上でも同時で使える仕様なのですが、検証の結果5人からは遅延が発生しやすくなる印象でした。(処理が重くなる感じ。) ゲームなど高頻度でAnchorを更新する場合は、工夫が必要かと思われます。
また、スタート地点の視点が近ければ近い程、早めにCollaboration OKとなりやすいです。
※2020/9追記
iOS14で今までのようにローカルネットワーク共有に関する制御が変わり利用出来なくなっていました
https://developer.apple.com/documentation/bundleresources/information_property_list/nslocalnetworkusagedescription?changes=latest_major
CollaborativeSessionsなどで使用するMultipeerConnectivityのローカルネットワーク制約解除をGithubに追記してます。


【Sample】[CollaborativeSessions_ViewController.swift](https://github.com/1901drama/ARKit-Invader/blob/master/ARKit-Invader/Menu/CollaborativeSessions_ViewController.swift)
【Document】[isCollaborationEnabled](https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/3152987-iscollaborationenabled), [MultipeerConnectivity
](https://developer.apple.com/documentation/multipeerconnectivity)([Creating a Multiuser AR Experience](https://developer.apple.com/documentation/arkit/creating_a_multiuser_ar_experience))




# まとめ
ARKit-SamplerやARKit-Emperorと同様に「**出来るだけシンプルな実装で伝えること**」を目的にしました。
ただ、iOS初学者はどの処理が機能として必要なのか判断つきにくいと思うので、エラー処理・バリエーション・切り替え・補助機能等もなるべく省いて超シンプル化しています。
`アプリとしては不完全なので利用する際には必要な分を足してください。（想定外の挙動があれば再起動をお願いします。）`

ARKit3の所感としては、個人利用ではなく複数人で使う為の仕組みが増えたなと感じました。
その為、ARKit1〜1.5のメインであった空間情報ベースでの開発というより、ARAnchorメインでの開発に考え方を改めないとなかなか厳しそうです。また、ARViewはSceneViewと異なりSCNNodeをそのまま配置出来ないので、[RealityKit](https://developer.apple.com/documentation/realitykit)の理解も必要でした。RealityKit自体は簡単そうなので今後の機能拡充に期待です。
設定やモードの切り替えも全体的に簡単になってきたので、ARKit3から始める方には とてもありがたいですね。

以上です。ここまで読んで頂きありがとう御座いました！
[<img width="400" alt="ARKit-Invader2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222377/9cbb887d-3959-2c81-67c1-d6d71fe6c031.jpeg">](https://github.com/1901drama/ARKit-Invader)
☆(star)や 👍(いいね) など、
フィードバック頂けるともっと頑張りますので、宜しくお願いします！
https://github.com/1901drama/ARKit-Invader

# 参考・関連記事
- [拡張現実 - ARKit - Apple Developer](https://developer.apple.com/jp/augmented-reality/arkit/)
- [Introducing ARKit 3](https://developer.apple.com/videos/play/wwdc2019/604/)
- [Introducing ARKit3/Laurent Brusa](https://multitudes.github.io/2019/07/Introducing-ARKit3.html)
- [ARKitのサンプルコード集「ARKit-Sampler」](https://qiita.com/shu223/items/e55e1426c9e036254761)
- [ARKit 2.0のサンプルコード集「ARKit-Emperor」を公開しました！](https://qiita.com/k-boy/items/760f8b3add6ea283f41c)
- [ARKit 3 の変更点 (Xcode 11 beta 3)](http://appleengine.hatenablog.com/entry/2019/07/03/053908)