在iOS的SceneKit中使用平移手势旋转一个节点

5

我正在使用以下代码来使用平移手势旋转节点。我只想在y轴上旋转我的节点。

let translation = gestureRecognize.translation(in: gestureRecognize.view!)

let x = Float(translation.x)
let y = Float(-translation.y)
let anglePan = (sqrt(pow(x,2)+pow(y,2)))*(Float)(Double.pi)/180.0

var rotationVector = SCNVector4()
rotationVector.x = 0.0
rotationVector.y = x
rotationVector.z = 0.0
rotationVector.w = anglePan


node.rotation = rotationVector

if(gestureRecognize.state == UIGestureRecognizerState.ended) {
    let currentPivot = node.pivot
    let changePivot = SCNMatrix4Invert( node.transform)

    node.pivot = SCNMatrix4Mult(changePivot, currentPivot)
    node.transform = SCNMatrix4Identity

}

这适用于欧拉角设置为(x:0,y:0,z:0)的节点。但是我的节点具有欧拉角(x:-90,y:0,z:0)。对于我上面的欧拉值,代码旋转了错误的角度。如何使用我的/不同的欧拉值旋转节点?


当您设置欧拉角时,您希望旋转轴随节点一起旋转吗? - jlsiewert
我想是的。我对Scene Kit还很陌生,所以不确定旋转是否会影响欧拉角。但我需要的是将我的节点水平旋转(y轴),并将欧拉角x值设置为-90。 - prabhu
3个回答

5

我认为你可能在这里过于复杂化了需要做的事情。

在我的例子中,我创建了一个带有SCNBox几何体的SCNNode并设置了它的Euler Angles,就像你的例子一样:(x: -90, y: 0, z: 0)。

你需要做的第一件事是创建一个变量来存储绕Y轴旋转的rotationAngle

var currentAngleY: Float = 0.0

然后尝试使用此函数绕Y轴旋转节点(顺便说一下,这个函数运行良好):

 /// Rotates An Object On It's YAxis
 ///
 /// - Parameter gesture: UIPanGestureRecognizer
 @objc func rotateObject(_ gesture: UIPanGestureRecognizer) {

        guard let nodeToRotate = currentNode else { return }

        let translation = gesture.translation(in: gesture.view!)
        var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
        newAngleY += currentAngleY

        nodeToRotate.eulerAngles.y = newAngleY

        if(gesture.state == .ended) { currentAngleY = newAngleY }

        print(nodeToRotate.eulerAngles)
}

希望能有所帮助...


当我同时对X和Y使用相同的逻辑时,当物体沿着X旋转180度,例如,当物体沿着X旋转180度时,Y旋转方向会改变,即+Y导致-Y旋转。有什么解决方法吗? - Manganese

0

这里是如何做你想做的事情,这也恰好是唯一的方法:

BOOL shouldRotateCube;    
- (IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender {
        if (sender.state == UIGestureRecognizerStateBegan)
        {
            shouldRotateCube = TRUE;
        } else if (sender.state == UIGestureRecognizerStateChanged && shouldRotateCube
                   && fabs([sender translationInView:sender.view].y) > 10.0)
        {
            shouldRotateCube = FALSE;
            [SCNTransaction begin];
[SCNTransaction setCompletionBlock:^{
    [SCNTransaction begin];
    [SCNTransaction setAnimationDuration:0.15];
    SCNVector3 nodeAngles = [node eulerAngles];
        [node setEulerAngles:SCNVector3Make(nodeAngles.x + (([sender translationInView:sender.view].y > 0.0) ? degreesToRadians(90) : degreesToRadians(-90)), nodes[i].rotation.y, nodes[i].rotation.z)];
    }
    [SCNTransaction commit];
}];
[SCNTransaction commit];

        }
    }

这是它的工作原理:使用BOOL来防止在设备屏幕上上下滑动时产生多次平移手势调用,因为这个特定的代码每次旋转90度。当您开始一个新手势时,它将允许您再次运行与旋转相关的代码。 translationInView方法被使用两次:一次是确保您希望通过在任一方向上至少距离10后才旋转立方体,第二次是确定您是向上还是向下滑动(通过评估平移值是否为正或负来完成)。
由于我的立方体只在上下方向上被旋转,所以我要么将90度添加到(x)eulerAngles向量的值中,要么从中减去;我将其余的eulerAngles值放在它们各自的位置上。
所有东西都被放在一个SCNTransaction中以动画旋转,并控制旋转动画的持续时间。
我已经尝试了所有其他可用的旋转方法,包括核心动画等——只有这种方法允许您在不发生奇怪事情的情况下前后旋转立方体等。 我有一个使用此代码的应用程序,它运行良好。 如果您在将其集成到自己的应用程序中遇到问题,我随时可以将我的应用程序代码提供给您。

这个逻辑“在旋转立方体之前需要在任一方向上距离至少为10”是一个很棒的想法 ^__________* - BlackMirrorz
它澄清了您以特定方式进行手势的意图(在开始时,轻敲、滑动或平移手势之间几乎没有区别);它还明确地确定了平移的方向;例如,在打算向上平移之前启动手势时,很容易意外地向下平移2个单位。由于动画将立方体旋转到完全直角,我使用这个方法来防止它朝错误的方向移动。 - James Bush

0

旋转向量应该长成这样:

rotation = SCNVector4(x: 0, y: 1, z: 0, w: some_angle_in_radians)

x、y、z是一个归一化的单位向量 w以弧度为单位

检查你的数学计算

let anglePan = (sqrt(pow(x,2)+pow(y,2)))*(Float)(Double.pi)/180.0

可能需要检查x、y的值,应该是类似这样的:

let anglePan = (sqrt(pow(x*deg2rd,2)+pow(y*deg2rad,2)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接