使用UIRotationGestureRecognizer结合orientation替代eulerAngles旋转SCNNode的问题
我之前刚好踩过这个坑!用欧拉角处理多轴旋转确实容易碰到万向锁导致的诡异旋转,换成四元数(也就是SCNNode的orientation属性)就能完美解决,而且结合UIRotationGestureRecognizer的实现其实没那么复杂,我给你分享下实际项目里验证过的方案:
核心思路
四元数天生没有万向锁问题,能在任意轴上平滑叠加旋转。我们要做的就是在手势开始时记录节点的初始四元数姿态,然后在手势变化时,根据当前选择的旋转轴生成增量旋转四元数,再和初始姿态相乘得到新的姿态——这样每次旋转都是基于节点自身的局部坐标系,不会出现“随机旋转”的问题。
具体实现步骤
1. 定义需要的状态变量
先在类里存几个关键变量,用来记录手势和节点的初始状态:
// 记录手势开始时节点的初始四元数姿态 private var initialNodeOrientation: SCNQuaternion! // 记录手势开始时的初始旋转值(用于计算增量) private var initialGestureRotation: CGFloat = 0.0 // 当前选中的旋转轴(默认Y轴,切换时改这个值就行) private var currentRotationAxis: SCNVector3 = SCNVector3(0, 1, 0)
2. 处理旋转手势
在手势处理方法里,按状态分逻辑处理:
@objc func handleRotationGesture(_ gesture: UIRotationGestureRecognizer) { guard let targetNode = yourTargetSCNNode else { return } switch gesture.state { case .began: // 手势开始时,保存节点当前的四元数和手势初始旋转值 initialNodeOrientation = targetNode.orientation initialGestureRotation = gesture.rotation case .changed: // 计算当前手势的旋转增量(相对于手势开始时的变化量) let rotationDelta = gesture.rotation - initialGestureRotation // 根据当前轴生成增量旋转四元数 // SCNQuaternionMakeAxisAngle的参数:轴向量、旋转弧度(gesture.rotation本身就是弧度) let rotationQuaternion = SCNQuaternionMakeAxisAngle(currentRotationAxis, Float(rotationDelta)) // 叠加旋转:初始姿态 × 增量旋转,得到新的节点姿态 // 这里的乘法顺序很重要,必须是初始四元数在前,增量在后,才能保证绕自身轴旋转 targetNode.orientation = SCNQuaternionMult(initialNodeOrientation, rotationQuaternion) case .ended, .cancelled: // 手势结束后清空初始状态,避免干扰下一次手势 initialNodeOrientation = nil default: break } }
3. 切换旋转轴
要切换Y/Z轴的话,只需要修改currentRotationAxis的值就行,比如:
// 切换到Z轴 currentRotationAxis = SCNVector3(0, 0, 1) // 切回Y轴 currentRotationAxis = SCNVector3(0, 1, 0)
关键细节说明
- 四元数的乘法顺序:一定要用
初始姿态四元数 × 增量旋转四元数,这样才能保证旋转是基于节点自身的局部坐标系(自身轴)进行的,如果搞反顺序,旋转就会变成基于世界坐标系,依然会出现奇怪的偏移。 - 关于orientation的w分量:完全不需要手动去调整它!
SCNQuaternionMakeAxisAngle会自动计算出正确的w/x/y/z分量——w分量本质是旋转角度的余弦值,x/y/z是旋转轴的方向向量,系统提供的构造和乘法函数已经帮我们处理好了,不用手动干预。 - 手势增量的计算:UIRotationGestureRecognizer的
rotation是累积值,所以必须在手势开始时记录初始值,用当前值减去初始值得到本次手势的实际旋转增量,否则每次手势的旋转会叠加之前的累积值,导致旋转速度失控。
亲测这个方案不管是单独转Y/Z轴,还是切换轴多次旋转,节点都会严格绕自身的对应轴旋转,完全不会出现万向锁导致的随机旋转问题。
内容的提问来源于stack exchange,提问作者matuslittva
相关产品推荐
相关产品推荐

