SlippyFlippy 1.1: Adding a Fading-in and out Label with Background in SpriteKit

icon2_120Today 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. 

We end up with this: iOS Simulator Screen shot Apr 1, 2014, 7.39.46 PM

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.

iOS Simulator Screen shot Apr 1, 2014, 7.38.04 PMThis 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];
     }];
}

iOS Simulator Screen shot Apr 1, 2014, 7.38.13 PMThat 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

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s