2
votes

I'm trying to have a SKNode move onto the screen on command. I've set up the following SKAction chain so that 1) The node moves up and offscreen, then 2) the node moves down into a starting place, then 3) starts moving around. I've used the following code to try and implement this:

    SKAction *moveUp = [SKAction moveTo: shipIntroSpot1 duration:3.0];
    SKAction *moveDown = [SKAction moveTo:shipSpot1 duration:ship1MovementSpeed];

    [self enumerateChildNodesWithName:@"ship1" usingBlock:^(SKNode *node, BOOL *stop) {
        NSLog(@"RUNNING MOVE UP");
        [node runAction: moveUp];

        [node runAction:moveUp completion:^{
            NSLog(@"RUNNING MOVE DOWN");
            [node setHidden: NO];
            [node runAction: moveDown];
        }];

        [node runAction:moveDown completion:^{

            NSLog(@"STARTING MOVEMENT");
        }];

However, the SKActions don't appear to be firing in the correct order. I used the code so that once one step was completed, the next would start. However, the SKAction doesn't appear to move the node fast enough before the next SKAction starts working. I was under the impressions that using the completion call would mean that the next action wouldn't start until the previous one had finished? That does not appear to be the case here. In addition, if I leave a long-enough duration for step 1 (MOVING UP), it will skip over step 2 (MOVING DOWN) and start executing step 3 (STARTING MOVEMENT). I have absolutely no idea how this is possible. If anyone could point out my error in understanding how to chain different actions together properly, I would appreciate it.

(I did not do a SKAction sequence because I have to "unhide" the node halfway through. I don't think I can put that in a sequence, unless I'm wrong about that too.)

3
look up action sequence if you want to run one action after another (in sequence).LearnCocos2D

3 Answers

10
votes

It does not seem like you understand how runAction is executed.

Using

    [node runAction:moveUp completion:^{
        NSLog(@"RUNNING MOVE DOWN");
        [node setHidden: NO];
        [node runAction: moveDown];
    }];

will not just define a completion to be done at the end of the action, but it runs the action and then calls the completion, so the very first [node runAction:moveUp] needs to be removed.

Secondly, two runAction calls that are made in one function/block will be called simultaneously, and since you call [node runAction: moveUp], [node runAction:moveUp completion:] , and [node runAction:moveDown completion:] all in the same block, they will all be executed simultaneously.

This is the code you are looking for:

SKAction *moveUp = [SKAction moveTo: shipIntroSpot1 duration:3.0];
SKAction *moveDown = [SKAction moveTo:shipSpot1 duration:ship1MovementSpeed];

[self enumerateChildNodesWithName:@"ship1" usingBlock:^(SKNode *node, BOOL *stop) {
    NSLog(@"RUNNING MOVE UP");

    [node runAction:moveUp completion:^{
        NSLog(@"RUNNING MOVE DOWN");
        [node setHidden: NO];
        [node runAction:moveDown completion:^{

            NSLog(@"STARTING MOVEMENT");
        }];
    }];
1
votes

The completion block runs after the action is finished. So, if you want to move up, then move down, then move around, you need to write the following code:

[node runAction:moveUp completion:^{
        [node runAction:moveDown completion:^{
            NSLog(@"STARTING MOVEMENT");
        }];
    }];

What you have right now won't work because you call three runAction's in a row. You first tell it to moveUp, but then immediately override that action with another moveUp, this time with a completion block where you run moveDown. Then, you immediately override that action with a moveDown, so essentially, all you're doing is telling the node to moveDown, everything else is superfluous.

1
votes

I know it's been marked as answered but though I'd offer up an alternative.

An alternative option would be to simply sequence the Actions with just one completion handler

for example (could just be written as a one liner if you wanted):

NSArray *actions = @[moveUp, [SKAction unhide], moveDown];
SKAction *action = [SKAction sequence:actions];

then in your loop just write:

[node runAction:action withCompletion^{
        NSLog(@"STARTING MOVEMENT");
    }];

Hopefully someone will find this useful.