Today we have a bug to deal with in SlippyFlippyPenguin. There are a two reports of when a user selects the hard skill level the game immediately goes to game over. I cannot find anything that is different in the code to account for this except user error. I think the user error is users not realizing how fast they have to take controls in the hard mode. The penguin drops to the bottom, and ends the game before they can react. I’ve had more reports from a lot other users that is a user problem. It is the speed that flippy drops after pressing the button that gets them. The game is not clear about Slippy touching the sides or about what you are to do — and all of that happens fast.
So here is what I’m going to do.
- Add directions as messages that appear and disappear.
- Add a countdown timer before starting
I’ll implement this in two stages. Lets’ start with placing a message on the screen. What we need to do is flash a string on the screen, then make it disappear.
-(void)flashMessage:(NSString *)message atPosition:(CGPoint)position duration:(NSTimeInterval)duration{ //a method to make a sprite for a flash message at a certain position on the screen //to be used for instructions //make a label that is invisible SKLabelNode *flashLabel = [SKLabelNode labelNodeWithFontNamed:@"MarkerFelt-Wide"]; flashLabel.position = position; flashLabel.fontSize = 17; flashLabel.fontColor = [SKColor blackColor]; flashLabel.text = message; flashLabel.alpha =0; flashLabel.zPosition = 100; [self addChild:flashLabel]; //make an animation sequence to flash in and out the label SKAction *flashAction = [SKAction sequence:@[ [SKAction fadeInWithDuration:duration/3.0], [SKAction waitForDuration:duration], [SKAction fadeOutWithDuration:duration/3.0] ]]; // run the sequence then delete the label [flashLabel runAction:flashAction completion:^{[flashLabel removeFromParent];}]; }
Pretty straightforward code, and the comments make clear each of our steps:
- Make a label node
- Set its properties, and make it invisible
- Make an action that fades in the label, shows it, then fades it out
- Run the action, then remove the sprite.
Couple of items to note here: it’s important to set the z-height to 100, so the messages are not obscured by anything in the game. A new method for me was runAction:completion:
which here immediately removes the node when the animation is done. I think this will be a good practice, and will be a modification to what I actually did in the SlippyFlippy challenge when I write about it shortly.
We implement that method in the touchesBegan: code when we select our difficulty level;
else if (CGRectContainsPoint(hardRect, location)){ // NSLog(@" Hard TOUCH!!"); [self flashMessage:@"Avoid the sides" atPosition:CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame)*0.1) duration:3]; [self flashMessage:@"Avoid the sides" atPosition:CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame)*0.8) duration:3]; [self flashMessage:@"Tap the screen to move Penguin" atPosition:CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame)*0.65) duration:1]; [myLabel removeFromParent]; [[self childNodeWithName:@"easy button"] removeFromParent]; [[self childNodeWithName:@"hard button"] removeFromParent]; Notice I keep everything a relative position, a percentage of the full screen. This helps with the screen size fragmentation problem, and keeps everything proportional.
This is good, but it needs a background to stand out more. So I made two more nodes: a SKNode
as a container, and a SKSpriteNode
as a background. I’ll put the label and the Sprite into the node and then animate the node. Note I had to set the label’s verticalAlignmentMode to center from its default baseline alignment. Otherwise our positioning is on the baseline of the text, and fails to align with the background.
-(void)flashMessage:(NSString *)message atPosition:(CGPoint)position duration:(NSTimeInterval)duration{ //a method to make a sprite for a flash message at a certain position on the screen //to be used for instructions //make a label SKLabelNode *flashLabel = [SKLabelNode labelNodeWithFontNamed:@"MarkerFelt-Wide"]; flashLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter; flashLabel.fontSize = 17; flashLabel.fontColor = [SKColor whiteColor]; flashLabel.text = message; //make a background based on the size of the label CGSize mySize = flashLabel.frame.size; mySize.height *=1.5; mySize.width *= 1.2; SKSpriteNode *background = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:mySize]; //make a container node SKNode *flashLabelNode = [SKNode node]; flashLabelNode.position = position; flashLabelNode.zPosition = 100; flashLabelNode.alpha = 0; flashLabelNode.name = @"flashLabelNode"; //add the node tree together [flashLabelNode addChild:background]; [flashLabelNode addChild:flashLabel]; [self addChild:flashLabelNode]; //make an animation sequence to flash in and out the label SKAction *flashAction = [SKAction sequence:@[ [SKAction fadeInWithDuration:duration/3.0], [SKAction waitForDuration:duration], [SKAction fadeOutWithDuration:duration/3.0] ]]; // run the sequence then delete the label [flashLabelNode runAction:flashAction completion:^{[flashLabelNode removeFromParent];}]; }
So our steps here are:
- Make the
SKLabelNode
- Make a background slightly bigger than the label’s size with a
SKSpriteNode
. - Make a
SKNode
as a container, then add the label and sprite to the node. - Make the action, though this time directed at the
SKNode
instead of the SKLabelNode - Perform the action, then remove the node.
This works but it leaves a bit of a problem. I don’t get rid of the labels when I need to. In a game over situation, the labels are still there, but I don’t want them there. I need to remove the labels completely at a game over state.
Lets remove all flash labels in one purge using the enumerateChildNodesWIthname:usingBlock:
method
-(void)removeAllFlashMessages{ [self enumerateChildNodesWithName:@"flashLabelNode" usingBlock:^(SKNode *node, BOOL *stop) { [node removeAllActions]; [node removeFromParent]; }]; }
That works great. I might want to identify the labels so I can get rid of them at events, but I’ll do that later.
Next time, we will do the countdown timer.
Leave a Reply