Skaction Completion Handlers; Usage in Swift

SKAction Completion Handlers; usage in Swift

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)

iOS Swift How to use completion handler for For loop SKAction

I tried explaining this to you in another question. a for loop DOES NOT wait for actions to be finished before going on to the next item. The for loop processes all 10 of your items instantaneously (almost). Therefore the output you are experiencing is expected. There are a number of ways that you can do this, but some are more complex than others (ie. completion blocks). The easiest way to do this would just be to track the duration in the for loop and wait for that duration to fire the completion.

According to your code that you presented here ALL 10 items are presented to the screen at the same time, which is contradictory to the last question you asked on this topic.

for x in 0..<10 {
self.run(.wait(forDuration: 1.0)) {
print(x)
}
}

will print out 0,1,2,3,4,5,6,7,8,9 on separate lines all at once 1 second after the code is run, because all the lines in the loop are processed all at once and told to wait 1 second before printing.

var delay: double = 1.0
for x in 0..<10 {
self.run(.wait(forDuration: delay)) {
print(x)
}

delay += 1
}

will print out 0,1,2,3,4,5,6,7,8,9 on separate lines 1 second after the prior one was printed with the last one taking 10 seconds total to output the code is run, because that lines in the loop are processed all at once and told to wait LONGER durations before printing.

var delay: double = 1.0
for x in 0..<10 {
self.run(.wait(forDuration: delay)) {
print(x)
}

delay += 1
}

self.run(.wait(forDuration: delay)) {
print("all done adding objects")
}

this will output the final line ONLY AFTER the last one is done because we have waited the necessary time.

Waiting for SKAction completion inside a for loop

You can achieve this using actions.

First, create actions for the delay and adding the node to the scene.

let waitAction = SKAction.wait(forDuration: 1)
let addNodeAction = SKAction.run {
let node = SKSpriteNode(imageNamed: "example")
self.addChild(node)
}

Next, create a sequence of actions so that the delay always occurs before the node is added to the scene.

let sequenceAction = SKAction.sequence([waitAction, addNodeAction])

Next, create an action that repeats the sequence 10 times.

let repeatAction = SKAction.repeat(sequenceAction, count: 10)

Finally, run this action and watch the nodes appear!

run(repeatAction)

EDIT:

To solve your second question in the comments about needing access to the current character (making each action where the node is added slightly different), loop through your characters to build a sequence of actions, and then run that action.

var actions = [SKAction]()

let waitAction = SKAction.wait(forDuration: 1)

for character in line.characters {

let addNodeAction = SKAction.run {
let node = SKSpriteNode(imageNamed: "example")
// use the character variable here
self.addChild(node)
}

actions.append(waitAction)
actions.append(addNodeAction)

}

let sequenceAction = SKAction.sequence(actions)
run(sequenceAction)

swift: detect which skaction stopped in group

You could create another action to handle what happens after moving, then run these actions as a sequence.

let afterMoveHandler = SKAction.runBlock({
// Your code here
})

let moveSequence = SKAction.sequence([moveUp, afterMoveHandler])
let bearRun = SKAction.group([moveSequence, bearRep])

If you want an extension of SKAction to do what you are looking for, consider the following:

(Code is Swift 3)

extension SKAction
{
static func run(action:SKAction,completion:()->()) -> SKAction
{
return SKAction.sequence([action, SKAction.run(completion)]
}

}

Usage:

let moveUpEvent = SKAction.run(action:moveUp){/*do something here*/}
let bearRun = SKAction.group([moveUpEvent , bearRep])

Completion block never called at end of SKAction sequence of groups

I want to focus your attention to these few lines:

Sample Image

The offending code is:

curtain.run(fadeMoveWipeAndReveal, completion: {
onDone()
print("I'm done!!!!")
})

where you can substitute fadeMoveWipeAndReveal with one of the other action like simply show:

let show = SKAction.run(reveal)

and you will see the completion is never called. Why? Because it run the first time during the WipeCurtain initialization but the action is removed before it completes, so the next time you re-call this one to run through touchesBegan, completion will never called.

You can test it by putting a breakpoint to the curtain.run code and launch the project:

Sample Image

as you can see the breakpoint stop the project immediatly during initialization, in this phase, action is removed before it completes.

About your workaround,that's correct, it works because you remove completion and use the simply SKAction.sequence that is executed correctly every time you call it.


Details:
The copy method works for me, I suspect some people have problems with it because around internet there are more versions about the SKEase and maybe some of that could have a problem, but this version works well as demonstrated by these two screenshots below:

Sample Image

Sample Image

How do you make Swift wait for SKAction to be executed?

Instead of calling startScreen.run(), call startScreen.run(_:completion) and do the things you want to do after the SKActions have run inside the completion handler. See the documentation.

startScreen.run(animateList, completion: {
self.startScreen.text = "I don't belive we have met before"
self.startScreen.run(animateList)
})

Detecting if SKAction is done running, Swift

You should use a completion-handler for your kind of problem:

//Run the action
iapButton.runAction(iapButtonReturn,
//After action is done, just call the completion-handler.
completion: {
firePosition.x = 320
firePosition.y = 280
}
)

Or you could use a SKAction.sequence and add your actions inside a SKAction.block:

var block = SKAction.runBlock({
fire.runAction(fireButtonReturn)
iapButton.runAction(iapButtonReturn)
aboutButton.runAction(abtButtonReturn)
})

var finish = SKAction.runBlock({
firePosition.x = 320
firePosition.y = 280
})

var sequence = SKAction.sequence([block, SKAction.waitForDuration(yourWaitDuration), finish])

self.runAction(sequence)


Related Topics



Leave a reply



Submit