【ARKit】タップした水平面に球体を出現させる

Blog Single

みなさん、こんにちは。
ホットコーヒーおいしい季節ですね、渋谷です。

最近AR関係の記事が続いていますが、今回もARです!
平面をタップすると前回書いたような球体を出現させる物を作っていきます。プロジェクトの作成準備などは、前回の記事を参考にしてください。

■完成イメージ

環境

Swift version 4.2.1
Xcode 10.1

実装

ViewController.swiftに処理を書き加えていきます。

override func viewDidLoad() {
  super.viewDidLoad()
    // Set the view's delegate
  sceneView.delegate = self

  // Set the scene to the view
  sceneView.scene = SCNScene()

  // デバッグ用のポイント表示
  sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]

  // ライトの追加
  sceneView.autoenablesDefaultLighting = true

    // タップした時のaction追加
  let tapScreen = UITapGestureRecognizer(target: self, action: #selector(tapped))
  self.sceneView.addGestureRecognizer(tapScreen)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Create a session configuration
    let configuration = ARWorldTrackingConfiguration()

    // 平面検出
    configuration.planeDetection = .horizontal

    // Run the view's session
    sceneView.session.run(configuration)
}

// タップされた位置を取得する
@objc func tapped(sender: UITapGestureRecognizer) {
    // タップされた位置のARアンカーを探す
    let tapLocation = sender.location(in: sceneView)
    let hitTest = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)

    // タップした箇所が取得できていればアンカーを追加
    if !hitTest.isEmpty {
        let anchor = ARAnchor(transform: hitTest.first!.worldTransform)
        sceneView.session.add(anchor: anchor)
    }
}

以上!
実機にビルドして検知された平面をタップしてみましょう。白い球体が出現するはずです。

解説

平面のタップされた場所に球体を出現させるには以下の3つの手順を踏む必要があります。

  • 平面でタップした座標の検出
  • タップした座標にARAnchorを追加
  • Anchorに合わせて3Dモデルの表示

■平面でタップした座標の検出

viewDidLoadメソッドの最後に

let tapScreen = UITapGestureRecognizer(target: self, action: #selector(tapped))
self.sceneView.addGestureRecognizer(tapScreen)

が書かれていますが、ここのUITapGestureRecognizerにタップされた時の挙動を書いていきます。
targetにタップ時に呼び出す3Dオブジェクト(今回はself)を、actionに作成したtappedメソッドを指定しています。

■タップした座標にARAnchorを追加

ARAnchorの追加は、tappedメソッドで行っています。
tapLocationに、タップした画面の座標を格納し、hitTestメソッドでtapLocationが平面かどうかを調べます。第二引数のtypes.existingPlaneUsingExtentを指定することで、タップ位置が平面内かどうかを調べることができます。水平面内であれば、ARAnchorをsceneに追加します。
また、typesには他にも以下のような引数を指定する事ができます。

  • featurePoint・・・一番近い特徴点の結果を返す。平面に限らず、自由にオブジェクトを配置したい時に使う。
  • estimatedHorizontalPlane・・・水平面だと推定される位置を返す。
  • existingPlane・・・検出された平面を返す。

■Anchorに合わせて3Dモデルの表示

Anchorが追加されると、renderer(_:didAdd:for)が自動的に呼び出れます。renderer(_:didAdd:for)に関しては、前回の記事と同様なので、ぜひ参考にしてください。

まとめ

今回はhitTestメソッドのtypesに指定する値に.existingPlaneUsingExtentを指定しましたが、.existingPlaneを使ってもあまり違いがわからなかったので、検証を重ねて理解を深める必要があると感じました。
次回はしっかりと3Dオブジェクトを使った開発をしていこうと考えています。

Posted by ShibuyaYuuki
今はPHPで開発を行なっているエンジニア。 就職してから体重が15キロ増えました!!

Other Posts: