Search code examples
xcodesprite-kita-star

Sprite Kit delay on runAction completion?


I'm building a game with Sprite Kit that implements A* Pathfinding. I'm using a grid system (blocks) that the character uses to calculate the path, it works great. My problem is the animation.

I'm using a NSMutableArray to hold the steps and removes a step each time the move has been completed (runAction completion). It works but the animation freezes for a microsecond between completion and the new move making it look "choppy", its not a smooth move...

I've tried a lot of things and the only thing that works is to change the duration to about 1.5 seconds to move 32px (its going to be a sloooooow game ;) ). When I want the duration I need (0.7) the problem is there.

I really hope someone can help me.

Here is some code:

-(void)animateChar {

    self.currentStepAction = nil;

    if (self.pendingMove != nil) {
        tileMap *moveTarget = pendingMove;
        self.pendingMove = nil;
        self.shortestPath = nil;
        [self moveToward:moveTarget];
        return;
    }

    if (self.shortestPath == nil) {
        return;
    }


    // WE HAVE REACHED OUR BLOCK! CHECK IF ITS CLICKED OR STROLLING
    if ([self.shortestPath count] == 0) {

        self.shortestPath = nil;
        self.moving = FALSE;
        self.strolling = FALSE;

        if (self.minionBlock) {
            CGPoint diff = ccpSub(self.minionBlock.position, self.parentBlock.position);
            if (abs(diff.x) > abs(diff.y)) {
                if (diff.x > 0) { [self runAnimation:_faceRight speed:0.3]; } else { [self runAnimation:_faceLeft speed:0.3]; }
            } else {
                if (diff.y > 0) { [self runAnimation:_faceUp speed:0.3]; } else { [self runAnimation:_faceDown speed:0.3]; }
            }

            // SET TIMER FOR REMOVING THE BLOCK. EVERY 0.5 SECONDS IT REMOVES X ENDURANCE.
            self.blockTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(pickBlock) userInfo:nil repeats:YES];

        } else {

            [self setMinionBusy:FALSE];
            [self runAnimation:_waiting speed:0.3];
            [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(selectRandBlockAndMove) userInfo:nil repeats:NO];
        }

        return;
    }

    ShortestPathStep *s = [self.shortestPath objectAtIndex:0];

    // CHECK HOLE ON NEXT DOWN
    tileMap *checkHole = [s.theBlock getNeighbor:1];

    if ([checkHole isBlockType] == 2) { // THERE IS A HOLE, WE CANT REACH!

        if ([_scene getRecentBlock] == self.minionBlock) { [_scene controlHud:1]; }

        [self removeBlockBar];
        [self.minionBlock setOccupied:FALSE];
        self.minionBlock = nil;
        self.currentStepAction = nil;
        self.pendingMove = nil;
        self.shortestPath = nil;
        [self runAnimation:_waiting speed:0.3];
        [self addBubble:1];

        if (!self.stuck) {
            [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(selectRandBlockAndMove) userInfo:nil repeats:NO];
        }

        return;
    }

    CGPoint futurePosition = s.theBlock.position;
    CGPoint currentPosition = self.parentBlock.position;
    CGPoint diff = ccpSub(futurePosition, currentPosition);

    if (abs(diff.x) > abs(diff.y)) {
        if (diff.x > 0) { [self runAnimation:_faceRight speed:0.2]; } else { [self runAnimation:_faceLeft speed:0.2]; }
     } else {
        if (diff.y > 0) { [self runAnimation:_faceUp speed:0.2]; } else { [self runAnimation:_faceUp speed:0.2]; }
    }

    self.parentBlock = s.theBlock;

    currentStepAction = [SKAction moveTo:s.theBlock.position duration:1.2];

    [self runAction:currentStepAction completion:^{
        [self animateChar];
    }];

    [self.shortestPath removeObjectAtIndex:0];
}

Solution

  • You can try performing the completion as an animation sequence:

    [self runAction:[SKAction sequence:@[currentStepAction,
                                        [SKAction runBlock:^{ [self animateChar]; }]
                                        ]]];
    

    Also this will not call animateChar when the SKAction is aborted while animating.