You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SceneKit:使用平移手势移动非原点位置的SCNNode

解决SCNNode首次平移手势跳至(0,0,0)的问题

我明白你遇到的问题了——你的节点初始位置是SCNVector3(0,0,-1),但第一次平移手势触发时,它会先跳到Z轴0的位置,再跟着手势移动。这是因为你的currentPositionDepth初始化和手势处理逻辑有两个关键问题:

  1. 初始值不匹配节点实际位置:你初始化currentPositionDepth为CGPoint()(也就是(0,0)),但节点的初始Z轴对应你代码里的newPos.y是-1,第一次手势计算时直接覆盖了节点的初始Z值。
  2. 手势状态处理不完整:你没有在手势开始时(.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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:20:34