Search code examples
functionsprite-kitwaitskaction

How to wait, then do something, in the GameScene


SKAction has waiting for duration abilities, for a period of time on a node. And seems to perform actions on nodes. Like moveTo, etc.

If I don't want that, rather I'd prefer to call functions within GameScene after a period of time, how do I do that with SpriteKit in the GameScene, not on a Sprite or other Node?

Are SKActions the way to do this? The only way to do this?

Yes. This question IS that ridiculously simple. I lack the heuristics and terminology to find an answer. Just keep looping around on how SKAction waits are calls on SKSprites for things like scale, rotation, etc, after time. Which isn't want I want/need.

Update:

Desired outcome, inside GameScene

doSetupStuff() // does some stuff...

waitForAWhile() // somehow wait, perhaps do somethings in here, while waiting

doSomethingElse() // does this after the waitForAWhile has waited

UPDATE 2:

What I think happens, again, inside didMove(to view...)

        func wait(){
            let timeToPause = SKAction.wait(forDuration: 3)
            run(timeToPause)
        }


     let wontwait = SKAction.wait(forDuration: 3)
  run(wontwait)
  thisFunction(willnot: WAIT"it starts immediately")

    wait()
    thisFunction(forcedToWait: "for wait()'s nested action to complete")

UPDATE 3:

Found a way to get the delay without using SKActions. It's a little crude and brutal, but makes more sense to me than SKActions, so far:

DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
            print("I waited ten seconds before printing this!")
        }

Solution

  • An option, as you cited, is to manage this externally. The way I typically manage this sort of thing is to have an externally run update cycle. One that

    To drive this updater, you could use either CADisplayLink (which is what I use right now with my OpenGL renderer) or a dispatch source timer (which I have used with my SpriteKit engine). When you use an updated, you want to calculate the delta time. The tick handler could look something like:

    func tickHandler() {
        let currTime = NSDate().timeIntervalSince1970
        let dt = lastTime - currTime // lastTime is a data member of the class
    
        // Call all updaters here, pretend "updater" is a known updater class
        updater.update(dt)
    }
    

    And updater's update method would look something like:

    func update(deltaTime:NSTimeInterval) {
        // Do your magic
    }
    

    I typically have a main overall updater running independent of what people are calling scenes. Example usage would be something like having an attract mode like in old school arcade games. There they show title screen, sample game play, high scores, rinse and repeat. Scenes would be title, game play, high score. Here you can your main updater manage the time and coordinate the construction/destruction/switching of the scenes. Note this implies having an overall scene manager (which is actually quite handy to have).

    For your case, you could use this updater to drive the GameScene updater. It's updater could look something like:

    func update(deltaTime:NSTimeInterval) {
        switch state {
            case .SetupState:
                // noop?
                println("I'm in setup") // Shown just so you can see there is a setup state
            case .WaitState:
                waitTime += deltaTime
                if waitTime >= kWaitTime {
                    // Do whats you gots to do
                    doSomethingElse()
                    state = .NextState
                }
            case .NextState:
                // blah blah blah blah
        }
    }
    

    So the flow to do this call path from your driver (CADisplayLink or dispatch source) would be something like:

    tickHandler -> master updater -> game scene updater

    Some will def find this is perhaps a little heavy handed. I, on the other hand, find this very helpful. While there is obviously some time management and the loss of being able to fire and forget, it can help provide more control for orchestrating pieces, as well as arbitrarily changing state without having to worry about killing already queued actions. There is also nothing that says you still cannot mix SKAction. When I did use SpriteKit, I did all my updating this way along with some dispatched items. I only used SKAction to update hierarchy. Keep in mind that I used my own animation and physics system. So at least for me I had a lot less dependency on SpriteKit (it effectively was just a renderer for me).

    Note you have to have your own means to handle pause and coming to foreground where your timer will need to be resynced (you only need to worry about tickHandler). Breakpoints also will cause time jumps.