本家のサンプルコードがMultiuser ARしか無いのでPresistent ARを試してみた。
準備
iOS12、Xcode12
ARKit2.0はiOS12以降が対応しているので、iOS12の開発環境が必要になる。
2018年7月現在、iOS12とXcode12はbeta版の公開のみなので、自己責任でインストールする必要がある。
言語はswift4(ちなみに初心者)。
Augmented Reality Appから始める
Application > Augmented Reality Appを選択。
タップした位置に箱を表示する
viewDidLoadを修正。
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// 特徴点の表示
sceneView.debugOptions = ARSCNDebugOptions.showFeaturePoints
// Create a new scene
let scene = SCNScene()
// Set the scene to the view
sceneView.scene = scene
// タップジェスチャーの登録
let gesture = UITapGestureRecognizer(target: self, action: #selector(tapView))
sceneView.addGestureRecognizer(gesture)
}
tapViewの実装。タップした位置にアンカーを追加。
@objc func tapView(sender: UITapGestureRecognizer) {
guard let hitTestResult = sceneView
.hitTest(sender.location(in: sceneView), types: [.existingPlaneUsingGeometry, .estimatedHorizontalPlane])
.first
else { return }
let anchor = ARAnchor(name: "box", transform: hitTestResult.worldTransform)
sceneView.session.add(anchor: anchor)
}
sessionにAnchorを追加した時の動作を記述。タップ時や読出時に、箱が追加される。
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
if let name = anchor.name, name.hasPrefix("box") {
let geometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
let material = SCNMaterial()
material.diffuse.contents = UIColor.darkGray
geometry.materials = [material]
let addnode = SCNNode(geometry: geometry)
node.addChildNode(addnode)
}
}
ここまでで、タップした位置に箱が表示されるアプリができている。
ARWorldMapの保持、初期化、読出をする
ローカルに保存する保存先としてsaveURL
を定義しておく。
...
class ViewController: UIViewController, ARSCNViewDelegate {
// 拡張子は適当
let saveURL = "arworldmap.dat"
...
Main.storyboardでARSCNView上に「SAVE」「INIT」「LOAD」ボタンを配置し、touchDownイベントをViewControllerの@IBAction
関数とする。
@IBAction func saveTouchDown(_ sender: Any) {
}
@IBAction func initTouchDown(_ sender: Any) {
}
@IBAction func loadTouchDown(_ sender: Any) {
}
それぞれボタン押下時の動作を実装していく。
@IBAction func saveTouchDown(_ sender: Any) {
sceneView.session.getCurrentWorldMap { worldMap, error in
guard let map = worldMap
else { print("Error: \(error!.localizedDescription)"); return }
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: map, requiringSecureCoding: true)
else { fatalError("can't encode map") }
if let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first {
let path_file_name = dir.appendingPathComponent( self.saveURL )
guard ((try? data.write(to: path_file_name)) != nil) else { return }
}
self.displayAlert(message: "保存しました")
}
}
@IBAction func initTouchDown(_ sender: Any) {
sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
node.removeFromParentNode()
}
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
self.displayAlert(message: "初期化しました")
}
@IBAction func loadTouchDown(_ sender: Any) {
var data: Data? = nil
if let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first {
let path_file_name = dir.appendingPathComponent( self.saveURL )
do {
try data = Data(contentsOf: path_file_name)
} catch {
return
}
}
guard let worldMap = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: data!) else { return }
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
configuration.initialWorldMap = worldMap
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
self.displayAlert(message: "読み込みました")
}
メッセージをアラート表示するdisplayAlert
の実装。
func displayAlert(message: String){
let alert = UIAlertController(title: "アラート", message: message, preferredStyle: UIAlertController.Style.alert)
let okayButton = UIAlertAction(title: "ok", style: UIAlertAction.Style.cancel, handler: nil)
alert.addAction(okayButton)
self.present(alert, animated: true, completion: nil)
}
これで
1. タップした位置に箱を表示する
2. 「SAVE」をタップしてARWorldMapを保存する
3. 「INIT」をタップして初期化する(わかりやすくするため)
4. 「LOAD」をタップしてARWorldMapを読出して再スタート
5. 1.と同じ場所を映すと同じ位置に箱が表示される
ができる。