SceneKit:使用平移手势移动非原点位置的SCNNode
解决SCNNode首次平移手势跳至(0,0,0)的问题
我明白你遇到的问题了——你的节点初始位置是SCNVector3(0,0,-1),但第一次平移手势触发时,它会先跳到Z轴0的位置,再跟着手势移动。这是因为你的currentPositionDepth初始化和手势处理逻辑有两个关键问题:
- 初始值不匹配节点实际位置:你初始化
currentPositionDepth为CGPoint()(也就是(0,0)),但节点的初始Z轴对应你代码里的newPos.y是-1,第一次手势计算时直接覆盖了节点的初始Z值。 - 手势状态处理不完整:你没有在手势开始时(
.began状态)同步节点的当前位置到currentPositionDepth,导致第一次计算时用了错误的基准值。
下面是修改后的完整代码,能让节点从初始位置(0,0,-1)开始平滑移动:
// 初始化时直接绑定节点的初始位置 var currentPositionDepth = CGPoint(x: 0, y: -1) @objc func handlePan(sender: UIPanGestureRecognizer) { switch sender.state { case .began: // 手势开始时,同步节点当前的位置到基准值(如果后续节点可能被其他逻辑修改,这一步很重要) currentPositionDepth = CGPoint( x: CGFloat(node.position.x), y: CGFloat(node.position.z) ) case .changed: let translation = sender.translation(in: sceneView) // 基于基准位置 + 手势平移量计算新位置 let newPosX = currentPositionDepth.x + translation.x let newPosZ = currentPositionDepth.y + translation.y node.position.x = Float(newPosX) node.position.z = Float(newPosZ) case .ended, .cancelled: // 手势结束时,更新基准值为节点当前位置,为下一次手势做准备 currentPositionDepth = CGPoint( x: CGFloat(node.position.x), y: CGFloat(node.position.z) ) default: break } }
关键修改点解释:
- 初始化
currentPositionDepth:直接设置为(0, -1),和节点初始位置的X、Z轴对应上(你的代码里用newPos.y控制Z轴)。 - 增加
.began状态处理:如果后续节点的位置可能被其他代码修改,手势开始时同步当前位置到基准值,能保证每次手势都从节点的实际当前位置开始。 - 拆分状态逻辑:把不同手势状态的处理分开,逻辑更清晰,避免了初始值不匹配导致的跳变问题。
这样修改后,第一次平移手势就会从节点的初始位置(0,0,-1)开始,平滑地跟随手势移动,不会再出现跳至(0,0,0)的情况了。
内容的提问来源于stack exchange,提问作者Gene M.
相关产品推荐
相关产品推荐

