当前位置: 首页 > 知识库问答 >
问题:

SceneKit:如何在一起设置多个SCNNodes的动画,然后调用一次完成块

强承望
2023-03-14

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

因此,这个答案建议对Sprite Kit使用函数,但由于SCNScene类缺少运行操作,因此没有模拟场景中的Kit。

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

另一种选择是避免使用完成处理程序,并在与动画持续时间匹配的延迟后调用完成代码。然而,这会在测试期间创造比赛条件,因为有时动画在完成之前会被延迟。

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

共有3个答案

华知
2023-03-14

试试这样的东西:

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()
        }
    }
}

注意,您还应该考虑节点数组为空(在这种情况下,您可能仍然希望在最后执行您想执行的任何操作)。

卢德惠
2023-03-14

我还没有完全考虑清楚,但我会把它贴出来,希望有用。

一般的问题是,在最后一组操作完成后做些什么,这是GCD的调度屏障的内容。将所有块提交到一个私有并发队列,然后使用dispatch\u barrier提交大结局完成块。大结局在之前的所有街区结束后进行。

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

也许调度组是一个更好的方法

欢迎编辑和评论!

巢安澜
2023-03-14

我最初的做法是,因为所有初始动画的持续时间都相同,所以只对其中一个动作应用完成处理程序。但有时,动画会挂断(动作完成处理程序等待手势执行)。

我目前成功的解决方案是,不将完成处理程序与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
}

我怀疑这是否是“正确的方式”,但它对我的使用效果很好,并且消除了我在尝试使用完成处理程序在多个节点上运行动画时遇到的随机障碍。

 类似资料:
  • 我对SpriteKit非常陌生。我有一组节点,需要一起移动到每个节点的不同点,在所有节点的动画完成后,我想做一些其他的事情。 我以前是用组件制作的。一个块提供了我需要的东西。但在SpriteKit中,每个节点都有自己的动作,并自行设置动画。我找不到任何精灵节点的块动画。所以我无法控制动画的完成。 希望,我很清楚。提前谢谢。

  • 由于网络问题,我可能会调用UIView动画函数两次。不知何故,这似乎会搞乱您在完成块中获得的完成布尔值。 在动画期间,我将UIView移出屏幕。在动画结束时,它应该将我正在制作动画的帧设置为隐藏。我注意到完成块中完成的BOOL总是设置为YES。因此,当我第二次调用动画时,在对动画的第一次调用完成之前,它将设置为BOOL完成,即使我希望它设置为NO,因为动画被对同一动画的新调用中断。 我也尝试添加以

  • UPATE启动 正确答案如下: 是否可以不使用完成块在UIView上执行多个动画 没必要读这个。 上端 我有类似的问题,UIView animateWithDuration会立即返回,但我不能使用完成块,因为我的动画有不同的功能。 但确实想要动画相同的对象。 我在做纸牌游戏,所以我在屏幕上移动纸牌,但在移动之前,我也有一些游戏逻辑。这就是为什么我可以方便地单独制作动画。 如何解决这个问题? @Fo

  • 我有一个简单的动画文件: 基本上,这会将一个组件沿X轴向左滑动50 dp。我已经成功地将它附加到一个组件上,它工作得非常完美,但是当我尝试同时将它附加到多个组件上时,动画只适用于最后一个组件。 例如:我有5张牌。AI敌人从手中随机选择一张牌。但是我想动画敌人“捡”卡。这就是动画发挥作用的地方。 比如说: 这里的目标是循环阵列中的每张卡,并将其滑出X轴。但动画只出现在最后一张牌上(阵列中的第五张牌)

  • 我有一个用于交互式过渡的自定义动画师。还有一个,根据过渡进度设置为。效果的动画代码如下: 我通过调用它,当从它到第一个的转换开始时,它在第二个上调用。 然而,我这里有一个问题。在动画结束之前调用完成块。当我第一次运行转换(没有取消它)时,它工作得很好,但在随后的运行过程中,它就不工作了。 我也曾尝试将动画添加到我的动画师中,但也没有成功。 此外,当我取消转换时,在实际动画结束之前调用完成块(在这种

  • 我正在为数组中的每个UIView设置动画,我想知道所有动画何时完成。例如:我用一个简单的缩放动画在ViewController上显示10个UIView,每个UIView都有一个小延迟。在所有10个动画完成后,我想在完成块中再做一个动画。 我如何知道所有动画何时完成?