1

目标是同时为多个 SCNNode 设置动画,然后在所有动画完成后调用完成块。并行动画具有相同的持续时间,因此如果一起开始,将同时完成。

这个 SO 答案建议使用groupSprite Kit 的功能,但 Scene Kit 中没有类似物,因为SCNScene该类缺少runAction.

一种选择是针对每个节点单独运行所有操作,并让每个操作调用相同的完成函数,该完成函数必须维护一个标志以确保它只被调用一次。

另一种选择是避免完成处理程序并在与动画持续时间匹配的延迟后调用完成代码。然而,这会在测试期间产生竞争条件,因为有时动画在完成之前就被暂停了。

不过,这似乎很笨拙。在 SceneKit 中对多个节点的动画进行分组然后调用完成处理程序的正确方法是什么?

4

3 回答 3

1

我最初处理这个问题的方法是,因为所有初始动画都具有相同的持续时间,所以将完成处理程序仅应用于其中一个动作。但是,有时,动画会挂起(SCNAction 完成处理程序等待手势执行)。

我目前成功的解决方案是不将完成处理程序与 an 结合使用,SCNAction但有延迟:

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

调用示例:

delay(0.95) {
     self.scaleNode_2.runAction(moveGlucoseBack)
     self.fixedNode_2.runAction(moveGlucoseBack)
     self.scaleNode_3.hidden = true
     self.fixedNode_3.hidden = true
}

我怀疑这可以称为“正确的方式”,但它适用于我的使用,并消除了我在尝试使用完成处理程序在多个节点上运行动画时遇到的随机挂起。

于 2016-08-23T17:25:46.273 回答
1

我还没有完全考虑到这一点,但我会发布它以希望有用。

一般问题,在一组动作中的最后一个完成后做某事,这就是 GCD 的意义dispatch_barrier所在。将所有块提交到一个私有并发队列,然后提交 Grand Finale 完成块dispatch_barrier。总决赛在之前的所有区块完成后运行。

我没有立即看到的是如何将这些 GCD 调用与 SceneKit 调用和完成处理程序集成在一起。

也许dispatch_group是更好的方法。

欢迎编辑和评论!

于 2016-08-24T08:35:39.780 回答
0

尝试这样的事情:

private class CountMonitor {
    var completed: Int = 0
    let total: Int
    let then: ()->Void

    init(for total: Int, then: @escaping(()->Void)) {
        self.total = total
        self.then = then
    }

    func didOne() {
        completed += 1
        if completed == total {
            then()  // Generally you should dispatch this off the main thread though
        }
    }
}

然后创建动作看起来像:

private func test() {
    // for context of types
    let nodes: [SCNNode] = []
    let complexActionsToRun: SCNAction = .fadeIn(duration: 100)

    // Set up the monitor so it knows how many 'didOne' calls it should get, and what to do when they are all done ...
    let monitor = CountMonitor(for: nodes.count) { () in
        // do whatever you want at the end here
        print("Done!")
    }
    for node in nodes {
        node.runAction( complexActionsToRun ) { () in
            monitor.didOne()
        }
    }
}

请注意,您还应该考虑节点数组为空(您可能仍想在最后做任何您想做的事情,在这种情况下立即执行)。

于 2019-08-13T05:07:24.963 回答