アプリ開発日誌
2018.03.07
【Swift】初心者でも簡単にできるARKitを使ってみよう(2)
こんにちは、松田翔大です。
前回(初心者でも簡単にできるARKitを使ってみよう)最後に書いた通り、カメラで平面を検知して、そこに3Dモデルを表示させるところまでやってみたので書いてみました。
#事前準備
まずは前回同様、Xcodeでプロジェクトを作成します。
Xcodeの環境も前回と同様です。
まず、新規のファイルを作成します。今回は、PlaneNode.swiftファイルを作成します。
作成する方法は、作成したプロジェクトのプロジェクト名のフォルダを右クリック -> New File… -> Cocoa Touch Classを選択してNextを押す。

Class:に[PlaneNode]と入力し、Subclass of:はNSObjectを選択し、Nextを押します。

この画面はそのままCreateを押せば問題ないです。

これでPlaneNode.swiftが作成されます。

#実装
まずは、先程作成したPlaneNode.swiftを実装していきます。
以下のように実装してください。
PlaneNode.swift
import UIKit
import SceneKit
import ARKit
class PlaneNode: SCNNode {
fileprivate override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(anchor: ARPlaneAnchor) {
super.init()
// ARAnchorの情報に基づいて、半透明の平面を表現するノードオブジェクトの作成
geometry = SCNPlane(width: CGFloat(anchor.extent.x), height: CGFloat(anchor.extent.z))
let planeMaterial = SCNMaterial()
planeMaterial.diffuse.contents = UIColor.white.withAlphaComponent(0.5)
geometry?.materials = [planeMaterial]
SCNVector3Make(anchor.center.x, 0, anchor.center.z)
transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0)
}
func update(anchor: ARPlaneAnchor) {
(geometry as! SCNPlane).width = CGFloat(anchor.extent.x)
(geometry as! SCNPlane).height = CGFloat(anchor.extent.z)
position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
}
}
次にViewController.swiftを実装します。
以下のように実装してください。
ViewController.swift
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.scene = SCNScene()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARWorldTrackingConfiguration()
// 平面の検出を有効化する
configuration.planeDetection = .horizontal
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
// MARK: - ARSCNViewDelegate
// ARPlaneAnchorが検出されて、自動的にシーンに追加される時に呼ばれる
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
DispatchQueue.main.async {
if let planeAnchor = anchor as? ARPlaneAnchor {
// 平面を表現するノードを追加する
node.addChildNode(PlaneNode(anchor: planeAnchor) )
}
}
}
// 情報が更新されるたびにARPlaneAnchorも更新されて呼ばれる
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
DispatchQueue.main.async {
if let planeAnchor = anchor as? ARPlaneAnchor, let planeNode = node.childNodes[0] as? PlaneNode {
// ノードの位置及び形状を修正する
planeNode.update(anchor: planeAnchor)
}
}
}
}
これで実装は終わりです。
ビルドして確認して以下の画像のようになっていれば問題ありません。

今回は、ARKitで平面を検出するところまでで終わりたいと思います。
次回は、今回書ききれなかった検出した平面に3Dオブジェクトを設置するところまで書きたいと思います。