ARKit
很新,而且我很快就是新人......所以我有一些麻烦...
我想保存会话期间检测到的ARPlaneAnchor
,并在重新启动应用时重新加载它们。我的手机总是在同一个地方,我想一次扫描房间。并记住我每次启动应用程序时在房间里找到的Anchor。
我尝试了几种解决方案:
解决方案1:
使用:NSKeyedArchiver.archiveRootObject(plane, toFile: filePath)
我收到了这个错误:
由于未捕获的异常终止应用程序' NSInvalidArgumentException',原因:' - [ARPlaneAnchor encodeWithCoder:]:无法识别的选择器发送到实例
我想也许我无法在本地保存这类数据
解决方案2:存储ARPlaneAnchor
的数据,然后在启动应用时对其进行实例化。数据主要是浮动的。我可以轻松地创建ARAnchor
,我可以将它们转换为ARPlaneAnchor
,但我无法修改"中心"并且"延伸" ARPlaneAnchor
的参数,因为它们只有一个getter而不是setter。所以我无法创造出好的锚点。
我愿意接受解决方案。我想我需要存储ARAnchor对象,但是现在我找不到一个没有崩溃的方法! 所以,如果有人能帮助我,我将非常感激。
答案 0 :(得分:7)
首先......如果您的应用仅限于设备永久安装并且用户永远无法移动或旋转它的情况,使用ARKit在相机Feed上显示叠加内容就像是“杀死蚊子大炮“有点情况。您还可以在开发时确定您的3D引擎需要什么样的相机投影,使用“哑”相机输入,并在顶部运行3D引擎,而不需要iOS 11或支持ARKit的设备。
因此,在提交特定解决方案和解决方法之前,您可能需要考虑一下您的用例或技术堆栈。
至于你更具体的问题......
ARPlaneAnchor
完全是一个只读类,因为它的用例完全是只读的。它的存在的唯一目的是为ARKit提供一种方法来为您提供有关检测到的飞机的信息。但是,一旦掌握了这些信息,您就可以随心所欲地使用它。从那以后,您不再需要将ARPlaneAnchor
保留在等式中。
由于平面检测的典型用例(以及基于SceneKit的显示),也许您感到困惑:
renderer(_:didAdd:for:)
以接收ARPlaneAnchor
个对象ARSCNView
自动为您定位内容,使其跟随飞机的位置但是,如果您的飞机相对于相机的位置是静止的,那么您并不需要这一切。
如果该位置需要持续管理,您只需要ARKit来处理场景中的内容放置,就像平面检测处于活动状态时一样(ARKit会优化其对平面位置和范围的估计)并相应地更新锚点。如果您提前完成所有飞机检查,则不会获得更新,因此您无需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)
}
}
答案 1 :(得分:1)
除了以上实际将平面锚点保存为对象以便稍后重新加载的方法之外,第二次打开应用程序时,更加强大的方法可以在正确的位置重新加载平面,这是使用某种可以放置的精确定位系统你的飞机坚持在正确的位置。