使用计时器vs更新来运行游戏SpriteKit

时间:2017-04-09 19:34:39

标签: swift sprite-kit

我很好奇我的游戏使用了什么。计时器:

let goodFPS = SKAction.wait(forDuration: 0.01666)
let mainGameRunner = SKAction.run {
//my code is here
}
let toRepeat = SKAction.repeatForever(SKAction.sequence([goodFPS,mainGameRunner]))
inGameHighScore.run(toRepeat,withKey:"mainGame")

或更新功能:

override func update(_ currentTime: TimeInterval){
//my code is here
}

哪种提供更快更一致的更新?

注意:我的帧速率在45到58之间

3 个答案:

答案 0 :(得分:1)

我很确定SKAction的计时工具是基于调用update的同一游戏循环。

SKAction的优势在于它很火,而且忘了你,而使用update会因设置和检查一堆定时器变量而变得尴尬。

答案 1 :(得分:1)

I don't have a ton of experience with SpriteKit but I do have some experience making games and animations in iOS.

There is a class called CADisplayLink that fires a call to a delegate every time the screen is refreshed, this is a great way to update the screen, either in a game or in an animation, because you can know it will be called every frame and no more.

I'm not sure if SpriteKit uses that class to fire the update method, but I'm sure it uses something similar. This is usually called the run loop.

SKActions run on top of this run loop.

By creating your own run loop using a wait action, not only you're not gaining any benefits, you could be introducing inconsistencies in the way your logic is run.

Imagine that you have your wait action set to 0.01 seconds (I rounded it down for simplicity). If the screen is refreshing faster than you expect, and the action is updated every 0.009 seconds, the first time it's checked, it won't fire because there's a remaining 0.001 second on the wait command, so another 0.009 seconds will pass, and your code will be executed after 0.018 seconds, instead of your desired 0.01. Not only that, but two frames will have passed between the execution.

If you use the update function, you can know that your logic will be executed once every screen refresh, and no more.

答案 2 :(得分:1)

我决定完全改写我的回答,以使其更清晰。

首先,我认为你正在以错误的方式解决FPS问题。您不能“强制”比设备可以提供的更快的帧速率。如果你假设每一帧都是一致的,那么你的游戏中的动作就是基础,那么你做错了。这实际上是他们在早期的表现,因为CPU速度很慢,而且从一代到新一代的差异起初并不太糟糕。但是在较年轻的硬件上运行一个旧的DOS游戏将是棘手的,因为帧速率太高,以至于整个游戏机制变得不稳定或者太快而无法播放。

这里的概念是“随着时间的推移”思考,并缩小与两帧之间经过的时间相关的任何行动。

update()方法通过每帧提供当前系统时钟状态为您提供机会。通过跟踪最后一帧的时间,您可以计算当前帧的时差,并使用该差异缩小您正在做的事情。

建议不要使用计时器以一致的帧速率获取更新。您可能在给定的时间间隔内调用更新闭包,但该闭包内的代码花费时间自行执行,并且根据您的游戏逻辑,它甚至可能具有不同的执行时间。因此,暂停时间可能是一致的,但在该暂停之前和之后运行的代码可能不一致。那么如果你在较慢的CPU上运行游戏会发生什么?代码速度会发生更大变化,使您的时间不准确。

反对在你的游戏循环中使用SKAction的另一个观点就是它们。动作是内存中的一个对象,可以被多个对象重用。如果您正在进行“跳转”操作,例如,建议将该操作存储在某处,并在每次需要“跳转”的内容时重用相同的对象,无论它是什么节点。你的游戏循环意味着每帧都要执行,而不是由不同的对象执行。行动也是一次性的。这意味着即使它正在运行,你也可以杀死它。如果你将游戏循环放在一个动作中,它可能会由SKScene运行。如果您在场景中使用其他操作,它会立即成为一个谜题,因为除了让它成为术语之外,只有两种方法可以删除操作:删除所有操作或使用标识符键创建操作并使用该键删除任何操作用那把钥匙。如果您还没有看到它,它会强制您将标识符放在场景将要运行的每个操作上并逐个删除它们。而且它们再次为一个错误而打开了一扇门,这个错误将摆脱你的游戏循环,因为,记住它,行动是可以的!然后,也没有保证你的行动将在每一帧开始执行。

为什么要使用update()方法?只是因为它是在你的场景中构建的。无论如何,每个帧update()首先被调用。那么,行动就会被执行。您无法像操作一样意外地清除update()方法。您不必注意导致内存泄漏的强/弱关系,因为您正在引用闭包内部的对象,就像执行操作一样。

建议阅读:

  1. SKAction API reference:你显然错过了这些。
  2. SKScene API reference:阅读spritekit中的帧处理。它将帮助您了解他们如何将所有内容放在一起。
  3. 我希望它能让事情变得更加清晰。