Sait
Sait

Reputation: 19855

Performing actions in order (not asynchronously) on different nodes

Here is an illustration of what I am trying to do:

enter image description here

Here is my code so far:

import SpriteKit

class GameScene: SKScene {
    let mySquare1 = SKShapeNode(rectOfSize:CGSize(width: 50, height: 50))
    let mySquare2 = SKShapeNode(rectOfSize:CGSize(width: 50, height: 50))

    override func didMoveToView(view: SKView) {
        mySquare1.position = CGPoint(x: 100, y:100)
        mySquare2.position = CGPoint(x: 300, y:100)

        mySquare1.fillColor = SKColor.blueColor()
        mySquare2.fillColor = SKColor.blueColor()

        self.addChild(mySquare1)
        self.addChild(mySquare2)

        let moveAction1 = SKAction.moveTo(CGPoint(x:250, y:100), duration: 1)
        mySquare1.runAction(moveAction1)

        let moveAction2 = SKAction.moveTo(CGPoint(x:300, y:350), duration: 1)
        mySquare2.runAction(moveAction2)
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    }

    override func update(currentTime: CFTimeInterval) {

    }
}

My problem is, I am trying to move the rectangles synchronously (not asynchronously). That is, I want my first rectangle start moving, finish its movement, stop. And then, start my second rectangle moving, finish its movement and stop.

Currently what happens is that, when I run my program, they both start moving at the same time.

I also found SKAction.sequence for actions to play in order, however, I only can use this for actions on the same object. Not in different objects like in my example.

Upvotes: 4

Views: 675

Answers (3)

Epsilon
Epsilon

Reputation: 1040

As Alessandro Ornano suggests, a way to accomplish this is run your first action with a completion block, where a block of code runs after the completion of the original action. A downside to this approach is that it can create a pyramid of doom when you need to chain together more than two actions at a time.

An alternative way to avoid to the "pyramid" is to define a method that calls itself recursively for each subsequent action in an array:

func runInSequence(actions:[(node:SKNode,action:SKAction)], index:Int) {
    if index < actions.count {
        let node = actions[index].node
        let action = actions[index].action
        node.runAction(action) {
            // Avoid retain cycle
            [weak self] in
            self?.runInSequence(actions, index: index+1)
        }
    }
}

To use this, define/create an array that stores the nodes and actions you want to run in sequence, and call the method with the starting index:

    let nodesAndActions:[(node:SKNode,action:SKAction)] = [
        (mySquare1,moveAction1),
        (mySquare2,moveAction2)
    ]
    runInSequence(nodesAndActions, index: 0)

Upvotes: 3

Kendel
Kendel

Reputation: 1708

You could use the SKAction for running blocks. Then sequence them from the scene.

For example:

func example() {
    let firstRectMove = SKAction.runBlock({
        let move1 = SKAction.moveToX(20, duration: 1.0)
        let move2 = SKAction.moveToY(20, duration: 1.0)
        rec1.runAction(SKAction.sequence([move1,move2]))
    })
    let actionWait = SKAction.waitForDuration(2.0)
    let secondRectMove = SKAction.runBlock({

        let move1 = SKAction.moveToX(20, duration: 1.0)
        let move2 = SKAction.moveToY(20, duration: 1.0)
        rec2.runAction(SKAction.sequence([move1,move2]))
    })
    //self is an SKScene or any other node really...
    self.runAction(SKAction.sequence([firstRectMove,actionWait,secondRectMove]))
}

Upvotes: 4

Alessandro Ornano
Alessandro Ornano

Reputation: 35412

If you want to move the two rectangles sequentially (not in parallel), you could use the completion property of the first action like this (apple docs):

import SpriteKit
class GameScene: SKScene {
    let mySquare1 = SKShapeNode(rectOfSize:CGSize(width: 50, height: 50))
    let mySquare2 = SKShapeNode(rectOfSize:CGSize(width: 50, height: 50))
    override func didMoveToView(view: SKView) {
        mySquare1.position = CGPoint(x: 100, y:100)
        mySquare2.position = CGPoint(x: 300, y:100)
        mySquare1.fillColor = SKColor.blueColor()
        mySquare2.fillColor = SKColor.blueColor()
        self.addChild(mySquare1)
        self.addChild(mySquare2)
        let move1 = SKAction.moveToX(250, duration: 1.0)
        let move2 = SKAction.moveToY(250, duration: 1.0)
        self.mySquare1.runAction(move1,completion: {
            self.mySquare2.runAction(move2)
        })
    }
}

Upvotes: 5

Related Questions