【问题标题】:SceneKit – Rotate and animate a SCNNodeSceneKit – 旋转 SCNNode 并为其设置动画
【发布时间】:2014-10-07 08:53:59
【问题描述】:

我正在尝试显示一个金字塔,它指向 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")

【问题讨论】:

    标签: swift animation 3d scenekit


    【解决方案1】:

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

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

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

    2. 使用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)
    // 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")
    

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

    • 当需要显式转换来初始化SCNVector 组件时,使用CGFloat;专门使用 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(或您将该表达式传递给的任何类型)来消除错误。当然,这会使您的代码不可读且难看,因此一旦您开始使用它,您可能会开始一次删除一个转换,直到找到可以完成这项工作的那个。

    【讨论】:

    • 感谢 rickster,这正是我想要的。关于使用 CGFloat 的另一件事。我开始使用它们,但是 xcode 通过编译错误“'Int' 不能转换为 'Float'”,因此我将它们更改为 Float。任何想法为什么?
    • 哇!使用我编辑的答案做得很好。检查时间戳!
    • 有关CGFloat 转换问题的更多信息,请参见编辑后的答案。 @FlippinFun:我没有看到您编辑的答案。 :) 我花了 8 多分钟来确保我的解决了“与旋转轴不同的方向”问题,否则清理和测试代码。
    • @rickster。没关系。你的答案更好?
    • 感谢 rickster,但我认为 CGFloat 的问题与 SCNMatrix4MakeRotation 将浮点数而不是 CGFloats 作为参数有关。我不明白为什么,因为我已经看到很多使用 CGFloat 实现此功能的示例。 developer.apple.com/library/prerelease/iOS/documentation/…
    【解决方案2】:

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

    [pyramidNode runAction:
        [SCNAction repeatActionForever:
            [SCNAction rotateByX:0 y:0 z:2*M_PI duration:3]]];
    

    【讨论】:

    • 动作本身并不是动画的快捷方式——它们在 SceneKit 渲染架构的不同部分生效。有时这种差异很重要,因此您不能始终交替使用动作和动画。但是,如果它对您的应用程序没有影响,请继续。 :)
    • @rickster 有什么地方可以了解更多关于差异的信息吗?我现在不确定何时应该使用其中一个。
    • SceneKit talk from WWDC 2014 Tallis 是一个好的开始。否则,评论的主题太大了,但它会成为一个很好的问题。
    【解决方案3】:

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

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

    改成...

    spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y:0, z:1, w: 2*float(M_PI))
    

    【讨论】:

    • 该行是对 NSValue 初始化程序 init(SCNVector4 v: SCNVector4) 的调用。您的更改无法编译。
    • Swift 要求所有初始化器参数都使用标签,除非初始化器的声明将 _ 指定为标签。 NSValue 没有声明 init(_ v: SCNVector4)
    • 你是对的骗子。我让它与 toValue 一起工作。我改变了答案。
    猜你喜欢
    • 2017-08-31
    • 2021-12-21
    • 1970-01-01
    • 2015-06-21
    • 2019-07-11
    • 2018-06-07
    • 1970-01-01
    • 2016-04-29
    • 2018-03-22
    相关资源
    最近更新 更多