ARKit开发AR健身应用如何检测机器人手部触碰ImageView黄点
ARKit场景下3D模型与2D UI元素碰撞检测解决方案
以下两种方案适配不同开发需求,可根据你的应用实际场景选择:
方案1:3D空间坐标映射法(精度最高,推荐AR健身场景使用)
核心逻辑是把2D的黄色圆点坐标映射到AR 3D世界空间,用原生物理碰撞体系做检测:
- 第一步:用ARSCNView的
unprojectPoint(_:ontoPlane:)方法,把UIImageView的center屏幕坐标,转换为你健身动作对应的平面(比如用户前方的交互平面)上的3D世界坐标 - 第二步:给转换后的3D点添加一个半透明/完全透明的球形碰撞节点,半径可根据黄色圆点的实际大小调整;同时给机器人手部节点添加运动类型的物理碰撞体,设置匹配的碰撞掩码
- 第三步:实现
SCNPhysicsContactDelegate的碰撞回调,直接监听碰撞触发事件即可
代码示例:
// 转换黄色圆点坐标到AR 3D世界空间 let dotScreenPos = yellowDotImageView.center let dot3DPos = sceneView.unprojectPoint(SCNVector3(Float(dotScreenPos.x), Float(dotScreenPos.y), 0.99), ontoPlane: interactivePlane) // 给圆点添加碰撞节点 let dotCollider = SCNNode(geometry: SCNSphere(radius: 0.05)) dotCollider.position = dot3DPos dotCollider.physicsBody = SCNPhysicsBody(type: .static, shape: nil) dotCollider.physicsBody?.categoryBitMask = 1 << 0 dotCollider.physicsBody?.contactTestBitMask = 1 << 1 dotCollider.geometry?.firstMaterial?.diffuse.contents = UIColor.clear sceneView.scene.rootNode.addChildNode(dotCollider) // 给机器人手部添加碰撞体 robotHandNode.physicsBody = SCNPhysicsBody(type: .kinematic, shape: nil) robotHandNode.physicsBody?.categoryBitMask = 1 << 1 robotHandNode.physicsBody?.contactTestBitMask = 1 << 0 // 碰撞触发回调 func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) { if (contact.nodeA.categoryBitMask | contact.nodeB.categoryBitMask) == (1<<0 | 1<<1) { // 执行得分、反馈等自定义逻辑 print("手部已触碰黄色得分点") } }
方案2:2D投影检测法(实现更简单,适合轻量交互场景)
核心逻辑是反过来把机器人手部的3D坐标投影到2D屏幕,直接判断坐标是否在黄色圆点范围内:
- 直接在SceneKit的每帧更新回调里,调用ARSCNView的
projectPoint(_:)方法把机器人手部的3D世界坐标转成2D屏幕坐标 - 判断转换后的2D坐标是否落在UIImageView的bounds范围内即可
代码示例:
// 每帧更新时检测 func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } // 过滤手部在摄像头背面的无效情况 let hand3DPos = self.robotHandNode.worldPosition let projectedPos = self.sceneView.projectPoint(hand3DPos) guard projectedPos.z < 1 else { return } // 转换坐标判断是否在圆点范围内 let handScreenPos = CGPoint(x: CGFloat(projectedPos.x), y: CGFloat(projectedPos.y)) if self.yellowDotImageView.bounds.contains(self.yellowDotImageView.convert(handScreenPos, from: self.view)) { // 执行得分、反馈等自定义逻辑 print("手部已触碰黄色得分点") } } }
注意如果你的黄色圆点是跟随AR空间位置移动的,优先选方案1;如果圆点是固定在屏幕上的UI元素,方案2的开发成本更低。
内容的提问来源于stack exchange,提问作者Kittisak B
相关产品推荐
相关产品推荐

