ARKit
很新,我很快也很新...所以我遇到了一些麻烦...
我想保存在 session 中检测到的ARPlaneAnchor
,并在重新启动应用程序时重新加载它们。我的手机将始终在同一地点,我想扫描一下房间。并记得每次启动应用程序时在房间中找到的 anchor 。
我尝试了几种解决方案:
解决方案1:
使用以下命令保存ARPlaneAnchor:NSKeyedArchiver.archiveRootObject(plane, toFile: filePath)
我收到此错误:
我认为也许我无法在本地保存此类数据
解决方案2:存储ARPlaneAnchor
的数据,然后在启动应用程序时对其进行实例化。数据主要是 float 的。我可以轻松创建ARAnchor
,可以将它们转换为ARPlaneAnchor
,但是我无法修改ARPlaneAnchor
的“center”和“extend”参数,因为它们只有一个getter而不是setter。所以我不能创造好的 anchor 。
我愿意接受任何解决方案。我想我需要存储ARAnchor对象,但是到目前为止,我无法找到一种方法来避免崩溃!
因此,如果有人可以帮助我,我将非常感激。
最佳答案
首先...如果您的应用仅限于永久安装设备且用户永远无法移动或旋转设备的情况,那么使用ARKit在相机Feed上显示叠加内容就是一种“用大炮消灭蚊子”的方式的情况。您也可以在开发时确定3D引擎需要什么样的相机投影,在3D引擎顶部运行时使用“哑”相机源,并且不需要iOS 11或具有ARKit的设备。
因此,您可能需要先考虑用例或技术堆栈,然后再提交特定的解决方案和解决方法。
至于您更具体的问题...ARPlaneAnchor
完全是一个只读类,因为它的用例是完全只读的。它存在的唯一目的是为ARKit提供一种向您提供有关检测到的平面的信息的方法。但是,一旦有了这些信息,就可以随便使用它。从那以后,您不再需要将ARPlaneAnchor
保留在公式中。
也许由于平面检测(以及基于SceneKit的显示)的典型用例而感到困惑:
renderer(_:didAdd:for:)
以接收ARPlaneAnchor
对象ARSCNView
自动为您定位该内容,使其遵循飞机位置但是,如果飞机相对于相机的位置是静态的,则不需要所有这些。
仅在现场需要实时管理的情况下,才需要ARKit来处理内容在场景中的放置,例如实时检测平面时(ARKit会优化其对平面位置和范围的估计并相应地更新 anchor )。如果您提前进行了所有飞机搜索,则不会获得更新,因此不需要ARKit来管理更新。
相反,您的步骤可以看起来像这样:
换句话说,您的“解决方案2”是朝正确方向迈出的一步,但还远远不够。您不是要存档一个
ARPlaneAnchor
实例本身,而是要存档它包含的信息,然后在取消存档时,不需要重新创建ARPlaneAnchor
实例,只需要使用该信息。因此,如果这是通过“实时”平面检测放置内容的方法:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
let extent = planeAnchor.extent
let center = planeAnchor.center
// planeAnchor.transform not used, because ARSCNView automatically applies it
// to the container node, and we make a child of the container node
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = .pi / 2
planeNode.simdPosition = center
node.addChildNode(planeNode)
}
然后,您可以对静态内容放置执行以下操作:
struct PlaneInfo { // something to save and restore ARPlaneAnchor data
let transform: float4x4
let center: float3
let extent: float3
}
func makePlane(from planeInfo: PlaneInfo) { // call this when you place content
let extent = planeInfo.extent
let center = float4(planeInfo.center, 1) * planeInfo.transform
// we're positioning content in world space, so center is now
// an offset relative to transform
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = .pi / 2
planeNode.simdPosition = center.xyz
view.scene.rootNode.addChildNode(planeNode)
}
// convenience vector-width conversions used above
extension float4 {
init(_ xyz: float3, _ w: Float) {
self.init(xyz.x, xyz.y, xyz.z, 1)
}
var xyz: float3 {
return float3(self.x, self.y, self.z)
}
}