目标是同时为多个SCNNode制作动画,然后在所有动画完成后调用完成块。并行动画具有相同的持续时间,因此如果一起开始将同时完成。
This SO answer建议使用Sprite Kit的group
函数,但Scene Kit中没有模拟,因为SCNScene
类缺少runAction
。
一个选项是针对每个节点分别运行所有操作,并让每个操作调用相同的完成函数,该函数必须保留一个标志以确保它只被调用一次。
另一种选择是避免完成处理程序并在与动画持续时间匹配的延迟后调用完成代码。然而,这会在测试期间产生竞争条件,因为有时动画会在完成之前被阻止。
但是,这似乎很笨重。什么是在SceneKit中对多个节点的动画进行分组然后调用完成处理程序的正确方法?答案 0 :(得分:1)
我最初接近这个的方式是,因为所有初始动画都具有相同的持续时间,所以将完成处理程序应用于其中一个动作。但是,有时,动画会挂起(SCNAction completion handler awaits gesture to execute)。
我目前的成功解决方案是不将完成处理程序与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
}
我怀疑这可以被称为“正确的方式”,但它适用于我的用途并消除了我在尝试使用完成处理程序在多个节点上运行动画时的随机挂起。
答案 1 :(得分:1)
我还没有完全想到这一点,但我会发布它以期有用。
一般问题是,在最后一组操作完成后执行某些操作,是GCD' dispatch_barrier
的用途。将所有块提交到专用并发队列,然后使用dispatch_barrier
提交Grand Finale完成块。所有以前的积木完成后,大结局运行。
我不能立即看到的是如何将这些GCD调用与SceneKit调用和完成处理程序集成。
也许dispatch_group
是一种更好的方法。
欢迎编辑和评论!
答案 2 :(得分: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()
}
}
}
请注意,您还应考虑到节点数组为空(您可能仍想在最后做任何想做的事情,在这种情况下立即这样做)。