ARKit 1.5 beta中检测识别图像生成的SCNNode平面触摸及作用域问题
解决ARKit中平面节点作用域导致的“未解析标识符”问题
嘿,这个问题我之前也碰到过!核心就是你说的作用域问题——你在renderer(_:didAdd:for:)里定义的planeNode是局部变量,出了这个函数就访问不到了,所以点击处理的代码里自然会报“未解析标识符”的错误。下面给你两个常用的解决方案,根据你的场景选就行:
方案1:把planeNode改成类的实例属性(单平面场景)
如果你的场景里只会识别一个图像、创建一个平面节点,那最简单的办法就是把planeNode从局部变量提升为你的视图控制器(或ARKit管理类)的实例属性:
在类顶部声明属性:
// 比如在你的ARSCNView所在的ViewController里 var planeNode: SCNNode?在Renderer方法中给属性赋值:
原来的代码里你是创建局部的planeNode,现在直接给实例属性赋值:func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // 确保这里是识别到图像锚点的逻辑(根据你的ARKit 1.5图像识别逻辑调整) guard let imageAnchor = anchor as? ARImageAnchor else { return } // 创建平面节点 let planeGeometry = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width, height: imageAnchor.referenceImage.physicalSize.height) // 给实例属性赋值,而不是定义局部变量 planeNode = SCNNode(geometry: planeGeometry) // 调整节点位置和朝向(根据你的需求) planeNode?.eulerAngles.x = -.pi / 2 node.addChildNode(planeNode!) }在点击处理中使用该属性:
现在点击代码里就能直接访问planeNode了,记得做安全的可选绑定:func handleTap(_ gestureRecognizer: UIGestureRecognizer) { guard let sceneView = gestureRecognizer.view as? ARSCNView else { return } let touchLocation = gestureRecognizer.location(in: sceneView) let hitResults = sceneView.hitTest(touchLocation, types: .existingPlaneUsingExtent) if let hitResult = hitResults.first { // 现在planeNode是实例属性,作用域覆盖整个类,不会报错了 if let targetPlane = planeNode, targetPlane == hitResult.node { print("平面被点击啦!") } } }
方案2:用数组存储所有平面节点(多平面场景)
如果你的场景可能识别多个图像、创建多个平面节点,那单个planeNode就不够用了,这时候用数组来管理所有平面节点:
声明数组属性:
var planeNodes: [SCNNode] = []在Renderer方法中添加节点到数组:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let imageAnchor = anchor as? ARImageAnchor else { return } let planeGeometry = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width, height: imageAnchor.referenceImage.physicalSize.height) let newPlaneNode = SCNNode(geometry: planeGeometry) newPlaneNode.eulerAngles.x = -.pi / 2 node.addChildNode(newPlaneNode) // 将新创建的平面节点加入数组 planeNodes.append(newPlaneNode) }在点击处理中判断是否是已创建的平面:
func handleTap(_ gestureRecognizer: UIGestureRecognizer) { guard let sceneView = gestureRecognizer.view as? ARSCNView else { return } let touchLocation = gestureRecognizer.location(in: sceneView) let hitResults = sceneView.hitTest(touchLocation, types: .existingPlaneUsingExtent) if let hitResult = hitResults.first { // 检查点击的节点是否在我们存储的平面节点数组里 if planeNodes.contains(hitResult.node) { print("某个平面被点击啦!") } } }
额外提醒
- 记得处理可选值的安全问题,比如用
if let或者guard let,避免强制解包导致崩溃; - 如果需要移除平面节点(比如图像丢失时),也要记得从数组里移除对应的节点,避免内存泄漏。
内容的提问来源于stack exchange,提问作者ToxicProxy
相关产品推荐
相关产品推荐

