17

我正在尝试显示一个金字塔,它指向 z 轴,然后也围绕 z 旋转。由于我的相机位于 z 轴上,我希望从上面看到金字塔。我设法旋转金字塔以这种方式看到它,但是当我添加动画时,它似乎在多个轴上旋转。

这是我的代码:

// The following create the pyramid and place it how I want
let pyramid = SCNPyramid(width: 1.0, height: 1.0, length: 1.0)
let pyramidNode = SCNNode(geometry: pyramid)
pyramidNode.position = SCNVector3(x: 0, y: 0, z: 0)
pyramidNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: Float(M_PI / 2))
scene.rootNode.addChildNode(pyramidNode)

// But the animation seems to rotate aroun 2 axis and not just z
var spin = CABasicAnimation(keyPath: "rotation")
spin.byValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: 2*Float(M_PI)))
spin.duration = 3
spin.repeatCount = HUGE
pyramidNode.addAnimation(spin, forKey: "spin around")
4

3 回答 3

16

尝试手动设置和动画相同的属性可能会导致问题。使用byValue动画会使问题变得更糟——它会连接到当前的变换,因此更难跟踪当前的变换是否是动画所期望的开始。

相反,将金字塔的固定方向(它的顶点在 -z 方向)与动画(它围绕它指向的轴旋转)分开。有两种好方法可以做到这一点:

  1. 使pyramidNode另一个节点的子节点获得一次性旋转(围绕 x 轴的 π/2),并将旋转动画直接应用于pyramidNode. (在这种情况下,金字塔的顶点仍将指向其局部空间的 +y 方向,因此您需要围绕该轴而不是 z 轴旋转。)

  2. 使用该pivot属性来转换 's 内容的局部空间,并相对于其包含空间进行pyramidNode动画处理。pyramidNode

下面是显示第二种方法的一些代码:

let pyramid = SCNPyramid(width: 1.0, height: 1.0, length: 1.0)
let pyramidNode = SCNNode(geometry: pyramid)
pyramidNode.position = SCNVector3(x: 0, y: 0, z: 0)
// Point the pyramid in the -z direction
pyramidNode.pivot = SCNMatrix4MakeRotation(CGFloat(M_PI_2), 1, 0, 0)
scene.rootNode.addChildNode(pyramidNode)

let spin = CABasicAnimation(keyPath: "rotation")
// Use from-to to explicitly make a full rotation around z
spin.fromValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: 0))
spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: CGFloat(2 * M_PI)))
spin.duration = 3
spin.repeatCount = .infinity
pyramidNode.addAnimation(spin, forKey: "spin around")

一些不相​​关的更改以提高代码质量:

  • CGFloat在需要显式转换来初始化SCNVector组件时使用;使用FloatDouble特别会在 32 或 64 位架构上中断。
  • 使用.infinity而不是旧的 BSD 数学常量HUGE。此类型推断出的类型spin.repeatCount是什么,并使用为所有浮点类型定义的常量值。
  • 使用M_PI_2π/2 对精度很迂腐。
  • 使用let而不是var动画,因为我们从不为spin.

关于CGFloat错误业务的更多信息:在 Swift 中,数字文字只有在它们所在的表达式需要类型之前才具有类型。这就是为什么你可以做这样的事情spin.duration = 3——即使duration是一个浮点值,Swift 允许你传递一个“整数文字”。但如果你这样做,let d = 3; spin.duration = d你会得到一个错误。为什么?因为变量/常量具有显式类型,而 Swift 不进行隐式类型转换。是无类型的3,但是当它被分配给 时d,类型推断默认为选择Int,因为您没有指定其他任何内容。

如果您看到类型转换错误,您的代码可能混合了文字、常量和/或函数返回的值。您可以通过将表达式中的所有内容转换为CGFloat(或您将该表达式传递给的任何类型)来消除错误。当然,这会让你的代码变得不可读和丑陋,所以一旦你让它工作,你可能会开始一次删除一个转换,直到找到能完成这项工作的那个。

于 2014-08-13T21:30:30.013 回答
15

SceneKit 包含动画助手,它比 CAAnimations 使用起来更简单、更短。这是 ObjC,但重点是:

[pyramidNode runAction:
    [SCNAction repeatActionForever:
        [SCNAction rotateByX:0 y:0 z:2*M_PI duration:3]]];
于 2014-12-27T17:03:13.330 回答
0

我将 byValue 更改为 toValue,这对我有用。所以换行...

spin.byValue = NSValue(SCNVector4: SCNVector4(...

将其更改为...

spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y:0, z:1, w: 2*float(M_PI))
于 2014-08-13T20:53:06.003 回答