
The next item on the update project list is making a countdown timer. For those who haven’t read earlier episodes of this blog, there is a bug I cannot find. For a very small number of users, the game ends immediately when starting in hard mode. This might just be a user problem not moving the penguin fast enough, which I did get some reports from users. To solve that, we’ll add a three-second delay between pressing the button to start the game and running the game. For feedback during that time, I am building a countdown timer. I tried to do this in a method to drop directly into what we already have, but found nothing worked. The problem is update:
and the frame cycle.
As you can find in Apple’s Sprite Kit Programming Guide there is a cycle of methods every frame. As developers, we can override three methods that update the frame in different ways. The first is update:
, where a lot of the actions take place. Actions are then evaluated by the system. Next, didEvaluateActions:
allows us to handle the scene after the action for that frame. The physics engine then kicks in. The evaluations of the physics engine can then be handled just before Sprite Kit renders the scene in the view.
I tried making a few methods that would run the countdown timer using SKAction
. The above cycling caused them not to work: They dumped all the timing labels in a few frames, piled on top of each other. In order to have both a timer and changes to the label with the countdown, we have to put our code in update:
and not out of it. Essentially, we will update the label with the current time, which conveniently is update:
‘s parameter.
I made a playbox to look at this without distractions. Starting with a new Sprite Kit template, I cleaned out the spaceship, and “Hello World” label.
I’ll add three instance variables:
-
SKLabelNode
,for the timer display -
BOOL
flag, to let us know to start the countdown -
NSTimeInterval
,to let us have a start time.
Add this to the code:
@implementation SLMyScene{ SKLabelNode *countDown; BOOL startGamePlay; NSTimeInterval startTime; }
Add to initWithSize:
the following to make the label:
countDown = [SKLabelNode labelNodeWithFontNamed:"Futura-Medium"]; countDown.fontSize = 70; countDown.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame)*0.85); countDown.verticalAlignment =SKVerticalAlignmentCenter; countDown.fontColor = [SKColor whiteColor ]; countDown.name = @"countDown"; countDown.zPosition = 100; [self addChild:countDown];
Add the following to update:
countDown.text = [NSString stringWithFormat:@"%i", (int)currentTime];
This casts current time as an integer, and then prints it as one. Now when we run, we get the system time and date in seconds.
Let’s expand on this. For the playbox, we will set reset the timer to 0 when the user touches the label. This is a simple way to make a label into a button BTW.
Set up touchesBegan:
to toggle the flag
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { /* Called when a touch begins */ for (UITouch *touch in touches) { CGPoint location = [touch locationInNode:self]; if (CGRectContainsPoint(countDown.frame, location)){ startGamePlay = YES; } } }
Then change update:
to the following:
//reset counter if starting if (startGamePlay){ startTime = currentTime; startGamePlay = NO; } countDown.text = [NSString stringWithFormat:"%i", (int)(currentTime-startTime)];
Build and run. Now when we tap the timer, it resets to 0.
We are still counting up. Let’s count down from 5. Modify update this way:
int countDownInt = 5.0 -(int)(currentTime-startTime); countDown.text = [NSString stringWithFormat:@"%i", countDownInt];
Build and run. That will give us negative numbers, which we don’t want. Modify update:
again to only show positive numbers. When we get a negative, stop counting.
int countDownInt = 5.0 -(int)(currentTime-startTime); if(countDownInt>0){ //if counting down to 0 show counter countDown.text = [NSString stringWithFormat:@"%i", countDownInt]; }else { //if not show message, dismiss, whatever you need to do. countDown.text=@"GO!"; }
And that works!! I then place this code into SlippyFlippy penguin. I made a few adjustments to fit into SlippyFlippy. I set the timer to three seconds, and aligned the timer and flash labels a bit better. It works great — with two exceptions.
The flash labels are slightly annoying and clunky looking when they show up. I Need to set timing better on when to show them, and I need a method to remove them from the screen if we get a game over status while they are still showing.
Then I try to test the hard mode. The bug which I couldn’t find is now very front and center. If a user taps the screen after hitting the hard mode but before the penguin appears, the game ends. Next time, we begin our search and destroy mission for The Bug.
The playbox code for the countdown timer:
// // SLMyScene.m // CountdownTimer // // Created by Steven Lipton on 4/8/14. // Copyright (c) 2014 Steven Lipton. All rights reserved. // #import "SLMyScene.h" @implementation SLMyScene{ SKLabelNode *countDown; BOOL startGamePlay; NSTimeInterval startTime; } -(id)initWithSize:(CGSize)size { if (self = [super initWithSize:size]) { /* Setup your scene here */ //Make a label for the timer self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0]; countDown = [SKLabelNode labelNodeWithFontNamed:@"Futura-Medium"]; countDown.fontSize = 50; countDown.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame)*0.85); countDown.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter; //good for positioning with other sprites countDown.fontColor = [SKColor whiteColor ]; countDown.name = @"countDown"; countDown.zPosition = 100; [self addChild:countDown]; } return self; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { /* Called when a touch begins */ for (UITouch *touch in touches) { CGPoint location = [touch locationInNode:self]; if (CGRectContainsPoint(countDown.frame, location)){ startGamePlay = YES; } } } -(void)update:(CFTimeInterval)currentTime { /* Called before each frame is rendered */ /*code for beginning animation before game play ,and the start game */ //reset counter if starting if (startGamePlay){ startTime = currentTime; startGamePlay = NO; } int countDownInt = (int)(currentTime-startTime); if(countDownInt>0){ //if counting down to 0 show counter countDown.text = [NSString stringWithFormat:@"%i", countDownInt]; }else { //if not show message, dismiss, whatever you need to do. countDown.text=@"GO!"; /* code here runs every frame -- Try not to put animations here, unless you can fire them once only or fire them through good timing. */ } } @end
Leave a Reply