4

ARKit很新,我在 swift 中很新……所以我遇到了一些麻烦……

我想保存ARPlaneAnchor在会话期间检测到的内容,并在重新启动我的应用程序时重新加载它们。我的手机总是在同一个地方,我想扫描一下房间。并记住每次启动应用程序时在房间里找到的 Anchor。

我尝试了几种解决方案:

解决方案 1: 使用以下方法保存 ARPlaneAnchor:NSKeyedArchiver.archiveRootObject(plane, toFile: filePath)

我收到了这个错误:

由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'-[ARPlaneAnchor encodeWithCoder:]:无法识别的选择器发送到实例

我想也许我无法在本地保存这种数据

解决方案2:ARPlaneAnchor在我启动应用程序时存储然后实例化它们的数据。数据主要是浮动的。我可以ARAnchor轻松创建,我可以将它们转换为ARPlaneAnchor,但我无法修改 的“中心”和“扩展”参数,ARPlaneAnchor因为它们只有一个 getter 而不是一个 setter。所以我不能创造好的锚。

我愿意接受任何解决方案。我想我需要存储 ARAnchor 对象,但现在我找不到不崩溃的方法!因此,如果有人可以帮助我,我将不胜感激。

4

1 回答 1

8

首先...如果您的应用程序仅限于设备被永久安装并且用户永远无法移动或旋转它的情况,那么使用 ARKit 在相机源上显示覆盖内容有点像“用大炮杀死蚊子”的那种的情况。您也可以在开发时确定您的 3D 引擎需要哪种相机投影,使用“哑”相机馈送并在顶部运行您的 3D 引擎,并且不需要 iOS 11 或支持 ARKit 的设备。

因此,在您承诺特定的解决方案和变通方法之前,您可能需要多考虑一下您的用例或技术堆栈。


至于你更具体的问题......

ARPlaneAnchor完全是一个只读类,因为它的用例完全是只读的。它存在的唯一目的是为 ARKit 提供一种向您提供有关检测到的平面的信息的方法。然而,一旦你有了这些信息,你就可以随心所欲地使用它。从那里开始,您不再需要保留ARPlaneAnchor在等式中。

也许您会因为平面检测(和基于 SceneKit 的显示)的典型用例而感到困惑:

  1. 开启平面检测
  2. 响应renderer(_:didAdd:for:)接收ARPlaneAnchor对象
  3. 在该方法中,返回虚拟内容以与平面锚点关联
  4. ARSCNView自动为您定位该内容,使其跟随飞机的位置

但是,如果您的飞机相对于相机的位置是静态的,则您不需要所有这些。

如果放置需要持续管理,您只需要 ARKit 来处理场景中内容的放置,例如平面检测是实时的(ARKit 改进其对平面位置和范围的估计并相应地更新锚点)。如果您提前完成了所有的飞机查找,您将不会获得更新,因此您不需要 ARKit 来管理更新。

相反,您的步骤可能看起来更像这样:

  1. 知道飞机在哪里(在世界空间中的位置)。
  2. 将您的虚拟内容的位置设置为平面的位置。
  3. 直接将内容添加到场景中。

换句话说,您的“解决方案 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)
    }
}
于 2017-07-25T21:23:25.400 回答