【问题标题】:In SceneKit SCNAction hangs when called from completion handler of RunAction在 SceneKit 中,当从 RunAction 的完成处理程序调用时,SCNAction 挂起
【发布时间】:2019-10-04 23:42:51
【问题描述】:

从 RunAction 的完成处理程序调用 SCNAction 似乎会挂起 SceneKit。

触摸事件或旋转设备似乎可以解除挂起。

复制:

1) 使用旋转飞船启动时的默认 SceneKit 项目。

2) 替换动画代码:

ship.RunAction(SCNAction.RepeatActionForever(SCNAction.RotateBy(0, 2, 0, 1)));

与:

        ship.RunAction(SCNAction.RotateBy(0, 2, 0, durationInSeconds: 3.0f), delegate
        {
            Console.WriteLine("DONE ROTATE");
            ship.RunAction(SCNAction.MoveBy(1, 0, 0, durationInSeconds: 3.0f), delegate
            {
                Console.WriteLine("DONE MOVEBY");
            });
        });

3) 在模拟器或真机上运行(两者的问题都是一样的)

4) 结果是:

  • 飞船可以正常旋转

  • DONE ROTATE 打印出来 OK

  • 现在挂了

  • 点击屏幕(或将设备旋转到横向),然后移动正常并打印出 DONE MOVEBY。

我正在使用 C# 和 Visual Studio for Mac,但我怀疑使用 Xcode 也会发生这种情况。

这是 SceneKit 中的错误吗?如何解决?

也许这与此处描述的问题相同:

SCNAction completion handler awaits gesture to execute

【问题讨论】:

  • 您是否尝试过 SCNAction.sequence([SCNAction]) 函数,而不是在完成处理程序中运行新的 SCNAction。这可能会有所帮助,但我不确定
  • 感谢您的想法,但我现在发现了问题的根本原因,即回调不在主线程上(!)。同样使用带有 Run 的 Sequence 来执行一个块也会在不同的线程上调用该块。所以一个解决方案是在主线程上调用回调,例如:SynchronizationContext Post...

标签: ios crash scenekit completionhandler scnaction


【解决方案1】:

这是因为默认情况下 SceneKit 不会连续渲染。当点击屏幕时,场景会改变并且会渲染一个新的帧。这就是moveBy 操作不会在rotateBy 操作之后立即触发的原因。

尝试将SCNViewrenderContinuously 属性设置为true,如下所示:

scnView.rendersContinuously = true
ship.runAction(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 3.0)) {
    print("DONE ROTATE")
    ship.runAction(SCNAction.moveBy(x: 1, y: 0, z: 0, duration: 3.0), completionHandler: {
        print("DONE MOVEBY")
        scnView.rendersContinuously = false
    })
}

【讨论】:

  • 感谢您的解释。它确实修复了上面的例子,但我认为它对电池寿命不利(我的游戏不需要连续渲染)并且 rendersContinuously 仅适用于 iOS 11+。我还发现使用 SCNTransaction 或 CABasicAnimation 工作正常(我将添加一个答案来说明如何)。为什么您认为它们有效,但 RunAction 无效?
【解决方案2】:

将 SCNTransaction 与 completionBlock 一起使用不会遇到同样的问题,因此可以正常工作:

        SCNTransaction.Begin();
        SCNTransaction.AnimationDuration = 3.0f;
        SCNTransaction.SetCompletionBlock(() =>
        {
            Console.WriteLine("DONE ROTATE");
            SCNTransaction.Begin();
            SCNTransaction.AnimationDuration = 3.0f;
            SCNTransaction.SetCompletionBlock(() =>
            {
                Console.WriteLine("DONE MOVEBY");
            });
            ship.Position = new SCNVector3(1.0f, 0.0f, 0.0f);
            SCNTransaction.Commit();
        });
        ship.EulerAngles = new SCNVector3(0.0f, (float)Math.PI / 2, 0.0f);
        SCNTransaction.Commit();

(也可以使用 CABasicAnimation 和 CAAnimationDelegate 进行回调。)

由于 SCNTransaction 和 CABasicAnimation 有效,但 RunAction 无效,它看起来确实像 RunAction 中的 Apple 错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-22
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 1970-01-01
    • 2021-03-18
    相关资源
    最近更新 更多