问题
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?
回答1:
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()
})
})
]))
回答2:
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.
回答3:
You can try an alternative solution:
itemB.runAction(SKAction.waitForDuration(0.5)) {
let realDest = CGPointMake(itemA.position.x, itemA.position.y)
let moveAction = SKAction.moveTo(realDest, duration: 2.0)
itemB.runAction(moveAction) {
itemB.removeFromParent()
}
}
Trailing closure in runAction
function is a completion block.
回答4:
You need to change the caller of your runAction
. Use self
to call it. Because you are using a runBlock
and you say that the parasite
runs the action inside it, there is no need to call the function on parasite
. So call it like that:
self.runAction(SKAction.sequence([SKAction.waitForDuration(0.5), moveParasite]))
回答5:
According to the documentation the runBlock executes immediately, and the duration of the moveTo
is not respected. The sequencing of both Code A and Code B is correct, but in the latter case it seems like it is out of sequence since the moveTo()
duration is not respected.
As a solution to the issue of running a block of code resulting in one or more actions, whilst respecting the duration, try this code:
func notSoBadMoveAction() -> SKAction {
let realDest = CGPointMake(itemA.position.x, itemA.position.y)
let moveAction = SKAction.moveTo(realDest, duration: 2.0)
return moveAction
}
itemB.runAction(SKAction.sequence([
SKAction.waitForDuration(0.5),
notSoBadMoveAction(),
SKAction.runBlock {
itemB.removeFromParent()
}]))
This code does use the full duration for the move, and it can replace some (but maybe not all other) other uses of runBlock. If you want to, the function can take parameters as well, and as such be made into an even more general case of generating actions.
ADDITION: Here is an alternate version of the function displaying the possibilites to add actions, and calculate stuff within the function:
func myMoveAction(pos: CGPoint, duration : NSTimeInterval) -> SKAction {
let realDest = CGPointMake(pos.x, pos.y)
let moveAction = SKAction.moveTo(realDest, duration: duration/4)
let moveAction2 = SKAction.moveTo(CGPointMake(realDest.x/2, realDest.y/2), duration: duration * 2/4)
let moveAction3 = SKAction.moveTo(realDest, duration: duration/4)
return SKAction.sequence([moveAction, moveAction2, moveAction3])
}
回答6:
SKAction.runBlock
has a duration of 0.0
. Fortunately, the duration
property is mutable.
badMoveAction.duration = 2.0
should delay the block long enough to run after the action within the block is finished.
来源:https://stackoverflow.com/questions/28957369/delay-next-action-in-skaction-sequence-after-runblock-occurs-swift