1. 程式人生 > >【iOS-ARKit】建立多使用者AR體驗-Creating a Multiuser AR Experience

【iOS-ARKit】建立多使用者AR體驗-Creating a Multiuser AR Experience

使用MultipeerConnectivity框架在附近裝置之間傳輸ARKit世界地圖資料以建立AR體驗的共享基礎。

Overview

此示例應用程式演示了兩個或更多iOS 12裝置的簡單共享AR體驗。 在探索程式碼之前,請嘗試構建並執行應用,以熟悉它演示的使用者體驗:

  1. 在一臺裝置上執行應用程式。 您可以檢視本地環境,然後點選以在真實世界的表面上放置虛擬3D角色。 (再次點選以放置該角色的多個副本。)
  2. 在第二個裝置上執行應用程式。 在兩個裝置螢幕上,一條訊息表明它們已自動加入共享會話。
  3. 點選一個裝置上的傳送世界地圖按鈕。 確保其他裝置位於傳送地圖之前第一臺裝置訪問的區域,或者具有與周圍環境類似的檢視。
  4. 另一臺裝置顯示一條訊息,指示它已收到地圖並正在嘗試使用它。 當這個過程成功時,兩個裝置都會在相同的真實世界位置上顯示虛擬內容,並且在任一裝置上點選都會將虛擬內容視為可見。

按照以下步驟檢視此應用程式如何使用ARWorldMap類儲存和恢復ARKit的空間對映狀態,以及MultipeerConnectivity框架在附近裝置之間傳送世界地圖。

執行AR會話並放置AR內容

此應用程式擴充套件了構建ARKit應用程式的基本工作流程。 (有關詳細資訊,請參閱構建您的第一個AR體驗。)它定義了啟用了平面檢測的ARWorldTrackingConfiguration,然後在附加到顯示AR體驗的ARSCNView的ARSession中執行該配置。

當UITapGestureRecognizer在螢幕上檢測到輕擊時,handleSceneTap方法使用ARKit命中測試在真實世界表面上查詢3D點,然後放置標記該位置的ARAnchor。 當ARKit呼叫委託方法ARSCNView時,應用程式會載入ARSCNView的3D模型以顯示在錨點的位置。

連線到同樣的裝置

示例MultipeerSession類提供了有關此應用程式使用的MultipeerConnectivity功能的簡單抽象。 在主檢視控制器建立MultipeerSession例項(應用程式啟動時)後,它開始執行MCNearbyServiceAdvertiser以廣播裝置加入多對等會話的能力,並通過MCNearbyServiceBrowser查詢其他裝置:

session = MCSession(peer: myPeerID, securityIdentity: nil, encryptionPreference: .required)
session.delegate = self

serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerID, discoveryInfo: nil, serviceType: MultipeerSession.serviceType)
serviceAdvertiser.delegate = self
serviceAdvertiser.startAdvertisingPeer()

serviceBrowser = MCNearbyServiceBrowser(peer: myPeerID, serviceType: MultipeerSession.serviceType)
serviceBrowser.delegate = self
serviceBrowser.startBrowsingForPeers()

當MCNearbyServiceBrowser找到另一個裝置時,它會呼叫 browser:foundPeer:withDiscoveryInfo: delegate方法。 要將其他裝置邀請到共享會話,請呼叫browser的 invitePeer:toSession:withContext:timeout:

public func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String: String]?) {
    // Invite the new peer to the session.
    browser.invitePeer(peerID, to: session, withContext: nil, timeout: 10)
}

當其他裝置收到該邀請時,MCNearbyServiceAdvertiser將呼叫 advertiser:didReceiveInvitationFromPeer:withContext:invitationHandler: delegate方法。 要接受邀請,請呼叫所提供的invitationHandler:

func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
    // Call handler to accept invitation and join the session.
    invitationHandler(true, self.session)
}

重要:此應用會自動加入找到的第一個附近會話。 根據您想要建立的共享AR體驗的種類,您可能希望更精確地控制廣播,邀請和接受行為。 有關詳細資訊,請參閱MultipeerConnectivity文件

在多人會議中,所有參與者根據定義都是平等的; 沒有將裝置明確分離為主機(或伺服器或主機)和來賓(或客戶機或從機)角色。 但是,您可能希望為您自己的AR體驗定義這些角色。 例如,多人遊戲設計可能需要伺服器角色來仲裁遊戲玩法。 如果您需要按角色分隔對等點,則可以選擇適合您應用設計的方式。 例如:

  • 讓使用者在開始會話之前選擇是作為主機還是來賓。 主機使用MCNearbyServiceAdvertiser廣播可用性,客人使用MCNearbyServiceBrowser查詢要加入的主機。
  • 以同齡人身份加入會話,然後在同伴之間進行協商以提名主人。 (這種方法對於需要主角色的設計是有幫助的,但也允許同行隨時加入或離開。)

捕獲併發送AR World Map

ARWorldMap物件包含ARKit用於在真實世界空間中定位使用者裝置的所有空間對映資訊的快照。 將地圖可靠地共享到其他裝置需要兩個關鍵步驟:找到拍攝地圖並捕獲和傳送地圖的好時機。

ARKit提供了一個worldMappingStatus值,該值指示當前是捕獲世界地圖的好時機(還是等到ARKit已對映更多本地環境為好)。 該應用使用該值為其“傳送世界地圖”按鈕提供視覺反饋:

switch frame.worldMappingStatus {
case .notAvailable, .limited:
    sendMapButton.isEnabled = false
case .extending:
    sendMapButton.isEnabled = !multipeerSession.connectedPeers.isEmpty
case .mapped:
    sendMapButton.isEnabled = !multipeerSession.connectedPeers.isEmpty
}
mappingStatusLabel.text = frame.worldMappingStatus.description

當用戶點擊發送世界地圖按鈕時,應用程式呼叫 getCurrentWorldMapWithCompletionHandler: 從正在執行的ARSession捕獲地圖,然後使用NSKeyedArchiver將其序列化為資料物件,並將其傳送到多對話會話中的其他裝置:

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") }
    self.multipeerSession.sendToAllPeers(data)
}

接收並重新定位到共享地圖

當裝置接收多方會話中另一個參與方傳送的資料時,session:didReceiveData:fromPeer: delegate方法提供該資料。 為了使用它,應用程式使用NSKeyedUnarchiver對ARWorldMap物件進行反序列化,然後使用該對映作為 initialWorldMap: 建立並執行一個新的ARWorldTrackingConfiguration

if let unarchived = try? NSKeyedUnarchiver.unarchivedObject(of: ARWorldMap.classForKeyedUnarchiver(), from: data),
    let worldMap = unarchived as? ARWorldMap {

    // Run the session with the received world map.
    let configuration = ARWorldTrackingConfiguration()
    configuration.planeDetection = .horizontal
    configuration.initialWorldMap = worldMap
    sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])

    // Remember who provided the map for showing UI feedback.
    mapProvider = peer
}

然後,ARKit嘗試重新定位到新的世界地圖 - 也就是說,將接收到的空間對映資訊與它感知的本地環境進行協調。 為獲得最佳效果:

  1. 在共享世界地圖之前,徹底掃描傳送裝置上的本地環境。
  2. 將接收裝置放置在傳送裝置旁邊,以便兩者都能看到相同的環境檢視。

分享AR內容和使用者操作

共享世界地圖也共享所有現有的錨點。 在這個應用程式中,這意味著只要接收裝置重新定位到世界地圖,它就會顯示傳送裝置在捕獲併發送世界地圖之前放置的所有3D角色。 但是,錄製和傳輸世界地圖並重新定位到世界地圖是耗時的,頻寬密集型操作,所以當新裝置加入會話時,您只應採取一次這些步驟。

要建立持續的共享增強現實體驗,其中每個使用者的操作都會影響其他使用者可見的增強現實場景,在每個裝置重新定位到同一世界地圖後,您應該只共享重新建立每個使用者操作所需的資訊。 例如,在這個應用程式中,使用者可以點選在場景中放置一個虛擬3D角色。 該角色是靜態的,所以將角色放置在另一個參與裝置上需要的只是角色在世界空間中的位置和方向。

這個應用程式通過在對等點之間共享ARAnchor物件來傳遞虛擬角色位置。 當一個使用者點選場景時,應用程式建立一個錨點並將其新增到本地ARSession,然後使用Data將該ARAnchor序列化並將其傳送到多對話會話中的其他裝置:

// Place an anchor for a virtual character. The model appears in renderer(_:didAdd:for:).
let anchor = ARAnchor(name: "panda", transform: hitTestResult.worldTransform)
sceneView.session.add(anchor: anchor)

// Send the anchor info to peers, so they can place the same content.
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: anchor, requiringSecureCoding: true)
    else { fatalError("can't encode anchor") }
self.multipeerSession.sendToAllPeers(data)

當其他對等方從多方會話接收資料時,它們會測試該資料是否包含歸檔的ARAnchor; 如果是的話,他們解碼並將其新增到他們的會話:

if let unarchived = try? NSKeyedUnarchiver.unarchivedObject(of: ARAnchor.classForKeyedUnarchiver(), from: data),
    let anchor = unarchived as? ARAnchor {

    sceneView.session.add(anchor: anchor)
}

這只是將動態功能新增到共享AR體驗中的一種策略 - 其他許多策略都是可能的。 選擇一個適合您的應用的使用者互動,渲染和網路要求。 例如,使用者在AR世界空間投擲投射物的遊戲可能會定義具有初始位置和速度等屬性的自定義資料型別,然後使用Swift的Codable協議將該資訊序列化為通過網路傳送的二進位制表示形式。

See Also

AR World Sharing and Persistence

ARWorldMap

來自世界追蹤AR會話的空間對映狀態和一組錨。

概覽

世界地圖中的會話狀態包括ARKit對使用者移動裝置的物理空間的瞭解(ARKit用於確定裝置的位置和方向)以及新增到會話中的任何ARAnchor物件(它可以表示檢測到的實時空間)世界功能或由您的應用程式放置的虛擬內容)。

使用 getCurrentWorldMapWithCompletionHandler 之後儲存會話的世界地圖,可以將其分配給配置的 initialWorldMap 屬性,並使用 runWithConfiguration:options: 以相同的空間感知和錨點啟動另一個會話。

通過儲存世界地圖並使用它們開始新的會話,您的應用可以新增新的AR功能:

  • 多使用者AR體驗。通過將歸檔的ARWorldMap物件傳送到附近使用者的裝置來建立共享參考框架。有了兩臺裝置可以跟蹤同一張世界地圖,您可以建立一種網路體驗,讓使用者可以看到相同的虛擬內容並與之互動。
  • 持續的AR體驗。應用程式變為不活動狀態時儲存世界地圖,然後在下次應用程式在相同物理環境中啟動時恢復它。您可以使用恢復的世界地圖中的錨點將相同的虛擬內容放置在儲存的會話的相同位置。

相關推薦

iOS-ARKit建立多使用者AR體驗-Creating a Multiuser AR Experience

使用MultipeerConnectivity框架在附近裝置之間傳輸ARKit世界地圖資料以建立AR體驗的共享基礎。 Overview 此示例應用程式演示了兩個或更多iOS 12裝置的簡單共享AR體驗。 在探索程式碼之前,請嘗試構建並執行應用,以熟悉

iOS-ARKit掃描和檢測3D物件-Scanning and Detecting 3D Objects

記錄真實世界物件的空間特徵,然後使用結果在使用者環境中查詢這些物件並觸發AR內容。 Overview 構建令人信服的AR體驗的一種方法是識別使用者環境的特徵並使用它們觸發虛擬內容的外觀。 例如,當用戶將他們的裝置指向顯示的雕塑或工件時,博物館應用可以新增

滲透課程第三篇-體驗http協議的應用

load 簡單介紹 class 發送數據 數據交互 動手實驗 服務端 yun 屬於 之前我們都了解了,訪問網頁時,客戶端與服務端之間的請求與響應數據交互。本篇就淺談它的應用。 利用HTTP攔截突破前段驗證 比方說,我們在某個網頁提交某些數據(例如留言、上傳、插入xss等)

iOS 國際化如何把國際化時需要3天的工作量縮減到10分鐘

痛點 如果 APP 要求國際化,其實新增國際化文字是很頭痛的一件事。對於一個大型 APP 來說,更是麻煩,而且工作量很大。通常替換國際化文字時,產品會給我們一個 Excel 表: excel.png 我們需要做的事,就是把 Excel 表中的文字,新增到下面各個檔案中:

實操建立雲監控報警規則

大家好,本期為大家介紹如何使用雲監控報警服務。報警服務提供監控資料的報警功能,我們通過設定報警規則來定義報警系統如何檢查監控資料,並在監控資料滿足報警條件時傳送報警通知。對我們的重要監控指標設定報警規則,可以在第一時間得知指標資料發生異常,迅速處理故障。 首先登入管理控制檯,在產品與服務→監控與管理中點選雲

TP5.1建立通用的基類自定義命令

author:咔咔 wechat:fangkangfk   在之前我們一直在做admin下的業務,在設定自定義模板的時候沒有考慮到一些情況   下面我們來改動一下 這樣做就ok了,不管是建立admin下的控制器,還是index的控制器,這一個模板就可以

緊跟時代建立asp.net core angular專案

需要安裝node.js,如何安裝請自行百度  1、使用vs2017 建立專案,選擇ASP.NET Core Web應用程式,名稱為:ASPNetAngularDemo 選擇angular專案,可以看到是 .NET Core  版本:ASP.NET Core 2.0 

學習筆記建立模式之原型模式

在某些應用程式中,某些物件(一種類別)比較複雜,且其建立過程比較複雜,並且我們需要頻繁使用到這些物件。如果我們一直使用new的方式來建立這些物件,會導致程式效率低下。 如果我們預先建立好該物件(即原型),通過該物件的自我複製(克隆)得到物件例項,從而提高程式碼效

iOS筆記AudioUnit錄音異常(聽起來類似於丟幀丟資料)

在做語音識別專案時候發現一個問題,識別率奇低無比……所以就把原始音訊資料錄下來,發現音訊丟資料。 實驗機器:iPhone6s iOS12 問題程式碼如下: OSStatus AudioUnitInput::RecordCallback( void *inRefC

SpringMVC學習指南筆記1建立bean例項的方法和依賴注入

Spring MVC 主要從Spring框架、Servlet、JSP這3個方面來講。   Java企業版技術包括JMS、EJB、JSF、JPA。   Java企業版容器:GlassFish、JBoss、Oracle、Weblogic、IBM WebSphere   T

C語言建立動態陣列

#include <iostream> #include <malloc.h> using namespace std; int main() { int *arr; int len; cout << "輸入需要建立的陣列的長度:"; cin

iOS開發iOS移動端架構

引言:一個app的初始階段,必然是先滿足各種業務需求。然後,經過多次版本迭代之後,先前的由於急於滿足需求而導致的雜亂程式碼則會充斥整個專案。而此時,專案有了一定的規模,有了一定數量的開發人員,那麼為了達到快速迭代版本的需求,則是需要有一個強大的架構來支撐。

iOS開發Gitlab教程 (一)

一 、gitLab 建立工程 Project name : 工程名字起一個,最好和專案相關; Namespace : 你自己的gitLab工作空間,預設就好; Description : 描述,隨便寫。 Visibi

iOS開發判斷app啟動的方式(launchOptions)

iOS app啟動的方式有哪些: 自己啟動(使用者手動點選啟動)urlscheme啟動(關於urlScheme的詳解點選開啟連結)本地通知啟動  (自己寫的本地通知啟動,藍芽模組的啟動,地理圍欄的啟動)遠端通知啟動    (後臺伺服器的推送通知)在appdelegate.m

iOS開發SEL和Selector 原理小結

一 、Selector(選擇器)簡介 選擇器是用來選擇一個方法來為一個物件 執行的名稱,或是在編譯原始碼時替換該名稱的 唯一識別符號的名稱。一個選擇器本身不做任何事情。它簡單地識別了一種方法。唯一使選擇器的方法名稱不同於普通字串,編譯器確保選擇器是獨特的。

設計模式--建立型模式

【前言】 設計模式主要分為三種類型,分別是:建立型模式、行為型模式、結構型模式,今天我們主要講一下建立型模式。 【正文】 1.什麼是建立型模式? 建立型模式旨在將系統與它的物件建立、結合、表示的方式分離。 2.為什麼要有建立型模式或者說建立型模式存在的意義? 建立型模式是處理

IOS學習到底什麼時候才需要在ObjC的Block中使用weakSelf/strongSelf

Objective C 的 Block 是一個很實用的語法,特別是與GCD結合使用,可以很方便地實現併發、非同步任務。但是,如果使用不當,Block 也會引起一些迴圈引用問題(retain cycle)—— Block 會 retain ‘self’,而 ‘self‘

iOS開發一些常見的警告解決方案(更新中。。。)

Unknown pattern color for the Background Color attribute 1.背景色屬性為未知模式的顏色 解決:預設xib裡面控制元件的背景色為Default。如果出現警告,可能是你定義的顏色Xcode啟動

linux學習建立虛擬機器並搭建叢集(1)

1.下載VMware Workstation 2.建立虛擬機器 其他都是預設操作,虛擬機器名字根據自己喜好。 記憶體根據電腦情況,我選擇1G的,如果自己記憶體小,可以選512M。 3.編輯虛擬機器設定 虛擬機器-設定,將CD/DVD連線設定為ISO映像檔案,我用

QAComplete教程建立測試和測試集

QAComplete為您的測試用例提供集中式的管理,讓您可以跨版本檢視自己的測試和操作缺陷,告別複雜的測試管理。本文旨在介紹在QAComplete中建立測試和測試集,供大家學習交流。 在QAComplete中,使用測試來確保完全覆蓋需求。將測試儲存在測試庫中並將它們組織到測試集中,以便以一致的邏