我试图了解 SpriteKit 场景的帧周期何时在主 iOS 运行循环中运行。具体来说,我很关心 AppDelegate 的applicationDidBecomeActive(_:)
方法。我一直认为该方法是在应用程序激活后调用的,但在您呈现的场景的帧周期运行之前。
这对我正在构建的项目很重要,因为我使用该applicationDidBecomeActive(_:)
方法来执行一些时间敏感的任务,例如检查时间戳、设置标志、启动计时器等。所以我需要可靠地预测在帧周期内何时调用此方法(让我们称之为“游戏循环”)。
我做了一些测试,这表明游戏循环在与applicationDidBecomeActive(_:)
方法相关的不同时间运行,具体取决于应用程序运行的 iOS 版本。这是一个问题,因为这意味着我不能依赖此方法的单个实现来在正确的时间执行我需要的任务。
我想确切地知道何时applicationDidBecomeActive(_:)
调用与 SpriteKit 游戏循环相关的时间。这似乎是任何编写 SpriteKit 游戏的人都需要了解的基本知识。我很震惊地看到它似乎因操作系统版本而异。我可能在测试和假设中犯了错误。但我会报告我在这里发现的内容,看看是否有其他人注意到这一点,以及是否有人可以解释这种奇怪的行为。
在我当前的项目中,我一直在运行 iOS 12.4 的物理 iPhone 上进行测试,有时还使用运行 iOS 13 的 iPhone 的模拟器。通过 usingprint
语句,我观察到AppDelegate
'applicationDidBecomeActive(_:)
方法和SKScene
'update(_:)
方法在不同的顺序,具体取决于使用的 iOS 版本。
请注意,我的项目使用UIViewController
'viewDidLoad()
方法来呈现场景。我尝试viewWillLayoutSubviews()
改用,希望事情可以以这种方式更可靠地工作。但事实证明这更不可靠,所以我不会在这里讨论。
方法调用顺序(iOS 12.4):
didFinishLaunchingWithOptions
viewDidLoad
didMove
update
applicationDidBecomeActive
update
...
方法调用顺序(iOS 13):
didFinishLaunchingWithOptions
viewDidLoad
didMove
?
applicationDidBecomeActive
update
...
您可以看到两个版本的操作系统都先调用了AppDelegate
方法application(_:didFinishLaunchingWithOptions:)
,然后才加载视图。在viewDidLoad()
,我打电话让视图呈现我的SKScene
。正如预期的那样,didMove(to:)
在视图呈现场景之后调用场景的方法。但接下来发生的是奇怪的部分。
在 iOS 12.4 中,update(_:)
调用了场景的方法,这表明场景执行了其游戏循环的单次运行。然后AppDelegate
调用它的方法applicationDidBecomeActive(_:)
。接下来,该update(_:)
方法再次运行。然后update(_:)
,正如预期的那样,随着场景的游戏循环每秒触发 60 次,不断地被调用。
在 iOS 13 中,该update(_:)
方法不会在被调用后立即didMove(to:)
被调用。相反,applicationDidBecomeActive(_:)
在 之后调用didMove(to:)
。只有这样,该update(_:)
方法才会运行(然后按预期继续运行)。
所以基本上,这里的问题是,在 iOS 12.4 中,游戏循环似乎在它出现后立即运行一次,beforeapplicationDidBecomeActive(_:)
被调用。但在 iOS 13 中,这不会发生。
iOS 12.4 中的游戏循环在调用之前多运行一次是一个问题applicationDidBecomeActive(_:)
。这使得游戏的生命周期在不同版本的操作系统之间不一致,这意味着我将不得不编写不同的代码来处理不同操作系统版本的情况。要么,要么我必须重新设计依赖于applicationDidBecomeActive(_:)
找到更一致的处理方式的应用程序部分。这也让我想知道游戏循环的额外运行是否是 iOS 12 中的一个错误。
我一直认为应用程序的生命周期在操作系统版本之间是一致的(至少关于 和 的方法调用顺序AppDelegate
)SKScene
。但这一发现使所有这些都受到质疑。我还没有测试过其他版本的 iOS,因为即使这是所有操作系统版本之间的唯一差异,它仍然意味着您的代码必须根据操作系统版本以不同方式处理事情。
为了给这个分析增加一个皱纹......
我还制作了一个新的 SpriteKit 模板项目并执行了相同的测试。我发现了同样的差异,但有一个额外的特点:在 iOS 12.4 中,该update(_:)
方法在被调用之后立即被调用两次didMove(to:)
,在applicationDidBecomeActive(_:)
被调用之前。在 iOS 13 中,行为与上述相同。
我不确定为什么update(_:)
会像在我的其他项目中那样触发两次而不是一次。这似乎很奇怪。但是这个“干净”模板项目中的测试表明这是一个真正的问题,而不是我自己的代码中的一些错误。
重申我的问题......
我想知道是否有其他人注意到这一点。也许我的结论是错误的。如果这是一个真正的问题,我想知道是否有任何“修复”可以使游戏循环在所有操作系统版本中以一致的方式工作。如果没有,任何人都可以提出一个好的解决方法,以便您的代码在applicationDidBecomeActive(_:)
游戏循环首次触发之前始终运行?我已经有了一些想法。但首先,我想确认这是 iOS 的实际问题,还是我自己的代码中的错误。