Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delay next action in SKAction.sequence after runBlock occurs (Swift)?

The duration property for moveTo isn't followed when inside a runBlock, allowing the subsequent action in a sequence to get executed immediately when it should only get executed after duration seconds.

Code A (sequence properly executed):

let realDest = CGPointMake(itemA.position.x, itemA.position.y)
let moveAction = SKAction.moveTo(realDest, duration: 2.0)

itemB.runAction(SKAction.sequence([SKAction.waitForDuration(0.5), moveAction, SKAction.runBlock {
    itemB.removeFromParent()
}]))

Code B (sequence not properly executed):

let badMoveAction = SKAction.runBlock {
    let realDest = CGPointMake(itemA.position.x, itemA.position.y)
    let moveAction = SKAction.moveTo(realDest, duration: 2.0)
    itemB.runAction(moveAction)
}

itemB.runAction(SKAction.sequence([SKAction.waitForDuration(0.5), badMoveAction, SKAction.runBlock {
    itemB.removeFromParent()
}]))

In Code A, itemB gets removed after the moveAction completes (about 2 seconds). This is the correct sequence.

In Code B, itemB gets removed before badMoveAction finishes, meaning itemB never moves from its original position. It's as if the duration property isn't honored in Code B.

How can we move itemB as in Code B but ensure the next action in the sequence doesn't start until badMoveAction completes?

like image 361
Crashalot Avatar asked Mar 10 '15 06:03

Crashalot


2 Answers

This should do what you want. i just re-arranged the code a little bit.

itemB.runAction(SKAction.sequence([

    // wait for half a second
    SKAction.waitForDuration(0.5),
    SKAction.runBlock({

        // after waiting half a second, get itemA's position
        let realDest = CGPointMake(itemA.position.x, itemA.position.y)
        let moveAction = SKAction.moveTo(realDest, duration: 2.0)

        // move to that position, after we get there, remove itemB from scene
        itemB.runAction(moveAction, completion: {
            itemB.removeFromParent()
        })

    })
]))
like image 91
hamobi Avatar answered Nov 27 '22 14:11

hamobi


EXPLANATION: When you execute a block of code, it is executed asynchronously. This means the code will be executed on a separate queue while the rest of your code continues to execute.

In the case of Code A this does not cause an issue because the moveTo action is run on the current queue, completed and then the runBlock is fired.

In the case of Code B this creates an issue because the badMoveAction block is fired, it begins executing on a separate queue and the code continues on to the next piece which happens to be the remove action that removes itemB, while the badMoveAction was executing in the background. If you did something else in that runBlock you will see them run simultaneously, but because you removed it, everything is removed.

Solution If you are saying you want to add badMoveAction to a node and have that calculate every time you could do something like this:

let waitAction = SKAction.waitForDuration(0.5)
let removeAction = SKAction.removeFromParent()

let sequence = SKAction.sequence([waitAction, moveAction(), removeAction])

func moveAction() -> SKAction {
    let realDest = CGPointMake(itemA.position.x, itemA.position.y)
    let moveAction = SKAction.moveTo(realDest, duration:2.0)
    return moveAction()
}

*Code is just for example of what you can do to resolve this issue.

like image 43
chrissukhram Avatar answered Nov 27 '22 13:11

chrissukhram