无法拖拽ARKit中的SCNNode?拖拽无动画问题求助
解决ARKit中SCNNode拖拽时瞬移/无平滑效果的问题
看起来你已经走对了第一步——能成功识别点击的节点,但拖拽逻辑里有两个关键问题导致了瞬移和无动画的情况,我来帮你修正:
核心问题分析
- 复用了旧的触摸位置:你的
touchesMoved里直接把节点位置设为touchesBegan时记录的currentTapPos,而没有获取当前触摸点对应的最新AR世界位置,这就导致节点要么不动,要么只能跳到初始点击位置。 - 直接赋值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
相关产品推荐
相关产品推荐

