59
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ARCoreのCloud AnchorsをSwift, iOSで試してみた

Last updated at Posted at 2018-05-13

Google I/O 2018で発表された Cloud Anchors|ARCoreはiOSでも使えるということで、ARKitを使ったアプリを作っている身として早速触ってみました。

公式サンプルがobjcだったので、今回Swiftでオリジナルサンプルを作ってみました。以下にアップしたので参考にしてみてください。

本記事では、Cloud Anchorsの概要とサンプルの解説をしたいと思います。

Cloud Anchorsとは

alt
(by Experience augmented reality together with new updates to ARCore)

現在、ARKit, ARCoreを使うと、カメラに写した世界の平面を認識し、空間を座標で表すことができます。しかし、他の端末で同じ場所を見た時に、**そこが同じ場所かどうかということはわかりません。**空間を座標で表し、平面を認識しただけだからです。

しかし、様々な企業が研究開発しているARCloudがあると、違う端末どうしが同じ場所を写した時に、そこが同じ場所であると認識できるようになります。

Cloud AnchorsはそのARCloudの簡易版と言えるでしょう。

空間から認識した特徴点を元にアンカーを生成し、その情報をGoogleのCloudを使って共有することができるフレームワークです。

公式で公開されているサンプルは、ドロイドくんを平面上に配置すると、同じAPIKeyを参照するアプリで、同じ場所にドロイドくんを現すことができるというものです。

Original Sample

僕が簡単に作ったオリジナルサンプルを紹介します。

飛行機をおく 同じ場所に飛行機が出現
GIF image-B938C504D902-1.gif GIF image-8D03C122CEC6-1.gif

ちょっと向き変わっちゃってますけど(笑)、こんな感じで同じ場所に物体を出現させることができます。

手順はこんな感じです。

  • 前提

  • アンカーが追加されたら飛行機出すようにする

  • 送る側

  • アンカーを追加する(飛行機を置く)

  • アンカー情報をGoogleのCloudに送る

  • GoogleのCloudから帰ってきたcloudIdentifierをdatabaseに入れる

  • 表示する側

  • databaseに入ったcloudIdentifierを受け取る

  • cloudIdentifierを使ってGoogleのCloudを検索する

  • アンカー情報を受け取ってアンカーを追加

  • アンカーが追加された場所に飛行機出てくる

解説していきます。

PlaneAnchor以外のアンカーだったら飛行機出すようにしとく

// MARK: - <#ARSCNViewDelegate#>
extension ViewController: ARSCNViewDelegate {
    
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        // Basicaly, you can get only ARPlaneAnchor,
        // but this case, you can also get another anchor which is created by ARCore.
        if !(anchor is ARPlaneAnchor) {
            let scene = SCNScene(named: "art.scnassets/ship.scn")!
            return scene.rootNode.childNode(withName: "ship", recursively: false)
        }
        return nil
    }
}

基本的にARAnchor って現状ARPlaneAnchorしか配置されないので、ARPlaneAnchor じゃなかったら飛行機を配置するようにしちゃいます。1

アンカー情報を送る

1.タップした平面にアンカーを追加

ViewController.swift

    // Tap Event
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else {
            return
        }
        let touchLocation = touch.location(in: sceneView)
        let hitTestResults = sceneView.hitTest(touchLocation, types: [.existingPlane, .existingPlaneUsingExtent, .estimatedHorizontalPlane])
        
        if let result = hitTestResults.first {
            addAnchor(transform: result.worldTransform)
        }
    }

    // add an anchor to AR Space and share the anchor data to Google Cloud
    private func addAnchor(transform: matrix_float4x4) {
        let arAnchor = ARAnchor(transform: transform)
        sceneView.session.add(anchor: arAnchor)
        
        do {
            _ = try gSession.hostCloudAnchor(arAnchor)
        } catch {
            print(error)
        }
    }

タップした平面にアンカーを追加し、その情報を try gSession.hostCloudAnchor(arAnchor) でGoogleのCloudに共有します。

2.session:didHostAnchorが呼ばれるので、databaseにcloudIdentifierを共有

GARSessionDelegate
    func session(_ session: GARSession, didHostAnchor anchor: GARAnchor) {
        let id = anchor.cloudIdentifier
        firebaseReference.childByAutoId().child("hosted_anchor_id").setValue(id)
    }

anchorのhostが終わると、GARSessionDelegateのsession:didHostAnchor が呼ばれて、cloudIdentifierを得ることができます。

ここではFirebaseRealtimeDatabaseでcloudIdentifierを共有していますが、何を使ってもいいです。idが別の端末と共有できればいいので。

記録されたアンカー情報を受け取って表示

1.cloudIdentifierを受け取る

ViewController.swift
    // fetch anchors from Firebase Database
    private func observeAnchors() {
        firebaseReference.observe(.value) { (snapshot) in
            guard let value = snapshot.value as? [String : Any] else {
                return
            }
            let list = value.values
            list.forEach { value in
                if let dic = value as? [String: Any],
                    let anchorId = dic["hosted_anchor_id"] as? String {
                    self.fetchedAnchorIds.append(anchorId)
                }
            }
        }
    }

サンプルでは、firebaseから受け取った情報を1回fetchedAnchorIds cloudIdentifierに入れています。。この後ボタンを押した時にそのidentifierを使って同期することを試みます。

2.cloudIdentifierからGoogle Cloudを検索

    private func resolveAnchors(){
        fetchedAnchorIds.forEach { id in
            _ = try! gSession?.resolveCloudAnchor(withIdentifier: id)
        }
        fetchedAnchorIds.removeAll()
    }

ボタンのタップをトリガーに、resolveAnchors()メソッドを呼び、try! gSession?.resolveCloudAnchor(withIdentifier: id)fetchedAnchorIds を一個ずつGoogleのCloudに投げています。

3.発見するとdidResolveが呼ばれるので、アンカーを配置する

GARSessionDelegate
    func session(_ session: GARSession, didResolve anchor: GARAnchor) {
        let arAnchor = ARAnchor(transform: anchor.transform)
        sceneView.session.add(anchor: arAnchor)
    }

空間を見回していると、発見できます。

これで、アンカーをクラウドで共有することができました。

飛行機をおく 同じ場所に飛行機が出現
GIF image-B938C504D902-1.gif GIF image-8D03C122CEC6-1.gif

注意点

ARSCNViewDelegate
// MARK: - <#ARSCNViewDelegate#>
extension ViewController: ARSessionDelegate {
    
    func session(_ session: ARSession, didUpdate frame: ARFrame) {
        do {
            try gSession.update(frame)
        } catch {
            print(error)
        }
    }
}

上のように、フレーム情報を常にARCoreに共有してください。そうじゃないとARCoreが空間を把握できないので。

まとめ

コードは https://github.com/kboy-silvergym/CloudAnchorsSwiftSampler に公開しました。Cloud AnchorsをSwiftで使う方の手助けになれば幸いです。

本家のサンプルよりもより無駄を削ぎ落として、CloudAnchorsが何をしているのか分かりやすく整理したつもりです。

では!

参考

  1. ...ARImageAnchorARFaceAnchorもあります

59
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
44

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?