I don't have experience with Sprite Kit. I was wondering is there something similar to Cocos2D schedulers in Sprite Kit ? If no, what should be used NSTimer is the only option? I guess if the only option is using NSTimer we manually need to handle case when application is in background. Thank you.
-
It could be possible to use SKAction sequence with waitForDuration action to simulate scheduler, depends on the use situation. – Dobroćudni Tapir Feb 15 '14 at 22:24
-
@DobroćudniTapir - [self schedule:@selector(fireMethod:) interval:0.5]; - Cocos2D . I want to fire method every X seconds. Cocos2D schedulers, handle cases when app is in background, paused etc.. – User1234 Feb 15 '14 at 22:27
-
the scene runs an update: method every frame. In Kobold Kit you can register nodes to receive that event, too. And a future version will implement cocos2d's way of scheduling selectors/blocks – CodeSmile Feb 16 '14 at 00:38
4 Answers
To achieve functionality similar to cocos scheduler you can use SKAction.
For example for the to achieve something like this
[self schedule:@selector(fireMethod:) interval:0.5];
using SKAction You would write this
SKAction *wait = [SKAction waitForDuration:0.5];
SKAction *performSelector = [SKAction performSelector:@selector(fireMethod:) onTarget:self];
SKAction *sequence = [SKAction sequence:@[performSelector, wait]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[self runAction:repeat];
It isn't best looking, and lacks some flexibility of CCScheduler, but it will pause upon backgrounding, pausing scene/view etc. + it is like playing with LEGOs :)

- 34,902
- 47
- 248
- 470

- 3,082
- 20
- 37
-
2
-
-
Do you mean doing ` self schedule:` in SKScene? it gives an error when I do this in ios9. @DobroćudniTapir – Reza.Ab Feb 22 '16 at 19:37
-
I made a demo for a simple scheduler for use with Sprite Kit in Swift.
NSTimers are difficult to manage because of the app's background-foreground cycle, and SKActions may not really be suited for this (for one - creating scheduled events as an SKAction is a pain and not very readable in the long run + it does not care about the SKScene
's paused state).
The approach I took was to roll out a custom scheduler which allows you to write code like:
Schedule a recurring event
scheduler
.every(1.0) // every one second
.perform( self=>GameScene.updateElapsedTimeLabel ) // update the elapsed time label
.end()
Schedule an event for a specific time
scheduler
.at(10.0) // ten seconds after game starts
.perform( self=>GameScene.createRandomSprite ) // randomly place a sprite on the scene
.end()
Schedule an event for later, and repeat 5 times
scheduler
.after(10.0) // ten seconds from now
.perform( self=>GameScene.createRandomSprite ) // randomly place a sprite on the scene
.repeat(5) // repeat 5 times
.end()
How does it work?
In a nutshell, the scheduler is a class that holds a priority queue of scheduler events. The scheduler maintains a variable that represents the elapsed time in a game. On every frame update:
- The scheduler updates its elapsed time reference
- Checks against the priority queue if any items in the queue need to be run; if it finds them, pops them out of the queue and runs the corresponding action. If this was a recurring event, its next trigger time will be updated and pushed back into the queue
- The scheduler runs forever, unless you stop it explicitly
Since the scheduler works by maintaining an elapsed time counter, it uses yet another roll your own Timer component. The default timing value in Sprite Kit's update
method is not useful when backgrounding/foregrounding an app, so we need to roll out a Timer component as well - this will allow us to calculate a correct time step for our game loop.
I go to some length in explaining the gotchas with finding your timestep further in a blog article.
Summary
NSTimer / GCD based dispatch async methods do not honour your game's notion of elapsed time, and does not integrate with Sprite Kit well. It will not work correctly in all cases (based on your game's logic), and will lead to hard-to-identify timing bugs.
Sprite Kit's
SKAction
s are awesome for running pre-defined actions like applying transformations on nodes, because its built in and respects a scene's paused state. But for scheduling blocks/closures and maintaining control over its execution, its a tough call. Expressing your intent is difficult.SKAction.runBlock
will pause your running block when the scene state ispaused
Roll your own / use a library. This approach gives you the most control and allows you to integrate with a scene's paused state and game's notion of elapsed time. This may seem daunting at first, but if you are already having a mechanism to calculate the time step of your game, making a scheduler on top of that is straightforward. The demo project I shared should provide some info on how to achieve this if rolling out your own / using those components outright if you are on Swift.

- 2,439
- 18
- 25
Inspired from various approaches I've maded an extension for Swift 3:
// © timer
// SCHEDULETIMERWITHINTERVAL maked with SKAction
class func scheduledTimerWith(timeInterval:TimeInterval, selector: Selector,withObject: AnyObject = SKNode(), repeats:Bool)->SKAction {
// instead of NSTimer use skactions
// now starting to search the selector: is in node, node parent or node childs?
let call = SKAction.customAction(withDuration: 0.0) { node, _ in
if node.responds(to: selector) {
node.performSelector(onMainThread: selector, with: withObject, waitUntilDone: false)
} else // check for the direct parent
if let p = node.parent, p.responds(to: selector) {
p.performSelector(onMainThread: selector, with: withObject, waitUntilDone: false)
} else { // check for childs
let nodes = node.children.filter { $0.responds(to: selector)}
if nodes.count>0 {
let child = nodes[0]
child.performSelector(onMainThread: selector, with: withObject, waitUntilDone: false)
} else {
assertionFailure("node parent or childs don't are valid or don't have the selector \(selector)")
}
}
}
let wait = SKAction.wait(forDuration: timeInterval)
let seq = SKAction.sequence([wait,call])
let callSelector = repeats ? SKAction.repeatForever(seq) : seq
return callSelector
}
Usage:
let generateIdleTimer = SKAction.scheduleTimerWith(timeInterval:20, selector: #selector(PlayerNode.makeRandomIdle), repeats: true)
self.run(generateIdleTimer,withKey: "generateIdleTimer")
Timer launched from parent:
if parent = self.parent {
let dic = ["hello":"timer"]
let generateIdleTimer = SKAction.scheduleTimerWith(timeInterval:20, selector: #selector(PlayerNode.makeRandomIdle),withObject:dict, repeats: true)
parent.run(generateIdleTimer,withKey: "generateIdleTimer")
}
Why I should use this method?
It's only an alternative, but it have also the withObject
input parameter if you need to call a method that have an input property.
With this method you can also launch a timer from a node parent and it works (because the method search to the parent and the childs to find the selector..), same thing if you want to launch a timer from a child that not have this selector so the method always search to it's parent or childs (this is useful if you want to often launch removeAllActions
without lose the timer..)

- 34,887
- 11
- 106
- 133
The simplest method:
var _: Timer = Timer.scheduledTimer(timeInterval: 20, target: self, selector: #selector(objcFunc), userInfo: nil, repeats: false)

- 55
- 9