ARKit

ARKit 2.0 教學:儲存並恢復世界地圖數據 建立更連貫的 AR 體驗

ARKit 2.0 教學:儲存並恢復世界地圖數據 建立更連貫的 AR 體驗
ARKit 2.0 教學:儲存並恢復世界地圖數據 建立更連貫的 AR 體驗
In: ARKit, Swift 程式語言

歡迎來到第 8 回 ARKit 教學系列的文章! 隨著 iOS 12 的發佈,現在 ARKit 能夠儲存世界地圖 (World Map) 的數據了。 以前,我們無法儲存 AR 世界地圖的數據,現在 iOS 12 讓開發者能夠建立儲存 AR 體驗的能力。 若你有興趣學習建立在擴增實境 (Augmented Reality, AR) 中儲存世界地圖數據的 App,這篇教程你就不容錯過了。

以下就是你即將開始建立的 App。

arkit-20-demo-1

你可以從圖片了解,我所說的儲存世界地圖數據 (Data),指的是你可以先儲存 AR 世界地圖,然後即使關閉了 app,仍可稍後回復地圖數據;這是 iOS 11 無法做到的。現在只要把世界地圖的數據儲存起來,你就能讓使用者回到之前 AR 體驗的時間點。

先決條件

在開始實作之前,你需要對之前的 ARKit 教學 內容有基本了解。如果你對 ARKit 還很陌生,請先看看我們的 ARKit 系列教程.

為配合本次教學,你需要準備 Xcode 10 beta 或更新版本,而 Apple 的裝置也需要 iOS 12 beta 或更新版本。

好,現在讓我們開始吧!

開始實作

首先,在此下載起始專案,這專案已有預先寫好的 UI 元件及方法 (Method),這樣我們就能夠專注在儲存 ARKit 世界地圖的核心元件開發。當你下載完成之後,在 iOS 裝置上建置並執行專案。過程中,一個視窗會彈出,詢問你是否允許此 App 使用相機,點選 OK 來允許。

arkit-20-starter-proj

好了! 現在讓我們談一下甚麼是 ARWorldMap 物件,以及如何運用 ARWorldMap 物件來取得 ARKit 世界地圖數據。

使用 ARWorldMap

ARWorldMap 物件包含所有空間地圖信息的快照,在真實世界中,ARKit 也是使用這些信息來定位裝置的所在位置。

ARWorldMap 物件所做的一如其名,它代表著現實世界中的一個地理位置。當你使用 ARWorldMap 時,你可以將 ARWorldMap 物件轉換成 Data 物件並儲存,然後儲存於裝置目錄裡。之後,你可以到當初儲存世界地圖 Data 物件的目錄,將它解壓轉換回 ARWorldMap 物件。要恢復這地圖,你需要將世界追蹤配置 (Configuration) 的初始世界地圖,設置為之前儲存的 ARWorldMap 物件。

這就是我們使用 ARWorldMap 物件的方法。現在,讓我們來實作一個範例 App。

設定世界地圖本機文件目錄 (Local Document Directory)

首先,宣告一個 URL 型別的變數,它為我們提供文件目錄的存取路徑,用於寫入與讀取世界地圖數據。然後,為 ViewController 類別新增屬性:

var worldMapURL: URL = {
    do {
        return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("worldMapURL")
    } catch {
        fatalError("Error getting world map URL from document directory.")
    }
}()

指定好世界地圖的 URL 後,讓我們建立一個世界地圖數據儲存器 (Archiver) 方法,並寫入我們本機文件目錄中。

儲存 AR 世界地圖為數據

現在你將創建一個儲存器方法,來儲存你的 ARWorldMap 物件。插入下列程式碼到 ViewController 類別:

func archive(worldMap: ARWorldMap) throws {
    let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
    try data.write(to: self.worldMapURL, options: [.atomic])
}

儲存了世界地圖為數據物件後,把這數據物件寫到本機目錄下。我們使用 .atomic 選項 (Option),這樣就能確定文檔是否已完全寫入到裝置內。這方法包含簽名 (Signature) 的 throws 敘述,因為在數據寫入本機文件目錄時,有可能因空間不足或其他緣故,而回傳錯誤訊息。

完成世界地圖儲存器方法後,讓我們實作從 Scene View 中儲存世界地圖。

將 AR 世界地圖數據儲存於本機文件目錄

Apple 提供了一個方便的方法,協助我們取得當前 Session 的世界地圖。利用下列的方式來更新 saveBarButtonItemDidTouch(_:)

@IBAction func saveBarButtonItemDidTouch(_ sender: UIBarButtonItem) {
    
    sceneView.session.getCurrentWorldMap { (worldMap, error) in
        guard let worldMap = worldMap else {
            return self.setLabel(text: "Error getting current world map.")
        }
        
        do {
            try self.archive(worldMap: worldMap)
            DispatchQueue.main.async {
                self.setLabel(text: "World map is saved.")
            }
        } catch {
            fatalError("Error saving world map: \(error.localizedDescription)")
        }
    }
}

我們 Scene View 的 Session 包含一個方法,讓我們輕易取得當前的世界地圖。在 getCurrentWorldMap 閉包 (Closure) 中,我們安全地解開回傳的可選性 ARWorldMap 物件。在 ARWorldMap 物件不再存在的情況下,我們將簡單地回傳並設置顯示錯誤訊息的標籤文本 (Label Text)。

在安全解開 ARWorldMap 物件後,我們宣告一個 do-catch 敘述。如果錯誤訊息是由 do 中的程式碼產生,我們會在 catch clause 中處理這個錯誤。此時,我們會中斷執行程式碼,印出錯誤訊息,以進行除錯 (Debug)。

建置並執行 App,掃描你的執行環境,然後在裝置上點擊新增一個或多個球體到 Scene。點擊 Save 按鈕時,請確認你的標籤顯示 “World map is saved” 字樣。現在當前的 AR 世界地圖已經儲存好了。

從你的文件目錄中下載 AR 世界地圖數據

成功儲存一個 ARWorldMap 物件後,現在我們要創建一個方法,來解開文件目錄中的 ARWorldMap 數據,並將它下載到我們的 Scene 上。

在我們解壓轉換 DataARWorldMap 物件前,我們先要從文件目錄中取得這世界地圖的數據。

將下列方法新增到你的 ViewController 類別:

func retrieveWorldMapData(from url: URL) -> Data? {
    do {
        return try Data(contentsOf: self.worldMapURL)
    } catch {
        self.setLabel(text: "Error retrieving world map data.")
        return nil
    }
}

上面的程式碼宣告了一個 do-catch 敘述,以嘗試從世界地圖 URL 中取回 Data 物件。在程式碼出現在 catch 句時,我們只需使用錯誤訊息設置標籤。

我們已寫好了一個方法,幫助我們藉著世界地圖 URL,從文件目錄中讀取這些數據。現在是時候試著去把回傳的 Data 物件解壓轉換回 ARWorldMap 物件了。

首先,將下列方法新增到 ViewController 類別:

func unarchive(worldMapData data: Data) -> ARWorldMap? {
    guard let unarchievedObject = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: data),
        let worldMap = unarchievedObject else { return nil }
    return worldMap
}

我們用 NSKeyedUnarchiver 去嘗試解開這數據物件的封包,然後傳給 unarchive(worldMapData:) 方法。如果封包的解壓過程順利,而且被解壓的 ARWorldMap 物件不是 Nil,我們就可以安全地回傳解壓了的 ARWorldMap 物件。

現在解壓方法已經準備好,讓我們來以下列程式碼更新 loadBarButtonItemDidTouch(_:) 吧!

@IBAction func loadBarButtonItemDidTouch(_ sender: UIBarButtonItem) {
    guard let worldMapData = retrieveWorldMapData(from: worldMapURL),
        let worldMap = unarchive(worldMapData: worldMapData) else { return }
    resetTrackingConfiguration(with: worldMap)
}

現在無論我們何時點擊 Load 按鈕,我們都呼叫 retrieveWorldMapData 方法,來根據指定的 URL 位置取回世界地圖數據。成功取回後,我們就解開世界地圖數據的封包,將它轉換為 ARWorldMap 物件。之後,我們呼叫 resetTrackingConfiguration 方法,把下載好的數據恢復成原來的 AR 世界地圖。

設定 Scene View 配置中的初始世界地圖

在宣告 options 常數後面,新增下列程式碼到 resetTrackingConfiguration(with:) 中:  

    if let worldMap = worldMap {
        configuration.initialWorldMap = worldMap
        setLabel(text: "Found saved world map.")
    } else {
        setLabel(text: "Move camera around to map your surrounding space.")
    }

上述程式碼將 Scene View 配置的初始世界地圖設置為世界地圖參數 (Parameter)。 然後我們更新文字標籤,顯示世界地圖已經被找到了;反之,我們就會設置一個文字標籤,引導使用者用相機拍下周遭環境,建立圖檔。

完成了! 讓我們展示範例吧!

演示

在展示範例的視頻中,使用者點擊了螢幕來新增一個球體到 Scene View。 然後,使用者按下 Save 按鈕來儲存 Scene View 當前的世界地圖。成功儲存後,文字標籤會顯示 “World map is saved”。 使用者點擊 Load 按鈕後,這被儲存的世界地圖就會成功地被載到 Scene View 上。

很有趣吧?

總結

恭喜你讀完了本次教學! 希望你在閱讀本篇教程中獲得樂趣,並有所收穫。 歡迎將教學分享到社交平台上,讓朋友圈也能獲得有用的知識!

如果有興趣了解更多,可以在 GitHub 下載完整的 Xcode 專案。

譯者簡介: 孟慶璋,專長 MES 系統導入與開發及 6 Sigma 導入與輔導。 過去曾從事軟體研發,有半導體、LCD、太陽能及 PCB 產業實務經驗。 雖有 Android 開發經驗,但熱愛 iOS App 開發。

連絡方式: 電郵 [email protected]

原文: ARKit 2.0 Tutorial: Saving and Restoring World-mapping Data to Create a Persistence AR Experience

作者
Jayven N
年輕、富創意的Jayven熱愛手機程式設計,善於發掘和凸顯不平凡處,透過文章抒發其獨特性。閒時喜歡做健身、看UFC。希望了解Jayven更多,可以到訪他的Medium平台或在LinkedIn跟他聯繫。
評論
更多來自 AppCoda 中文版
透過 Reality Composer 和 RealityKit 輕鬆地創建 3D AR Apps
ARKit

透過 Reality Composer 和 RealityKit 輕鬆地創建 3D AR Apps

RealityKit 是 2019 年推出的新框架,用於實作高性能 3D 模擬和渲染功能,而 Reality Composer 就讓初學者無需編寫任何程式碼,都可以輕鬆地創建互動的 AR 體驗。在這篇文章中,你將學會使用這兩個框架,構建互動的 3D AR App。
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。