Reputation: 19855
Here is an illustration of what I am trying to do:
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
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
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
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