如何用UITapGestureRecognizer在SCNView中移动SCNNode?位置异常求助
解决SCNNode点击移动位置不准确的问题
问题分析
你当前代码的核心错误在于:
- 使用
result.simdModelTransform获取的是被点击节点的局部模型变换,这个坐标是相对于该节点父节点的局部位置,不是世界空间的点击位置。如果点击的是场景中的其他物体,拿到的是那个物体的局部坐标,自然会导致你的节点移动到错误位置。
解决方案
根据你的需求,分两种场景给出修复代码:
场景1:将节点移动到点击物体的世界位置(保留原Y高度)
直接使用SCNHitTestResult的worldPosition属性,它返回的是点击点在世界空间的坐标:
@objc func handleTapGesture(_ gesture: UITapGestureRecognizer) { let location = gesture.location(in: scnView) guard let result = self.scnView?.hitTest(location, options: nil).first else { return } // 获取点击点的世界空间坐标 let worldPos = result.worldPosition // 保留节点原Y高度,替换X、Z坐标 let newPosition = SCNVector3(worldPos.x, node.position.y, worldPos.z) node.position = newPosition }
场景2:将节点移动到指定平面(比如Y=0的地面)的点击位置
如果需要点击空白区域也能让节点移动到地面对应位置,可通过计算射线与平面的交点实现:
@objc func handleTapGesture(_ gesture: UITapGestureRecognizer) { let location = gesture.location(in: scnView) // 将屏幕坐标转换为近、远平面的3D点,构建射线 let nearPoint = scnView.unprojectPoint(SCNVector3(location.x, location.y, 0)) let farPoint = scnView.unprojectPoint(SCNVector3(location.x, location.y, 1)) // 计算射线与Y=0平面的交点(可根据你的地面高度修改Y值) let t = -nearPoint.y / (farPoint.y - nearPoint.y) let targetX = nearPoint.x + t * (farPoint.x - nearPoint.x) let targetZ = nearPoint.z + t * (farPoint.z - nearPoint.z) // 设置节点位置,Y可保留原高度或设为平面高度 node.position = SCNVector3(targetX, node.position.y, targetZ) }
额外优化:限制点击检测范围
如果只想让点击地面时触发移动,可给地面节点设置categoryBitMask,并在hitTest中指定检测该类别:
// 先给地面节点设置分类位 groundNode.categoryBitMask = 1 << 0; // 修改手势处理代码 @objc func handleTapGesture(_ gesture: UITapGestureRecognizer) { let location = gesture.location(in: scnView) // 只检测categoryBitMask为1<<0的节点 let hitOptions: [SCNHitTestOption: Any] = [ .categoryBitMask: 1 << 0, .searchMode: SCNHitTestSearchMode.all.rawValue ] guard let result = self.scnView?.hitTest(location, options: hitOptions).first else { return } let worldPos = result.worldPosition node.position = SCNVector3(worldPos.x, node.position.y, worldPos.z) }
内容的提问来源于stack exchange,提问作者helloworld12345
相关产品推荐
相关产品推荐

