中村@アールテクニカ
アールテクニカの中村です。
前回の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) } }
ここまででキューブの作成は実装できたので実行してみます。
まず最初に変面を検出します。
画面をタップするとキューブが作成されます。
キューブを複数作成してみます。
長押しで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つ作成し、一番上のキューブを長押しします。
長押ししたキューブが削除されました。
最後に
ARKit を使うことでAR空間内のオブジェクトの操作を簡単に実装することができました。
iOS 12に搭載される予定のARKit 2では、3Dオブジェクトの検出や複数ユーザでAR空間の共有ができるようになるそうなので、今後これらの機能も使ってみたいと思います。
中村@アールテクニカ
アールテクニカのSE兼プログラマ。Web系のサーバサイド・フロントエンドからスマホ・PCのネイティブアプリまでソフトウェア開発全般が守備範囲。最近はハードウェア開発にも興味あり。