尝试手动设置和动画相同属性可能会导致问题。使用byValue
动画会使问题变得更加复杂--这会连接到当前的变换,因此更难跟踪当前的变换是否是动画期望的起始状态。
相反,将金字塔的固定方向(其顶点朝向-z方向)与动画分离开来(它围绕指向轴旋转)。有两种好的方法可以做到这一点:
将pyramidNode
作为另一个节点的子级,该节点获得一次性旋转(π/2绕x轴),并直接对pyramidNode
应用旋转动画。(在这种情况下,金字塔的顶点仍将指向其局部空间的+y方向,因此您需要围绕该轴而不是z轴旋转。)
使用pivot
属性转换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)
pyramidNode.pivot = SCNMatrix4MakeRotation(CGFloat(M_PI_2), 1, 0, 0)
scene.rootNode.addChildNode(pyramidNode)
let spin = CABasicAnimation(keyPath: "rotation")
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")
一些无关的更改,以提高代码质量:
- 在需要明确转换以初始化
SCNVector
组件时,请使用 CGFloat
;特别地,如果使用 Float
或 Double
则会在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 (或传递该表达式的类型)来使错误消失。当然,这会使您的代码难以阅读和丑陋,因此,一旦使其正常工作,您可能会开始一次删除一个转换,直到找到完成工作的转换为止。
CGFloat
转换问题的更多信息。@FlippinFun:我没有看到你的编辑答案。 :) 我花了超过8分钟来确保我的代码解决了“方向与旋转轴不同”的问题,并进行了清理和测试。 - rickster