12

I'm new to Swift and SpriteKit. A lot of the samples of SpriteKit Actions are in Objective C, which I can't map to, nor get working, in Swift.

If running an SKAction, and upon SKAction completion wanting to do something else, how do I get this right, in Swift?

    spaceMan.runAction(spaceManDeathAnimation, completion: {
        println("red box has faded out")
    })

Any ideas will be greatly appreciated.

Edit:

for i in 0...29 {
    textures.append(SKTexture(imageNamed: "spaceManDeath_\(i)"))
}
spaceManDeathAnimation = SKAction.repeatActionForever(SKAction.animateWithTextures(textures, timePerFrame: 0.15625))
Confused
  • 6,048
  • 6
  • 34
  • 75
spacecash21
  • 1,331
  • 1
  • 19
  • 41
  • What you want is a SKAction running a block in a SKAction sequence. – BotMaster Apr 14 '15 at 12:50
  • what do the spaceManDeathAnimation action does ? how it is defined? can you share the code for it as well? – giorashc Apr 14 '15 at 12:50
  • 1
    Search the page for "completion" to get some good examples. http://www.gamefromscratch.com/post/2014/07/02/Game-development-tutorial-Swift-and-SpriteKit-Part-4-Actions.aspx – sangony Apr 14 '15 at 12:54

3 Answers3

6

Found an issue here:

spaceManDeathAnimation = SKAction.repeatAction(SKAction.animateWithTextures(textures, timePerFrame: 0.15625), count: 1)

Also,as sangony posted a very nice link - solved the completion block syntax to

    spaceMan.runAction(spaceManDeathAnimation, completion: {() -> Void in
        println("death")
    })

Very big thanks to everybody for contributions for a solution!

spacecash21
  • 1,331
  • 1
  • 19
  • 41
4

You completion code is not called since your "death" action is running forever, which means it will never end.

You can use

+ repeatAction:count:

method for setting a count for how many repeats will be made before finishing:

spaceManDeathAnimation = SKAction.repeatAction(SKAction.animateWithTextures(textures, timePerFrame: 0.15625), count:5)
giorashc
  • 13,691
  • 3
  • 35
  • 71
4

In Swift you can use this extension:

extension SKNode
{
    func runAction( action: SKAction!, withKey: String!, optionalCompletion: dispatch_block_t? )
    {
        if let completion = optionalCompletion
        {
            let completionAction = SKAction.runBlock( completion )
            let compositeAction = SKAction.sequence([ action, completionAction ])
            runAction( compositeAction, withKey: withKey )
        }
        else
        {
            runAction( action, withKey: withKey )
        }
    }
}

Use for example as:

myShip.runAction(move,withKey:"swipeMove",optionalCompletion: {
  //Here action is ended..do whatever you want
}

Swift 3:

extension SKNode
{
    func run(action: SKAction!, withKey: String!, optionalCompletion:((Void) -> Void)?) {
        if let completion = optionalCompletion
        {
            let completionAction = SKAction.run(completion)
            let compositeAction = SKAction.sequence([ action, completionAction ])
            run(compositeAction, withKey: withKey )
        }
        else
        {
            run( action, withKey: withKey )
        }
    }

    func actionForKeyIsRunning(key: String) -> Bool {
            return self.action(forKey: key) != nil ? true : false
    }
}

Usage:

myShip.runAction(action: move, withKey:"swipeMove", optionalCompletion: {
       //Here action is ended..do whatever you want
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • I apologise for two very stupid questions: Why do you assign a `withKey: String!` to this first action? – Confused Nov 16 '16 at 07:34
  • What does the second function do: `func actionForKeyIsRunning(key: String) -> Bool { return self.action(forKey: key) != nil ? true : false }` – Confused Nov 16 '16 at 07:34
  • 1
    @Confused It simply check if an action is currently in running. It is useful do for example : if !sprite.actionForKeyIsRunning("rotate") { // do rotation) } to avoid rotation override – Alessandro Ornano Nov 16 '16 at 07:42
  • argh, this is pretty cool. So, let me see if I have this right??? You can start the action, as per your example, and later check if it's running, like this? `if myShip.actionForKeyIsRunning("swipeMove"){myShip.removeActionForKey("swipeMove")}` – Confused Nov 16 '16 at 10:50
  • 1
    Yes, it could be used also to remove if you want as you write, of course. – Alessandro Ornano Nov 16 '16 at 10:53
  • Completion handlers are horribly under-documented by Apple. They seem like one of the more Swifty things in SpriteKit, too. – Confused Nov 16 '16 at 10:58
  • Yes I know. Completion handlers should tell you when a task has been completed. In this case , you last action is a "interruption of service" because you remove an action when it is in run – Alessandro Ornano Nov 16 '16 at 11:02
  • 1
    Sorry I've seen just only your first comment, the answer for you comment is: because you can identify an action with the key. – Alessandro Ornano Nov 16 '16 at 11:20
  • @AlessandroOrnano, use `@inline(__always)` to get better performance on your `actionForKeyIsRunning` method. Inlining a function will reduce overhead that come with functions like pushing and popping to the stack at the cost of a larger binary size. Since your method is tiny, you will barely notice the increase in size, but you will see an increase in performance if you did a test on it. – Knight0fDragon Jan 06 '17 at 20:51