平面にオブジェクトを配置する
この記事ではARKitを用いて、検出された平面にアイテムを配置する実装を紹介します。
ARKitでの初期設定や、アイテムとして追加する3Dモデルのプロジェクトへの取り込みなどは以下をご参照ください〜。;)
・【ARKit入門】 まずやること
・【ARKit入門】Xcodeに3Dファイルを入れてみる
ではでは本題です!!
タップの検出
ARSCNViewがタップされた時にその情報を受け取る必要があるので、まずその検出をします。tapped
メソッドについては後述します。
/// メインのビューのタップを検知するように設定する
func registerGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped))
self.mainSceneView.addGestureRecognizer(tapGestureRecognizer)
}
アイテムボタン
ボタンがタップされたら、対応するアイテムを選択状況とします。
selectedItem
の値は.scnファイルのファイル名とします。
@IBAction func cupButtonTapped(_ sender: Any) {
selectedItem = "cup"
}
@IBAction func wineButtonTapped(_ sender: Any) {
selectedItem = "wine_glass"
}
画面タップ時の処理
画面がタップされたらsender
よりタップ位置を取得し、ARアンカーを検索します。そこでARアンカーの情報が所得されていればアイテム追加処理をします。
@objc func tapped(sender: UITapGestureRecognizer) {
// タップされた位置を取得する
let sceneView = sender.view as! ARSCNView
let tapLocation = sender.location(in: sceneView)
// タップされた位置のARアンカーを探す
let hitTest = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)
if !hitTest.isEmpty {
// タップした箇所が取得できていればitemを追加
self.addItem(hitTestResult: hitTest.first!)
}
}
アイテム追加処理
選択したアイテムに対応する.scnファイルを使用してノードを作成し、現実世界の座標を検出しそこにアイテムを配置します。
/// アイテム配置メソッド
func addItem(hitTestResult: ARHitTestResult) {
if let selectedItem = self.selectedItem {
// アセットのより、シーンを作成
let scene = SCNScene(named: "Models.scnassets/\(selectedItem).scn")
// ノード作成
let node = (scene?.rootNode.childNode(withName: selectedItem, recursively: false))!
// 現実世界の座標を取得
let transform = hitTestResult.worldTransform
let thirdColumn = transform.columns.3
// アイテムの配置
node.position = SCNVector3(thirdColumn.x, thirdColumn.y, thirdColumn.z)
self.mainSceneView.scene.rootNode.addChildNode(node)
}
}
以上で、実装完了です!!
最後に全体のサンプルコードを載せていますのでご参考ください。
また、今後もARKit入門記事を投稿予定です!ARKitを用いたサンプルアプリ、ARKitで使用されているメソッド、プロパティの詳細についてまとめていこうと思っています。お楽しみにっ!
サンプルコード全体
import UIKit
import ARKit
class ViewController: UIViewController{
@IBOutlet weak var mainSceneView: ARSCNView!
let configuration = ARWorldTrackingConfiguration()
var selectedItem: String?
override func viewDidLoad() {
super.viewDidLoad()
initialize()
registerGestureRecognizers()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
/// ARSCNiew初期化設定
func initialize (){
self.mainSceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, ARSCNDebugOptions.showFeaturePoints]
self.configuration.planeDetection = .horizontal
self.mainSceneView.session.run(configuration)
self.mainSceneView.autoenablesDefaultLighting = true
}
/// メインのビューのタップを検知するように設定する
func registerGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped))
self.mainSceneView.addGestureRecognizer(tapGestureRecognizer)
}
@objc func tapped(sender: UITapGestureRecognizer) {
// タップされた位置を取得する
let sceneView = sender.view as! ARSCNView
let tapLocation = sender.location(in: sceneView)
// タップされた位置のARアンカーを探す
let hitTest = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)
if !hitTest.isEmpty {
// タップした箇所が取得できていればitemを追加
self.addItem(hitTestResult: hitTest.first!)
}
}
/// アイテム配置メソッド
func addItem(hitTestResult: ARHitTestResult) {
if let selectedItem = self.selectedItem {
// アセットのより、シーンを作成
let scene = SCNScene(named: "Models.scnassets/\(selectedItem).scn")
// ノード作成
let node = (scene?.rootNode.childNode(withName: selectedItem, recursively: false))!
// 現実世界の座標を取得
let transform = hitTestResult.worldTransform
let thirdColumn = transform.columns.3
// アイテムの配置
node.position = SCNVector3(thirdColumn.x, thirdColumn.y, thirdColumn.z)
self.mainSceneView.scene.rootNode.addChildNode(node)
}
}
@IBAction func cupButtonTapped(_ sender: Any) {
selectedItem = "cup"
}
@IBAction func wineButtonTapped(_ sender: Any) {
selectedItem = "wine_glass"
}
}