Search code examples
iossprite-kitprogress-barskspritenodeskcropnode

iOS SpriteKit run progress bar in infinite loop


TL;DR - In spritekit, In a while loop, I'm using a for loop to do arbitrary work to delay the next call of the while loop so that my power bar doesn't run ridiculously fast. Is there a better way to make a small delay in spritekit?

Hey everyone,

I'm making a game that involves a user setting the power for a cannon before launching a projectile from it. I want the power bar to go from 0-100 then 100-0 and loop like this. The power bar runs while the user is tapping a button, and as soon as they release I calculate the speed of the projectile based on where the power bar is. My plan was to use a SKCropNode to show a progress bar image based on the progress of the bar. This works well using the following class for the progress bar:

//Progress bar Header

#import <SpriteKit/SpriteKit.h>

@interface CustomProgressBar : SKCropNode
- (void) setProgress:(CGFloat) progress;
@end

//Progress bar Implemenation

#import "CustomProgressBar.h"
@implementation CustomProgressBar

- (id)init {
if (self = [super init]) {
    self.maskNode = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor]    size:CGSizeMake(50,20)];
    SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"progressBar.jpg"];
    sprite.anchorPoint = CGPointMake(0, 0);
    [self addChild:sprite];
}
return self;
}

- (void) setProgress:(CGFloat) progress {
self.maskNode.xScale = progress;
}

@end

and I use this function to update the power bar (which uses a for loop to delay the update of the bar):

-(void)runPowerBar {
    i = 1;
    progressBar.hidden = NO;
    power = 10;
    while (!self.isStarted){
        float prog = power * 0.00001;
        multiplier = prog;
        [progressBar setProgress:prog];

        for(int z = 0; z < 100; z++){
           // this is bad and inconsistent, need a new way
           // to delay the next call of [progressBar setProgress:prog]
        }

        power = power + i;

        if (power > 1000000) {
            i = -1;
        }
        else if (power < 1) {
            i = 1;
        }
    }
}

I call the power bar as follows, after initializing the powerbar:

backgroundQueue = dispatch_queue_create("com.joshnussbaum.powerBarQ", 0);
            dispatch_async(backgroundQueue, ^{
                [self runPowerBar];
            });

Solution

  • Although you appear to have found a solution in Sanders answer, I would advise using SKScene's update: method, which is part of the SpriteKit rendering loop. Adding your own asynchronous calls will eventually bite you in the behind.

    The update: method is called once every frame and should be more than enough for your needs. In this way, you also ensure not updating your power bar more than is necessary, i.e., more than once per frame.

    Below image is taken from Apple's SpriteKit Programming Guide - read more on the Sprite Kit rendering loop here.

    The steps used by an SKScene to execute the rendering loop. (Apple)