英文:
How to pause a timer run through SKAction in SpriteKit
问题
在我的基于SpriteKit的游戏中,玩家需要在每个关卡中完成一定时间内的任务。在时间到期时,会调用游戏结束方法或下一关的方法。
由于我知道在不同的角度下,使用NSTimer
与SpriteKit结合使用是不好的,所以我在我的GameScene
中将定时器构建为一系列SKActions
,如下所示:
private func runTimer(duration: TimeInterval) {
let wait = SKAction.wait(forDuration: duration)
let endLevel = SKAction.run { [weak self] in
self?.handleEndLevel()
}
run(SKAction.sequence([wait, endLevel]), withKey: "timer")
}
由于我还有一个暂停按钮,用于显示MenuScene
,以及一个恢复按钮,我想要实现的是暂停/恢复定时器。什么是正确的实现方式?
以下是我已经尝试但没有成功的方法:
- 将
GameScene
的属性isPaused
设置为true
。 - 在
GameViewController
中,在呈现GameScene
/MenuScene
时设置SKTransition
的pausesOutgoingScene
和pausesIncomingScene
属性。 - 引入一个
timerNode
作为场景的子节点,然后使用timerNode.run
并设置其isPaused
属性。 - 使用
.speed
而不是.isPaused
。
所有这些方法都导致了相同的结果:即使游戏已经暂停并恢复,定时器仍然在运行,就好像它从未被暂停过,导致关卡在预定时间之前结束。由于我仍在学习SpriteKit,我对isPaused
属性的理解是它用于确定是否执行动作,因此我认为已经运行的动作会被暂停,但这可能是我理解有误。
我知道可能有一个激进的解决方案:在暂停时调用removeAction(forKey: "timer")
,同时存储删除时的时间,并通过一些计算在游戏恢复后设置另一个具有剩余时间的定时器。然而,这看起来不太美观和过于复杂,我无法相信苹果没有实现更简单的方法。
英文:
In my game built on SpriteKit the player has a set amount of time to complete each level, and on expiration either a game over method or a next level one is called.
Since I know that using NSTimer
combined with SpriteKit is bad under different points of view, what I did in my GameScene
is building the timer as a sequence of SKActions
like this:
private func runTimer(duration: TimeInterval) {
let wait = SKAction.wait(forDuration: duration)
let endLevel = SKAction.run { [weak self] in
self?.handleEndLevel()
}
run(SKAction.sequence([wait, endLevel]), withKey: "timer")
}
Since I also have a pause button, which presents a MenuScene
, and a resume button, what I'm trying to achieve is to pause/unpause the timer. What is the proper way to achieve this?
Here's what I already tried with no luck:
- set
GameScene
propertyisPaused
totrue
. - in
GameViewController
, setting thepausesOutgoingScene
andpausesIncomingScene
properties ofSKTransition
when presentingGameScene
/MenuScene
. - introducing a
timerNode
to add as a child to the scene, then sayingtimerNode.run
and setting itsisPaused
property. - using
.speed
instead of.isPaused
.
All of these lead to the same result: even if the game is paused and resumed, the timer runs as it was never paused, so ending the level before it is supposed to. Since I'm still learning SpriteKit, my understanding of the isPaused
property is that it is used to determine wheter actions are performed, so I thought an already running action would be paused, this might be wrong on my part maybe tho.
I know there might be a drastic solution: I can call removeAction(forKey: "timer")
on pause while storing the time at which it was removed, and with some calculation I can set up another timer with the amount of time left once the game is resumed. This looks not pretty and overcomplicated tho and I can't believe Apple did not implement something easier.
答案1
得分: 0
经过调试,我发现问题出在用户点击暂停按钮时,我会呈现另一个场景并在“ViewController”中将gameScene.isPaused
设置为true。如果我在“GameScene”中设置isPaused
属性,而不呈现其他场景,场景实际上会暂停。这种行为的原因对我来说仍然不清楚。
重要提示,必须使用SKAction.pause
单独暂停和恢复SKAudioNodes
,否则背景音乐会暂停,但在恢复时音频播放器状态和场景状态之间会有(小)偏移。
英文:
After a debug, I found out that the issue lies in the fact that when the user taps the pause button I'm presenting another scene and I'm setting gameScene.isPaused
to true in the ViewController
. If I set the isPaused
property in the GameScene
, without presenting other scenes, the scene actually pauses. The reason of this behaviour is still unclear to me.
An important note, SKAudioNodes
must be paused and resumed separately using SKAction.pause
, otherwise the background music will pause but on resume there will be a (small) offset between the audio player state and the scene state.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论