SceneKit 中的多个串行动画

Multiple serial animations in SceneKit

我希望能够 运行 多个动画,一个接一个,在 SceneKit 中。我已经实现了一个函数,它 运行 是一个像这样的动画:

fileprivate func animateMove(_ move: Move) {
    print("Animate move started " + move.identifier)

    // I am creating rotateNode
    let rotateNode = SCNNode()
    rotateNode.eulerAngles.x = CGFloat.pi
    scene.rootNode.addChildNode(rotateNode)

    // Then I am selecting nodes which I want to rotate
    nodesToRotate = ...

    // Then I am adding the nodes to rotate node
    _ = nodesToRotate.map { rotateNode.addChildNode([=10=]) }

    SCNTransaction.begin()
    SCNTransaction.animationDuration = move.animationDuration

    SCNTransaction.completionBlock = {
        rotateNode.enumerateChildNodes { node, _ in
            node.transform = node.worldTransform
            node.removeFromParentNode()
            scene.rootNode.addChildNode(node)
        }
        rotateNode.removeFromParentNode()
        print("Animate move finished " + move.identifier)
    }
    SCNTransaction.commit()
}

然后我尝试 运行 多个连续动画,如下所示:

    func animateMoves(_ moves: [Move]) {
        for (index, move) in moves.enumerated() {
            perform(#selector(animateMove(_:)), 
            with: move, 
            afterDelay: TimeInterval(Double(index) * move.duration)
        }
    }

一切都是动画,但动画不是 运行 连续的。动画的开始和结束时间不可预测。 来自调试器的示例日志:

Animate move started 1
Animate move started 2
Animate move finished 1
Animate move finished 2
Animate move started 3
Animate move finished 3

我意识到我的方法不是最好的,但只有这样我才能实现几乎可以工作的动画。

我知道有一个 SCNAction class 可用。也许我应该在一次交易中进行多项操作?如果是这样,有人可以向我解释 SCNTransactions 究竟是如何工作的以及为什么完成 SCNTransaction 的完成块会在不可预测的时间触发吗?

尝试使用SCNAction.sequence():

class func sequence([SCNAction])

创建一个按顺序运行一组动作的动作

let sequence = SCNAction.sequence([action1, action2, action3]) // will be executed one by one

let node = SCNNode()
node.runAction(sequence, completionHandler:nil)

根据@Oleh Zayats 的回答,我尝试使用 SCNAction.sequence(_:) 方法来实现我的案例,但问题是我需要在每个完成的子操作之后触发完成处理程序,以便能够从 rotationNode 中移除节点。

经过几个小时的努力,我最终找到了一个相当不错的解决方案,而且效果非常好。

即:

我制作了一个函数 rotateAction,它看起来像这样:

func rotateAction(with move: Move, from rotateNode: SCNNode) -> SCNAction {

    let preAction = SCNAction.run { (rotateNode) in
        // all the pre action setup like choosing nodes to rotate
    }

    let action = SCNAction.rotate(by: -move.angle, around: vector, duration: move.animationDuration)

    let postAction = SCNAction.run { (rotateNode) in
        // completion handler for each action 
    }

    return SCNAction.sequence([preAction, action, postAction])
}

然后我写了一个函数可以运行一个接一个地播放多个动画:

func animateRotateMoves(_ moves: [Move]) {
    let rotateNode = SCNNode()
    scene.rootNode.addChildNode(rotateNode)

    var actions: [SCNAction] = []
    for move in moves {
        let action = rotateAction(with: move, from: rotateNode)
        actions.append(action)
    }
    actions.append(SCNAction.removeFromParentNode())
    let sequence = SCNAction.sequence(actions)
    rotateNode.runAction(sequence)
}