节点绕圈时方向异常,需保持朝向相机并获取角度值
解决节点朝向相机时y轴旋转跳变且获取角度值的问题
我之前也碰到过类似的坑——直接操作orientation(四元数)的分量来同步相机旋转,很容易因为四元数的表示特性出现跳变问题,毕竟四元数的y属性可不是你以为的欧拉角y轴旋转值😅。而且SCNBillboardConstraint虽然省心,但确实没法直接拿到我们需要的角度数据,那咱们换个思路来解决:
核心思路:通过向量计算获取准确的y轴旋转角度
我们可以直接计算相机相对于节点的水平方向,然后算出节点需要绕y轴旋转多少角度才能正面朝向这个方向,这样既不会出现旋转跳变,还能直接拿到角度值。
具体实现代码
首先,我们需要给SCNVector3加几个实用的扩展方法,用来计算向量的长度、夹角和叉乘:
extension SCNVector3 { // 计算向量长度 func length() -> Float { return sqrt(x*x + y*y + z*z) } // 归一化向量 func normalized() -> SCNVector3 { let len = length() return len > 0 ? SCNVector3(x/len, y/len, z/len) : self } // 计算两个向量的夹角(弧度) func angle(with vector: SCNVector3) -> Float { let dotProduct = x*vector.x + y*vector.y + z*vector.z let productOfLengths = length() * vector.length() guard productOfLengths > 0 else { return 0 } // 防止acos参数超出[-1,1]范围,避免崩溃 let cosTheta = max(min(dotProduct / productOfLengths, 1), -1) return acos(cosTheta) } // 计算两个向量的叉乘 func cross(with vector: SCNVector3) -> SCNVector3 { return SCNVector3( y*vector.z - z*vector.y, z*vector.x - x*vector.z, x*vector.y - y*vector.x ) } }
然后修改你的renderer(_:updateAtTime:)方法,用向量计算来实现旋转:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let cameraNode = sceneView.pointOfView else { return } // 计算相机相对于节点的位置向量 let cameraRelativePos = cameraNode.worldPosition - node.worldPosition // 投影到XOZ平面,只保留水平方向的朝向(忽略y轴高度差) let horizontalDir = SCNVector3(cameraRelativePos.x, 0, cameraRelativePos.z) // 避免相机在节点正上方/正下方时的计算异常 guard horizontalDir.length() > 0.001 else { return } // 节点的初始正面方向(默认是-z轴,也就是SCNNode默认的"向前"方向,根据你的需求调整) let forwardDir = SCNVector3(0, 0, -1) // 计算水平方向与初始正面的夹角 let angle = horizontalDir.angle(with: forwardDir) // 通过叉乘判断旋转方向(顺时针还是逆时针) let cross = horizontalDir.cross(with: forwardDir) let finalRotationAngle = cross.y > 0 ? angle : -angle // 给节点设置y轴旋转角度,现在你可以直接用node.eulerAngles.y获取需要的角度值了 node.eulerAngles.y = finalRotationAngle }
为什么这个方案能解决问题?
- 避免四元数跳变:我们直接计算的是欧拉角的y轴旋转值,相机绕节点一圈时,角度会平滑地从0过渡到2π(或者-π到π),不会出现突然跳变的情况。
- 可直接获取角度值:
node.eulerAngles.y就是我们需要的绕y轴旋转的弧度值,你可以随时读取它来做其他逻辑。 - 灵活调整:如果你的节点初始正面不是-z轴,只需要修改
forwardDir的值就行;如果需要让节点同时上下朝向相机,也可以扩展这个逻辑计算x轴的旋转角度。
内容的提问来源于stack exchange,提问作者AntiVIRUZ
相关产品推荐
相关产品推荐

