アールテクニカ地下ガレージ

アールテクニカ株式会社の製品開発・研究開発・日々の活動です

ARKitで3Dオブジェクトを操作する

Author

中村@アールテクニカ

アールテクニカの中村です。
前回のARKit記事からだいぶ時間が経ってしまいましたが、前回作成したARアプリに画面タッチでAR空間内の3Dオブジェクトを操作する機能を追加してみたいと思います。

アプリの仕様

下記の仕様で実装します。

  • タップした3D平面上に3Dオブジェクト(キューブ)を作成する。
  • 3Dオブジェクトを長押しすると削除する。

タップで3Dオブジェクト作成する

まず、タップした座標に応じて3D空間にキューブを作成する機能を実装します。
viewDidLoad()にタップイベントハンドラを登録するコードを追加します。
また、シーンのデフォルトのライティングは環境光のみなのでキューブに陰影が出ないので、オムニライト(点光源)を追加しておきます。

 // タップイベントハンドラの登録
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapView))
sceneView.addGestureRecognizer(tapGesture)
        
// オムニライトを追加
let lightNode = SCNNode()
lightNode .light = SCNLight()
lightNode .light!.type = .omni
lightNode .position = SCNVector3(x: 0, y: 10, z: 10)
 scene.rootNode.addChildNode(lightNode )

次に、タップのイベントハンドラを実装します。
ARSCNViewのhitTestメソッドにタップした2D座標を渡すと、3D空間上にヒットするオブジェクトがあればそれを返してくれます。
typesに、existingPlaneを指定することで検出済みの3D平面がヒットテストの対象になります。
existingPlaneでは検出した平面のサイズは考慮されませんが、existingPlaneUsingExtentを使用すれば平面サイズが考慮されます。

@objc func tapView(sender: UITapGestureRecognizer) {
    // タップした座標にヒットする平面を取得する
    let location = sender.location(in: sceneView)
    let hitTestResult = sceneView.hitTest(location, types: .existingPlane)
    if let result = hitTestResult.first {
        // Cubeを作成
        let cube = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
        let cubeNode = SCNNode(geometry: cube)
        cubeNode.name = "cube"
        
        // Cubeのマテリアルを設定
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.red
        material.diffuse.intensity = 0.8;
        cubeNode.geometry?.materials = [material]

        // Cubeの座標を設定
        cubeNode.position = SCNVector3(
            result.worldTransform.columns.3.x,
            result.worldTransform.columns.3.y + 0.1,
            result.worldTransform.columns.3.z
        )
            
        sceneView.scene.rootNode.addChildNode(cubeNode)
    }
 }


ここまででキューブの作成は実装できたので実行してみます。

まず最初に変面を検出します。
f:id:artteknika_nakamura:20180731172548j:plain:h400
画面をタップするとキューブが作成されます。
f:id:artteknika_nakamura:20180731172536j:plain:h400
キューブを複数作成してみます。
f:id:artteknika_nakamura:20180731172540j:plain:h400

長押しで3Dオブジェクトを削除する

続いて、作成したキューブを長押しすると削除する機能を実装します。
まず、viewDidLoad()にロングプレスイベントハンドラを登録するコードを追加します。

// ロングプレスイベントハンドラの登録
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressView))
sceneView.addGestureRecognizer(longPressGesture)


次にロングプレスのイベントハンドラを実装します。

@objc func longPressView(sender: UILongPressGestureRecognizer) {
    if sender.state == .began {
        let location = sender.location(in: sceneView)
        let hitTest  = sceneView.hitTest(location)
        if let result = hitTest.first  {
            if result.node.name == "cube"
            {
                result.node.removeFromParentNode();
            }
        }
    }
}

以上で、削除機能も実装できたので実行してみます。

キューブを3つ作成し、一番上のキューブを長押しします。
f:id:artteknika_nakamura:20180731172540j:plain:h400
長押ししたキューブが削除されました。
f:id:artteknika_nakamura:20180731172544j:plain:h400

最後に

ARKit を使うことでAR空間内のオブジェクトの操作を簡単に実装することができました。
iOS 12に搭載される予定のARKit 2では、3Dオブジェクトの検出や複数ユーザでAR空間の共有ができるようになるそうなので、今後これらの機能も使ってみたいと思います。

Author

中村@アールテクニカ

アールテクニカのSE兼プログラマ。Web系のサーバサイド・フロントエンドからスマホ・PCのネイティブアプリまでソフトウェア開発全般が守備範囲。最近はハードウェア開発にも興味あり。

スポンサーリンク