简体   繁体   English

Swift:ARKit保存ARPlaneAnchor用于下一场比赛

[英]Swift : ARKit Save ARPlaneAnchor for next session

ARKit is quite new and I am quite new in swift... So I'm having some troubles... ARKit很新,我很快就开始......所以我遇到了一些麻烦......

I'd like to save the ARPlaneAnchor detected during a session and reload them when I relaunch my app. 我想保存会话期间检测到的ARPlaneAnchor ,并在重新启动应用程序时重新加载它们。 My phone will always be at the same place and I'd like to scan the room one time. 我的手机总是在同一个地方,我想一次扫描房间。 And remembering the Anchor I found in the room everytime I launch the app. 记住我每次启动应用程序时在房间里找到的Anchor。

I tried several solutions : 我试过几个解决方案:

Solution1 : Save the ARPlaneAnchor using : NSKeyedArchiver.archiveRootObject(plane, toFile: filePath) 解决方案1:使用以下命令保存ARPlaneAnchor: NSKeyedArchiver.archiveRootObject(plane, toFile: filePath)

I got this error : 我收到了这个错误:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ARPlaneAnchor encodeWithCoder:]: unrecognized selector sent to instance 因未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:' - [ARPlaneAnchor encodeWithCoder:]:发送到实例的无法识别的选择器

I think that maybe I can't save this kind of data locally 我想也许我无法在本地保存这种数据

Solution2 : Store the datas of the ARPlaneAnchor then intantiate them when I launch the app. 解决方案2:存储ARPlaneAnchor的数据,然后在我启动应用程序时ARPlaneAnchor进行实例化。 the datas are mainly float. 数据主要是浮动的。 I could creat ARAnchor easily, I could cast them as ARPlaneAnchor , but I could not modify the "center" and "extend" parameter of the ARPlaneAnchor because they only have a getter and not a setter. 我可以创造ARAnchor容易,我能投他们作为ARPlaneAnchor ,但我不能修改“中心”和“扩展”的参数ARPlaneAnchor ,因为他们只有一个getter,而不是制定者。 So I can't create the good anchors. 所以我无法创造出好的锚点。

I am open to anysolution. 我愿意接受解决方案。 I think I need to store the ARAnchor object, but for now I could not find a way to do it without a crash! 我想我需要存储ARAnchor对象,但是现在我找不到一个没有崩溃的方法! So if someone can help me I would be very grateful. 所以,如果有人能帮助我,我将非常感激。

First... if your app is restricted to a situation where the device is permanently installed and the user can never move or rotate it, using ARKit to display overlay content on the camera feed is sort of a "killing mosquitos with a cannon" kind of situation. 首先......如果您的应用仅限于设备永久安装并且用户永远无法移动或旋转它的情况,使用ARKit在相机上显示叠加内容就像是“用大炮杀死蚊子”类型情况。 You could just as well work out at development time what kind of camera projection your 3D engine needs, use a "dumb" camera feed with your 3D engine running on top, and not need iOS 11 or an ARKit-capable device. 您还可以在开发时确定您的3D引擎需要什么样的相机投影,使用“哑”相机输入,并在顶部运行3D引擎,而不需要iOS 11或支持ARKit的设备。

So you might want to think about your use case or your technology stack some more before you commit to specific solutions and workarounds. 因此,在提交特定解决方案和解决方法之前,您可能需要考虑更多用例或技术堆栈。


As for your more specific problem... 至于你更具体的问题......

ARPlaneAnchor is entirely a read-only class, because its use case is entirely read-only. ARPlaneAnchor完全是一个只读类,因为它的用例完全是只读的。 It exists for the sole purpose of giving ARKit a way to give you information about detected planes. 它的存在的唯一目的是为ARKit提供一种方法来为您提供有关检测到的飞机的信息。 However, once you have that information, you can do with it whatever you want. 但是,一旦掌握了这些信息,您就可以随心所欲地使用它。 And from there on, you don't need to keep ARPlaneAnchor in the equation anymore. 从那以后,你不再需要将ARPlaneAnchor保留在等式中。

Perhaps you're confused because of the typical use case for plane detection (and SceneKit-based display): 也许你因为平面检测的典型用例(以及基于SceneKit的显示)而感到困惑:

  1. Turn on plane detection 打开平面检测
  2. Respond to renderer(_:didAdd:for:) to receive ARPlaneAnchor objects 响应renderer(_:didAdd:for:)以接收ARPlaneAnchor对象
  3. In that method, return virtual content to associate with the plane anchor 在该方法中,返回虚拟内容以与平面锚点关联
  4. Let ARSCNView automatically position that content for you so it follows the plane's position ARSCNView自动为您定位内容,使其跟随飞机的位置

If your plane's position is static with respect to the camera, though, you don't need all that. 但是,如果您的飞机相对于相机的位置是静态的,那么您并不需要这一切。

You only need ARKit to handle the placement of your content within the scene if that placement needs ongoing management , as is the case when plane detection is live (ARKit refines its estimates of plane location and extent and updates the anchor accordingly). 如果该位置需要持续管理 ,您只需要ARKit来处理场景中的内容放置,就像平面检测处于活动状态时一样(ARKit会优化其对平面位置和范围的估计并相应地更新锚点)。 If you did all your plane-finding ahead of time, you won't be getting updates, so you don't need ARKit to manage updates. 如果您提前完成了所有的飞机检查,那么您将无法获得更新,因此您不需要ARKit来管理更新。

Instead your steps can look more like this: 相反,您的步骤可能看起来更像这样:

  1. Know where a plane is (position in world space). 知道飞机的位置(世界空间中的位置)。
  2. Set the position of your virtual content to the position of the plane. 将虚拟内容的位置设置为平面的位置。
  3. Add the content to the scene directly. 直接将内容添加到场景中。

In other words, your "Solution 2" is a step in the right direction, but not far enough. 换句话说,你的“解决方案2”是朝着正确方向迈出的一步,但还不够远。 You want to archive not an ARPlaneAnchor instance itself, but the information it contains — and then when unarchiving, you don't need to re-create an ARPlaneAnchor instance, you just need to use that information. 您希望归档不是ARPlaneAnchor实例本身,而是归档它包含的信息 - 然后在取消归档时,您不需要重新创建ARPlaneAnchor实例,您只需要使用该信息。

So, if this is what you do to place content with "live" plane detection: 那么,如果您正在使用“实时”平面检测来放置内容:

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)
}

Then you can do something like this for static content placement: 然后,您可以为静态内容放置执行以下操作:

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)
    }
}

Along with the above method of actually saving plane anchors as objects to reload later, a more robust way to reload the planes in the right positions the second time you open the app is to use some sort of accurate localization system that can place your planes persistently in the right positions. 除了以上实际保存平面锚点作为对象以便稍后重新加载的方法之外,第二次打开应用程序时,更加强大的方法将平面重新加载到正确的位置是使用某种精确的定位系统,可以永久地放置您的飞机在正确的位置。

This article is a good reference on creating "persistent" scenes by saving any SCNNode in a physical space. 本文是通过将任何SCNNode保存在物理空间中来创建“持久”场景的良好参考。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM