TCProgressTimer – A SpriteKit progress timer

Hello all.  For my first legit blog post, I’d like to share a very basic progress timer I made while messing around with SpriteKit.  It’s very similar to CCProgressTimer if you’ve ever used cocos2d.

In this prototype game, the player must position her units appropriately to successfully defend against waves of creeps and prevent them from reaching the end of a path. I created a nice wave indicator node that shows how long it will take for the next wave of creeps to arrive on the map.  The wave indicator node’s background is gray and slowly fills with an orange color.  You can see the wave indicator in this screenshot from a prototype of the game:

shot1

I decided to pack up the progress timer into an open source demo project so that you all could modify it for usage in your own SpriteKit games.  The progress timer object is named TCProgressTimer.

Usage

Usage of TCProgressTimer is simple. Initialize the object using one of the two initializers

- (id)initWithForegroundImageNamed:(NSString *)foregroundImageName
              backgroundImageNamed:(NSString *)backgroundImageName
               accessoryImageNamed:(NSString *)accessoryImageName;

- (id)initWithForegroundTexture:(SKTexture *)foregroundTexture
              backgroundTexture:(SKTexture *)backgroundTexture
               accessoryTexture:(SKTexture *)accessoryTexture;

The background and accessory parameters are optional – you can pass nil if you don’t need a background or an accessory view on the progress timer.  Once initialized, just throw the progress timer into your scene somewhere and use the setProgress: method to set the fill percentage (a value from 0.0 to 1.0).

Implementation

The implementation is pretty simple as well.  TCProgressTimer is a SKSpriteNode subclass which  has three layers:  a background texture, a foreground texture, and an accessory texture.

@interface TCProgressTimerNode ()

@property (nonatomic, strong) SKSpriteNode *backgroundImageSpriteNode;
@property (nonatomic, strong) TCProgressTimerForegroundCropNode *foregroundCropNode;
@property (nonatomic, strong) SKSpriteNode *accessorySpriteNode;

@end

The background and accessory nodes are nothing special.  The magic lies in the TCProgressTimerForegroundCropNode that is sandwiched between the two.  TCProgressTimerForegroundCropNode is an SKCropNode subclass that consists of a SKSpriteNode and an SKShapeNode that is used as a mask.

@interface TCProgressTimerForegroundCropNode ()

@property (nonatomic, strong) SKSpriteNode *indicatorSpriteNode;
@property (nonatomic, strong) SKShapeNode *maskShapeNode;

@end

All the magic is in two methods:

- (void)initializeMaskShapeNode
{
    _maskShapeNode = [SKShapeNode node];
    _maskShapeNode.antialiased = NO;
    _maskShapeNode.lineWidth = _indicatorSpriteNode.texture.size.width;

    self.maskNode = _maskShapeNode;
}

- (void)setProgress:(CGFloat)progress
{
    progress = 1.0f - progress;

    CGFloat startAngle = M_PI / 2.0f;
    CGFloat endAngle = startAngle + (progress * 2.0f * M_PI);

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointZero
                                                        radius:self.radius
                                                    startAngle:endAngle
                                                    endAngle:startAngle
                                                    clockwise:YES];
    self.maskShapeNode.path = path.CGPath;
}

The SKShapeNode is initialized, given a lineWidth equal to the width of the texture provided upon initialization, and assigned as a mask node. Later, when setProgress: is called, a UIBezierPath is generated using bezierPathWithArcCenter: and the newly generated path is applied to the mask node. That’s really all there is to it!

Here’s a shot from the demo showing three versions of the progress timer. The first uses the foreground image only. The second adds a background image. And the third adds the accessory image.

shot2

Source Code

I probably won’t be touching the progress timer much more because this basic implementation fit my prototyping needs.  I’ve posted the complete source code wrapped up in a demo project on BitBucket.

You can download the source code here.

Note:  At the time of this writing, SKShapeNode appears to leak a small amount of memory.  Several other developers in the Apple dev forums have reported having the same leak with SKShapeNode.  Hopefully we will get a fix soon!

Thanks for reading!

2 thoughts on “TCProgressTimer – A SpriteKit progress timer

  1. Uwe

    Hi, how can I get a signal in my scene when the timer is 100%. For example, I want to stop the running level then.

    Thanks!!

Comments are closed.