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

无法拖拽ARKit中的SCNNode?拖拽无动画问题求助

解决ARKit中SCNNode拖拽时瞬移/无平滑效果的问题

看起来你已经走对了第一步——能成功识别点击的节点,但拖拽逻辑里有两个关键问题导致了瞬移和无动画的情况,我来帮你修正:

核心问题分析

  1. 复用了旧的触摸位置:你的touchesMoved里直接把节点位置设为touchesBegan时记录的currentTapPos,而没有获取当前触摸点对应的最新AR世界位置,这就导致节点要么不动,要么只能跳到初始点击位置。
  2. 直接赋值position导致瞬移:没有给节点添加平滑过渡的逻辑,直接修改position属性会让节点瞬间移动,没有拖拽的流畅感。

修正后的完整解决方案

第一步:添加必要的状态变量

先定义几个变量来记录选中的节点、初始触摸偏移,方便后续拖拽计算:

var selectedNode: SCNNode? // 当前选中的可拖拽节点
var nodeOffset: SCNVector3 = SCNVector3Zero // 节点与触摸点的位置偏移(避免点击时瞬移)

第二步:完善touchesBegan逻辑

在触摸开始时,不仅要识别节点,还要记录触摸点与节点的相对偏移,这样拖拽时节点会跟着手指的相对位置移动,而不是直接跳到触摸点:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else { return }
    
    // 先检测点击的SceneKit节点
    let sceneHits = gameView.hitTest(touch.location(in: gameView), options: nil)
    if let tappedNode = sceneHits.first?.node {
        selectedNode = tappedNode
        
        // 获取当前触摸点对应的AR世界位置
        let arHits = gameView.hitTest(touch.location(in: gameView), types: .featurePoint)
        if let hitFeature = arHits.last {
            let tapARPos = getARPos(hitFeature: hitFeature)
            // 计算节点与触摸点的偏移量
            nodeOffset = SCNVector3(
                tappedNode.position.x - tapARPos.x,
                tappedNode.position.y - tapARPos.y,
                tappedNode.position.z - tapARPos.z
            )
        }
    } else {
        selectedNode = nil
    }
}

第三步:修复touchesMoved的拖拽逻辑

在触摸移动时,实时获取最新的AR位置,应用偏移量后,要么直接更新位置(即时响应),要么用SceneKit动画实现平滑移动:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first, let targetNode = selectedNode else { return }
    
    // 获取当前触摸点对应的最新AR世界位置
    let arHits = gameView.hitTest(touch.location(in: gameView), types: .featurePoint)
    guard let hitFeature = arHits.last else { return }
    
    let currentARPos = getARPos(hitFeature: hitFeature)
    // 应用偏移量,计算节点的目标位置
    let targetPos = SCNVector3(
        currentARPos.x + nodeOffset.x,
        currentARPos.y + nodeOffset.y,
        currentARPos.z + nodeOffset.z
    )
    
    // 方案1:即时更新位置(响应快,适合需要精准拖拽的场景)
    targetNode.position = targetPos
    
    // 方案2:平滑动画过渡(更流畅,适合视觉优先的场景)
    // 注意:如果用动画,建议先停止节点之前的动画,避免冲突
    targetNode.removeAllActions()
    let moveAction = SCNAction.move(to: targetPos, duration: 0.1)
    moveAction.timingMode = .easeInEaseOut // 缓动效果更自然
    targetNode.runAction(moveAction)
}

第四步:补充touchesEnded清理逻辑

触摸结束时清空选中的节点,避免后续误操作:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    selectedNode = nil
}

额外优化建议

  • 用平面检测替代特征点:如果你的场景是在平面上拖拽物体,建议把hitTest的类型改成.existingPlaneUsingExtent,比featurePoint更稳定,不会出现位置飘移的情况。
  • 限制Y轴位置:如果不需要物体上下移动,可以固定targetPos.y为节点原来的Y值,避免拖拽时物体上下浮动。

内容的提问来源于stack exchange,提问作者blue

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:45:40