持久化存储与共享体验技术基础
AR数据持久化存储与体验共享是一个庞大的主题,ARKit 提供了目前业内最好的解决方案。为解决数据持久化存储的问题,ARKit 提供了 ARWorldMap 技术;为解决体验共享的问题,ARKit 提供了协作Session (Collaborative Session)技术,通过这两种技术手段将复杂的技术简单化,并给予了开发者良好的支持。在 RealityKit 中,每一个实体对象都包含一个Synchronization 组件用于同步通信,通过内建的同步支持,实现 AR体验共享更便捷。
initialWorldMap 属性
ARKit 借助 ARWorldap 技术实现 AR运行数据的持久化保存,ARWorldap 是将 ARSession 运行的状态信息进行了收集打包,这些状态信息包括设备对物理环境的感知信息、用户操作信息(用户添加的 ARAnchor)及开发者自定义的一些信息。在 ARSession 运行时可以通过 getCurrent WorldMap (completionHlandler:)方法获取这些状态信息,并可以在序列化后保存到文件系统或者数据库等介质中,或者通过网络传输到其他设备上。
为加载保存的 AR应用进程数据,ARWorldTrackingConfiguration 配置类提供了一个 initial WorldMap属性,在 ARSession 运行前力 initialWorldMap 属性设置一个 ARWorldMap,ARSession 通过run(_: Options:)方法运行时将会尝试利用 ARWorldMap 中存储的信息恢复 AR进程。 通过存储和加载 ARWorldMap,解决了 AR体验的持久化和多用户共享的技术问题。
(1)持久化存储 AR 体验。利用 ARWorldMap 可以将使用者的AR进程数据存储起来,并可以在相同的物理环境中恢复该进程,可以通过ARAnchor 恢复所有的虚拟元素,而且这些虚拟元素在物理环境中的位置和方向与之前的状态保持一致。
(2)多用户共享AR体验。可以通过网络将 ARWorldMap 发送到其他用户设备,当这些设备在相同的物理空间环境中时,通过加载ARWorldMap就可以共享这些体验,即所有设备都可以看到放置在物理环境中相同位置的虚拟元素。
给 initialWorldMap 属性赋予一个 ARWorldMap 并启动 ARSesson后,ARSession 会进入 ARCamera.TrackingState. limited (_:)状态,跟踪受限的原因是 ARCamera. TrackingState. Reason. relocalizing,即ARKit尝试进行重定位。如果重定位成功,则状态改变次 ARCamera. TrackingState. normal,即环境匹配成功,应用进程就可以恢复;如果重定位不成功,例如在完全不相同的物理环境中尝试恢复进程,这时ARSession 的状态就会持续保持受限状(ARCamera. TrackingState. Reason. relocalizing。
isCollaborationEnabled 属性
为实时通过网络传输 AR 应用进程数据,ARWorldTrackingConfiguration 配置类提供了一个isCollaborationEnabled 属性,该属性为布尔值类型,用于标识是否通过点对点网络传输 AR 进程数据。
该值默认为 false,在需要时可以将其设置 true,当设置为 true 时,ARKit 会周期性调用 session(-:didOutputCollaborationData:)方法,通过这个方法就可以将 AR 运行时数据(Collaboration Data)共享给其他用户。Collaboration Data 数据包括 ARKit 检测到的物理表面特征信息、当前用户设备的姿态信息、用户添加的 ARAnchor 信息。
通过 Collaboration Data,多个用户之间可以实时地共享 AR 体验数据,包括对 ARAnchor 的操作数据。ARKit 会融合所有参与用户对物理环境的感知数据,从而增强和加快了 ARKit 对环境的探索进程。在使用协作 Session (Collaborative Session)时,开发人员需要负责 Collaboration Data 数据的发送与接收处理,在进行数据发送时,所有传输的数据(ARSession. CollaborationData)都必须序列化,获取并发送数据的典型示例代码如下所示。
func saveWorldMap() {
print("save:\(String(describing: arView))")
self.arView?.session.getCurrentWorldMap(completionHandler: {[weak self] loadWorld, error in
guard let worldMap = loadWorld else {
print("当前无法获取ARWorldMap:\(error!.localizedDescription)")
return
}
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
self?.multipeerSession?.sendToAllPeers(data, reliably: true)
print("ARWorldMap已发送")
} catch {
fatalError("无法序列化ARWorldMap: \(error.localizedDescription)")
}
})
}