LoginSignup
27
17

More than 3 years have passed since last update.

ARKitでグラフィティアートをして、ARWorldMapで共有する

Last updated at Posted at 2019-02-13

はじめに

はじめまして、drama(@1901drama)です!
ARを勉強するにあたり日本語ドキュメントが少なくて苦戦したので、
誰かの助けになれば...と思い、Qiitaデビューしました。少しでもARが普及すると嬉しいです。
※記載間違い等あれば教えてください。

ARkit 2.0の発表

もう5ヶ月程前ですが、2018/9にARKit2.0(iOS12)がリリースされましたね。
201806221123354000.jpg
https://www.moguravr.com/arkit-2-0/

~追加された機能達~
・ARのマルチプレイが可能に
・ARオブジェクトの再利用が可能に
・イメージ認識やトラッキングサポートが拡大
・AR向けフォーマット「USDZ」に対応 など

つまり、AR体験を
・他人と同時に見れる(共有出来る)
・過去の情報を再現出来る
・専用アプリ以外(safariや他のデバイス)でも見れる ということに。

以前unityでARをやってたときよりも、めちゃくちゃ出来ることが増えていて驚きです。
これはもう電脳コイルのようなAR時代も近いのではないか!!? と思い ARを始めました。
今回はAR開発を初めて 一つ目に作ったものを紹介します。

出来たもの

グラフィティーアートをどこでも出来れば楽しそう!という考えから、ARでラクガキするSNSを作りました。
3Dオブジェクトを置いたり描いたり、つくったものを共有/評価 出来ます。

・置いたり、書いたり

・共有したり、評価したり、上書きしたり 手こずった点は山ほどある...のですが、 一先ず、このアプリの肝であるAR共有機能についてのみ解説します。

ARの共有方法

ARKitのARWorldMapを使っています。
https://developer.apple.com/documentation/arkit/arworldmap

ARWorldMapには、物理的な空間情報と空間の特徴点を記録したARanchorが含まれています。
ざっくりいうと、ARworldMap≒取得した空間内の位置情報群 と思って頂ければいいです。

このARWorldMapを利用し下図のような形で、空間情報の共有を実装しました。
firebase-connect.png

・作成ユーザーを識別する為に、Authenticationを利用。
・空間情報(ARWorldMap)とオブジェクトの情報はサイズが大きいのでCloudStorageを利用。
・その他の情報は、Firestoreを利用。
 ※図のRealtime DBは、Firestoreに変更しました。

【処理の順番】
1.空間に置いたり描いたりしたオブジェクトと、空間情報(ARWorldMap)を紐づけて表示させます。

//空間にオブジェクトを置く時の処理
//SCNodeを作成し名前をつける。(細かい設定等は省略します)
let testNode= SCNNode()
testNode.name = "testNode"

//アンカーにSCNNodeと同じ名前をつけて、タッチした場所(空間)に追加する。
let Anchor = ARAnchor(name: "testNode", transform: hitTestResult.worldTransform)
sceneView.session.add(anchor: Anchor)
self.testNodes.append(testNode)
//空間にanchorが追加された時の処理
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
  //anchorと同じ名前のSCNNodeを空間に追加する。
  for testNode in testNodes{
    if anchor.name == testNode.name {
      sceneView.scene.rootNode.addChildNode(testNode)
    }
  }
}

2.空間情報(ARworldMap)と 置いたり描いたりしたオブジェクトの情報を アーカイブし、共有したい人がアップロードします。

//Apple公式
func writeWorldMap(_ worldMap: ARWorldMap, to url: URL) throws {
 let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true) try data.write(to: url) 
}
//①ARachor(ARworldMap)を取得→②ARachor(ARworldMap)をアーカイブ(シリアライズ)してdata化 →③dataをURLにアップロードの流れ
//アプリ内に記載したコード
func dataExport(){
    //AR_DATA(ARworldMap) Export
    self.sceneView.session.getCurrentWorldMap { (worldMap, error) in
        guard let worldMap = worldMap else { print("Error getting current world map."); return }
        do {
            self.ar_data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
        } catch let error as NSError { print("failed to write: \(error)")}
    }

    //OBJECT_DATA(オブジェクト) Export
    let objectData = self.testNodes
    do {
        self.object_data = try NSKeyedArchiver.archivedData(withRootObject: objectData, requiringSecureCoding: true)
    } catch let error as NSError { print("failed to write: \(error)") }
}

//この後 FirebaseのFirestoreにアップロードしてます。

3.最後に、共有されたい人がインポートすることで、空間を再現します。

//Apple公式
func loadWorldMap(from url: URL) -> ARWorldMap throws {
 let mapData = try Data(contentsOf: mapURL) guard let worldMap = try NSKeyedUnarchiver.unarchivedObject(of: ARWorldMap.classForKeyedUnarchiver(), from: mapData) as? ARWorldMap else { throw ARError.invalidWorldMap } return worldMap 
}
//④dataをURLからダウンロード→⑤dataをアンアーカイブ(デシリアライズ)してARachor(ARworldMap)化→⑥現在の環境にARachor(ARworldMap)を反映
//アプリ内に記載したコード
//この前に FirebaseのFirestoreからダウンロードしてます。(downloadsPath_AR_DATA,downloadsPath_OBJECT_DATA)
func dataImport(){
  //OBJECT_DATA(オブジェクト) Import
  let unarchievedOBJECT_DATA = try? NSKeyedUnarchiver.unarchiveObject(with:Data(contentsOf:downloadsPath_OBJECT_DATA))
  guard let downloadtestNodes:[SCNNode] = unarchievedOBJECT_DATA as? [SCNNode] else { return }
  for testNode in downloadtestNodes {
    self.testNodes.append(testNode)
  }

  //AR_DATA(ARworldMap) Import
  let unarchievedAR_DATA = try? NSKeyedUnarchiver.unarchiveObject(with:Data(contentsOf:downloadsPath_AR_DATA))
  guard let newWorldMap = unarchievedAR_DATA as? ARWorldMap else {return}

  //downloadしたARWorldMapを反映
  self.configuration.initialWorldMap = newWorldMap
  self.sceneView.session.run(self.configuration)
}

まとめ

AR開発は成果物が空間に目に見えて現れるのが面白いです。
普段はディレクションやサービス設計がメインなので、アプリ開発自体も楽しいです。

ここまで読んで頂きありがとうございました!
※今回のアプリは、ちゃんとしたキャラクターが完成すればリリースする予定です。

27
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
17