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

ARKit与SceneKit项目中3D模型旋转手势实现问题求助

实现ARKit+SceneKit虚拟对象的旋转手势

Hey there! 恭喜你已经搞定了点击放置、捏合缩放和平移这些手势功能,旋转手势其实很容易就能加上,咱们一步步来实现:


第一步:添加旋转手势识别器

首先,你需要创建一个UIRotationGestureRecognizer实例,并把它添加到你的ARView上。假设你的ARView引用叫arView,可以在viewDidLoad或者视图初始化的地方添加这段代码:

// 初始化旋转手势识别器,绑定处理方法
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
// 将手势添加到AR视图
arView.addGestureRecognizer(rotationGesture)

第二步:实现手势处理逻辑

接下来要写手势的处理方法,这里需要注意两个关键点:

  1. 要跟踪当前需要旋转的虚拟对象(比如你点击放置的那个SCNNode)
  2. 根据手势的状态(开始/变化/结束)来更新节点的旋转角度

首先,在你的ViewController里定义一个变量来保存当前选中的节点:

// 保存当前要操作的虚拟对象节点
var currentTargetNode: SCNNode?

然后实现手势处理的方法:

@objc private func handleRotation(_ gesture: UIRotationGestureRecognizer) {
    // 确保有可旋转的节点
    guard let targetNode = currentTargetNode else { return }
    
    switch gesture.state {
    case .began:
        // 手势开始时,将手势的初始旋转值设为节点当前的y轴旋转角度
        // 这样旋转会基于节点当前的姿态进行,不会跳变
        gesture.rotation = targetNode.eulerAngles.y
    case .changed:
        // 计算当前手势的旋转角度,直接赋值给节点的y轴欧拉角
        // 绕y轴旋转符合日常“拧转”物体的直觉,你也可以改成x/z轴实现不同方向的旋转
        targetNode.eulerAngles.y = gesture.rotation
        
        // 如果想要更平滑的旋转动画,可以加上这段SCNTransaction代码
        /*
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.08
        targetNode.eulerAngles.y = gesture.rotation
        SCNTransaction.commit()
        */
    default:
        break
    }
}

第三步:关联点击放置的节点

因为你是通过点击屏幕放置虚拟对象,所以需要在点击手势的处理方法里,把刚放置的节点赋值给currentTargetNode,这样旋转手势才知道要操作哪个对象:

// 假设这是你之前实现的点击放置方法
@objc private func handleTap(_ gesture: UITapGestureRecognizer) {
    let touchLocation = gesture.location(in: arView)
    // 省略你之前的放置逻辑...
    if let placedNode = yourPlacedNode { // 替换成你实际创建的节点实例
        arView.scene.rootNode.addChildNode(placedNode)
        // 赋值给当前目标节点,让旋转手势能识别到它
        currentTargetNode = placedNode
    }
}

如果需要支持点击切换不同节点进行旋转,可以在旋转手势的.began状态里,通过hitTest检测当前点击的节点:

case .began:
    let touchLocation = gesture.location(in: arView)
    // 检测点击位置对应的虚拟节点
    let hitResults = arView.hitTest(touchLocation, options: [.boundingBoxOnly: true])
    if let tappedNode = hitResults.first?.node {
        currentTargetNode = tappedNode
        gesture.rotation = tappedNode.eulerAngles.y
    }

额外注意点:调整节点的旋转中心点

如果你的模型旋转时不是绕自身中心转,那是因为节点的pivot(枢轴点)没设置对。可以在加载模型后调整枢轴到模型的边界框中心:

func setupNodePivot(for node: SCNNode) {
    let boundingBox = node.boundingBox
    // 计算边界框中心的坐标
    let centerX = (boundingBox.max.x - boundingBox.min.x) / 2 + boundingBox.min.x
    let centerY = (boundingBox.max.y - boundingBox.min.y) / 2 + boundingBox.min.y
    let centerZ = (boundingBox.max.z - boundingBox.min.z) / 2 + boundingBox.min.z
    // 设置枢轴点为中心
    node.pivot = SCNMatrix4MakeTranslation(centerX, centerY, centerZ)
}

调用这个方法:在你加载.dae模型并创建节点后,执行setupNodePivot(for: yourModelNode)即可。


这样就能完美实现虚拟对象的旋转手势啦,亲测在ARKit+SceneKit环境下运行稳定,和你已有的手势功能也不会冲突~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:17:44