SceneKit中检测AR追踪生成的3D汽车模型触摸事件
解决ARKit中仅点击3D汽车模型时播放音效的问题
核心思路
通过SceneKit的hitTest方法精准检测触摸点对应的3D节点,判断是否为目标汽车模型后再触发音效播放,替代原有的全局屏幕触摸检测逻辑。
步骤1:给汽车节点添加唯一标识
在渲染汽车模型的renderer(_:nodeFor:)方法中,给每个汽车节点设置唯一name属性,方便后续识别:
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? { let node = SCNNode() if let imageAnchor = anchor as? ARImageAnchor { let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width, height: imageAnchor.referenceImage.physicalSize.height) plane.firstMaterial?.diffuse.contents = UIColor(white: 1.0, alpha: 0.75) let planeNode = SCNNode(geometry: plane) planeNode.name = "trackingPlane" // 给追踪平面命名,后续可排除点击 planeNode.eulerAngles.x = -.pi/2 node.addChildNode(planeNode) if imageAnchor.referenceImage.name! == "mercedes2019coupe" { let mercedesScene = SCNScene(named: "art.scnassets/mercedesAR.scn")! let mercedesNode = mercedesScene.rootNode.childNodes.first! mercedesNode.name = "mercedesCar" // 设置奔驰节点唯一标识 mercedesNode.eulerAngles.x = .pi/2 mercedesNode.eulerAngles.z = -.pi/2 let yShift = (mercedesNode.boundingBox.max.y - mercedesNode.boundingBox.min.y) / 2 mercedesNode.position.y += yShift * 0.05 planeNode.addChildNode(mercedesNode) } if imageAnchor.referenceImage.name! == "audir8" { let audiScene = SCNScene(named: "art.scnassets/audi.scn")! let audiNode = audiScene.rootNode.childNodes.first! audiNode.name = "audiCar" // 设置奥迪节点唯一标识 audiNode.eulerAngles.x = .pi/2 audiNode.eulerAngles.z = -.pi/2 let yShift = (audiNode.boundingBox.max.y - audiNode.boundingBox.min.y) / 2 audiNode.position.y += yShift * 0.1 planeNode.addChildNode(audiNode) } } return node }
步骤2:修改触摸检测逻辑,精准识别汽车节点
替换原有的touchesBegan方法,使用hitTest检测触摸点对应的3D节点:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let touchLocation = touch.location(in: sceneView) // 执行SceneKit节点命中检测,优先检测节点的 bounding box let hitResults = sceneView.hitTest(touchLocation, options: [ SCNHitTestOption.searchMode: SCNHitTestSearchMode.all.rawValue, SCNHitTestOption.boundingBoxOnly: true, SCNHitTestOption.ignoreHiddenNodes: true // 忽略隐藏节点 ]) // 遍历检测结果,匹配目标汽车节点 for result in hitResults { guard let nodeName = result.node.name else { continue } switch nodeName { case "mercedesCar": playMercedesSound() // 播放奔驰专属音效 return case "audiCar": playAudiSound() // 播放奥迪专属音效 return default: continue // 忽略其他节点(如追踪平面) } } }
额外注意事项
- 确保
sceneView的isUserInteractionEnabled属性为true(默认开启,若手动修改过需恢复) - 若不需要显示追踪平面,可设置
planeNode.isHidden = true,避免误触 - 如果汽车模型面结构复杂,可将
SCNHitTestOption.boundingBoxOnly改为false,但会略微降低检测性能
内容的提问来源于stack exchange,提问作者Arcee
相关产品推荐
相关产品推荐

