Cocos2d for iPhone: Adding Layers and Making a Simple Pause Screen

Exclusive offer: get 50% off this eBook here
Cocos2d for iPhone 0.99 Beginner's Guide

Cocos2d for iPhone 0.99 Beginner's Guide — Save 50%

Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!

$23.99    $12.00
by Pablo Ruiz | December 2010 | Open Source

Cocos2d for iPhone is a framework for building 2D games, applications, presentations, demos, and more. It was originally made for Python and then ported to IPhone by Ricardo Quesada as an open source project with the MIT license.

In the previous articles by Pablo Ruiz, author of the book Cocos2d for iPhone 0.99 Beginner's Guide, we saw how to create new scenes and move through them, and build the base for an action game and how to handle accelerometer input and detect collisions.

In this article, we will see how to add more layers to your scenes and make a simple pause screen.

 

Cocos2d for iPhone 0.99 Beginner's Guide

Cocos2d for iPhone 0.99 Beginner's Guide

Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!

  • A cool guide to learning cocos2d with iPhone to get you into the iPhone game industry quickly
  • Learn all the aspects of cocos2d while building three different games
  • Add a lot of trendy features such as particles and tilemaps to your games to captivate your players
  • Full of illustrations, diagrams, and tips for building iPhone games, with clear step-by-step instructions and practical examples
        Read more about this book      

(For more resources on Cocos2d, see here.)

Adding more layers to scenes

As we have discussed before, a scene can contain any number of layers you want. Well, as long as performance is not an issue. As CCLayer inherits from CCNode, it can be added as the child of CCScenes or other CCLayers, allowing you to organize your content in a nice fashion.

There are three types of CCLayers that you can use and combine in your games. They are as follows:

  • CCLayer: We have been using them forever. Besides all the features inherited from CCNodes, they can receive touches and accelerometer input.
  • CCColorLayer: They inherit from CCLayer so besides being able to receive touches and accelerometer input, their opacity and RGB colors can be changed.
  • CCMultiplexLayer: It inherits from CCLayer and can have many children, but only one will be active at any given time. You can switch between those children.

In the following examples, we will be creating some CCLayers in different ways to achieve different results.

Time for action – creating a HUD to display lives and the score

We will begin by building a simple Heads Up Display (HUD) for our game. Its purpose is to show some useful data about the current state of the game to the player. The idea behind making the HUD into a new layer is to simplify the logic of the GameLayer. This way, the GameLayer will only handle stuff of the game itself while leaving the HUD logic to the HUDLayer.

In our game, the HUD will display the remaining lives, score, remaining bombs, and anything you want to display later. Once it is done, all we need to do in the GameLayer is send a message so the HUDLayer gets updated.

  1. The first step in creating the HUD is to add a new file to the project. In the Xcode project, select File | New file and add a new Objective-C class. Rename it as HUDLayer.
  2. Replace the contents of the HudLayer.h with the following lines:

    #import <Foundation/Foundation.h>
    #import "cocos2d.h"
    #import "GameScene.h"

    @interface HudLayer : CCLayer
    {

    CCBitmapFontAtlas * level;
    CCBitmapFontAtlas * score;
    CCBitmapFontAtlas * bombs;
    NSMutableArray * lives;
    }

    @property (nonatomic,retain) CCBitmapFontAtlas * level;
    @property (nonatomic,retain) CCBitmapFontAtlas * score;
    @property (nonatomic,retain) CCBitmapFontAtlas * bombs;
    @property (nonatomic,retain) NSMutableArray * lives;

    @end

  3. Do the same with the contents of the HudLayer.m file:

    #import "HudLayer.h"

    @implementation HudLayer

    @synthesize lives,bombs,score,level;

    - (id) init
    {
    if ((self = [super init])) {

    CCSprite * background = [CCSprite spriteWithFile:@"hud_background.png"];
    [background setPosition:ccp(160,455)];
    [self addChild:background];

    lives = [[NSMutableArray arrayWithCapacity:3]retain];
    for(int i=0;i<3;i++)
    {
    CCSprite * life = [CCSprite spriteWithFile:@"hud_life.png"];
    [life setPosition:ccp(18+ 28*i,465)];
    [self addChild:life];
    [lives addObject:life];
    }

    CCSprite * bomb = [CCSprite spriteWithFile:@"hud_bomb.png"];
    [bomb setPosition:ccp(18,445)];
    [self addChild:bomb];

    GameLayer * gl = (GameLayer *)[self.parent getChildByTag:KGameLayer];

    level = [CCBitmapFontAtlas bitmapFontAtlasWithString:@"Level 1"
    fntFile:@"hud_font.fnt"];
    [level setAnchorPoint:ccp(1,0.5)];
    [level setPosition:ccp(310,465)];
    [level setColor:ccBLACK];
    [self addChild:level];

    score = [CCBitmapFontAtlas bitmapFontAtlasWithString:@"Score 0"
    fntFile:@"hud_font.fnt"];
    [score setAnchorPoint:ccp(1,0.5)];
    [score setPosition:ccp(310,445)];
    [score setColor:ccBLACK];
    [self addChild:score];

    bombs = [CCBitmapFontAtlas bitmapFontAtlasWithString:@"X3"
    fntFile:@"hud_font.fnt"];
    [bombs setAnchorPoint:ccp(1,0.5)];
    [bombs setPosition:ccp(47,440)];
    [bombs setColor:ccBLACK];
    [self addChild:bombs];

    }
    return self;
    }

    - (void) dealloc
    {
    [super dealloc];
    [lives release];
    }
    @end

    You can find the images and the font used above in the companion files at Support.

  4. What we have to do now is load this new layer and add it as a child of the GameScene. Change the init method of the GameScene class to look like the following:

    - (id) init
    {
    self = [super init];
    if (self != nil)
    {

    [self addChild:[GameLayer node] z:0 tag:KGameLayer];
    //kGameLayer defined in the GameScene.h file.
    #define kGameLayer 1
    [self addChild:[HudLayer node] z:1 tag:KHudLayer];
    //kHudLayer defined in the GameScene.h file. #define
    kHudLayer 2

    }
    return self;
    }

    The only thing missing now is to make some changes here and there to be able to update the HUDLayer with the actual state of the game. Let's update the score and remaining lives, for now.

  5. Change the loseLife method of the GameLayer class:

    -(void)loseLife
    {
    self.lives--;

    HudLayer * hl = (HudLayer *)[self.parent getChildByTag:KHudLayer];
    CCSprite * live = [hl.lives objectAtIndex:self.lives];
    [live setVisible:NO];

    if(self.lives ==0)
    {
    [self resetGame];

    //LOSE THE GAME
    //GO TO GAME OVER LAYER
    }
    }

  6. Add the resetGame method:

    -(void)resetGame
    {
    HudLayer * hl = (HudLayer *)[self.parent getChildByTag:KHudLayer];
    for(CCSprite * c in hl.lives)
    {
    [c setVisible:YES];
    }
    self.level=1;
    [hl.level setString:@"Level 1"];
    self.score=0;
    [hl.score setString:@"Score 0"];
    self.bombs =3;
    [hl.bombs setString:@"X3"];
    lives = STARTING_LIVES;
    }

    These methods will handle the displaying of the remaining lives.

  7. Finally, modify the Enemy class's destroy method, so it updates the score label instead of logging the score to the console:

    -(void)destroy
    {
    [self reset];
    [theGame setScore:theGame.score+100];
    HudLayer * hl = (HudLayer *)[theGame.parent getChildByTag:KHudLayer];
    [hl.score setString:[NSString stringWithFormat:@"Score
    %d",theGame.score]];
    }

  8. Run the game. You should see a HUD at the top of the screen (as shown in the following screenshot) with all the actual information about the state of the game. Destroy some enemies to see the score updated and lose some lives to see the "lives" icons disappear.

What just happened?

We just went through the steps needed to create a new layer, adding it to the scene and updating its contents.

Our new layer just holds some information of the game state and displays it to the player. In order to achieve that we just added a few CCSprites and CCBitmapFontAtlases which get updated when needed.

Once the HudLayer class was created we added it to the GameScene over the GameLayer, so its contents are always shown on top of the GameLayer's ones. We also provided a tag for both layers, as we will need to access them from other places. We could also have added a reference to the other layer inside them.

That is all what we need to do in order to add more layers to a scene. The rest of the code just handles the updating of the contents of the HudLayer. When the player hits an enemy, a score is awarded. Then the label placed in the HUD is updated.

When the hero is hit and a life is lost, we just turn the corresponding icon's visibility off, then when the game is reset we turn all of them on.

Cocos2d for iPhone 0.99 Beginner's Guide Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!
Published: December 2010
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on Cocos2d, see here.)

Have a go hero – creating a Game Over screen

I will let you put into practise what you have just learnt.

Our game needs a Game Over screen. It should be a new layer that appears when the lives variable reaches 0. Right now we are just resetting the corresponding variables, but a proper way to do this would be as follows:

  • When lives reaches 0, stop updating the game elements, just unschedule the step method.
  • Create and add to the scene a GameoverLayer which could have anything you want to display. Maybe a background image, the final score, and a tap to restart message.
  • When the layer is tapped, remove it from the scene. Call the resetGame method from the GameLayer, and re-schedule the step method.

Once you have that working you can later display high scores, add a menu to go to the main menu screen, or anything else you can think of!

Time for action – creating a pause menu

Now we will see how to create a CCColorLayer and use it to display a pause screen. Along with that I will teach you a way to actually pause the game. So when you press the pause button, or press the lock button, or receive a call, the elements of the game stop moving and then when you wish to, gameplay is resumed.

Actually, the template already created some helpful code for handling the pausing, when the game stops/resumes activity. Check the AerialGunAppDelegate, and you will find the following two methods:

-(void) applicationWillResignActive:(UIApplication *)application
{
[[CCDirector sharedDirector] pause];
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[CCDirector sharedDirector] resume];
}

If you run the game and press the lock button, then unlock the screen, your game keeps playing. Although execution was really paused while you left, the game is resumed abruptly. So we should have the game still paused when you unlock the screen and give a chance to the player to resume it when he is ready.

Pausing the CCDirector stops all movement in your game and sets the fps to four, so very little battery is consumed. The drawback of doing this is that you are not able to show any custom screen while you are in this state. If you want to just call the CCDirector's pause method, you should show a UIAlert, for example, with a resume button that calls the CCDirector's resume method when touched. That's easy, but looks pretty awful for a game you spent so much time designing a nice coherent UI for.

 

Don't worry; there is another way to solve this! Let me guide you through the process of making a custom pause screen now.

  1. First, create the PauseLayer class, which should inherit from CCColorLayer. The following is the PauseLayer.h contents:

    #import <Foundation/Foundation.h>
    #import "cocos2d.h"
    #import "GameScene.h"

    @interface PauseLayer : CCColorLayer {

    }

    @end

  2. Now replace the contents of the PauseLayer.m with the following lines:

    #import "PauseLayer.h"

    @implementation PauseLayer


    - (id) initWithColor:(ccColor4B)color
    {
    if ((self = [super initWithColor:color]))
    {

    self.isTouchEnabled=YES;

    CCSprite * paused = [CCSprite spriteWithFile:@"paused.png"];
    [paused setPosition:ccp(160,240)];
    [self addChild:paused];

    }
    return self;
    }



    - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    for( UITouch *touch in touches )
    {
    CGPoint location = [touch locationInView: [touch view]];

    location = [[CCDirector sharedDirector] convertToGL: location];

    GameLayer * gl = (GameLayer *)[self.parent getChildByTag:KGameLayer];
    [gl resume];
    [self.parent removeChild:self cleanup:YES];
    }
    }

    - (void) dealloc
    {
    [super dealloc];
    }

    @end

    Remember to add the paused.png image to the project, which can be found in this article's companion files.

  3. Let's first handle the manual pausing. For now, we will pause the game when the player touches the HUD bar, at the top. Add the ccTouchesBegan method to the HudLayer to handle that, as follows:

    - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    GameLayer * gl = (GameLayer *)[self.parent getChildByTag:KGameLayer];

    for( UITouch *touch in touches )
    {
    CGPoint location = [touch locationInView: [touch view]];

    location = [[CCDirector sharedDirector] convertToGL: location];

    if(location.y>400)
    {
    [gl pauseGame];
    }
    }
    }

    From now on, when you touch over there, the GameLayer's pauseGame method will be called. Let's check what that method does.

  4. Add the following methods to the GameLayer class:

    -(void)pauseGame
    {
    ccColor4B c = {100,100,0,100};
    PauseLayer * p = [[[PauseLayer alloc]initWithColor:c]autorelease];
    [self.parent addChild:p z:10];
    [self onExit];
    }
    The preceding method will be called when we decide to pause the
    game. It will create a new PauseLayer, add it to the GameScene,
    and place it on top of the GameLayer.
    - (void) resume
    {
    if(![AerialGunAppDelegate get].paused)
    {
    return;
    }
    [AerialGunAppDelegate get].paused =NO;
    [self onEnter];
    }
    The Resume method will be called when the player dismisses the PauseLayer.
    - (void) onExit
    {
    if(![AerialGunAppDelegate get].paused)
    {
    [AerialGunAppDelegate get].paused = YES;
    [super onExit];
    }
    }

    - (void) onEnter
    {
    if(![AerialGunAppDelegate get].paused )
    {
    [super onEnter];

    These are the two methods that do the magic of stopping and resuming the execution of that layer and its contents.

  5. As you can see, we are calling a method on the AppDelegate and retrieving one of its variables, so we need to add those to the AerialGunAppDelegate:

    +(AerialGunAppDelegate *) get
    {

    return (AerialGunAppDelegate *) [[UIApplication sharedApplication]
    delegate];
    }

    That method will allow you to reach the AppDelegate from anywhere. We can use that to hold some "global" variables, such as whether the game is paused, or sounds that should be loaded and be available from the beginning of the execution.

  6. Add the paused Bool variable to the AerialGunAppDelegate class.
  7. Run the game now and touch the top portion of the screen. You should see a yellowish layer appear on top and the game getting paused below. When you want to resume gameplay, just touch the screen again. This is shown in the following screenshot:

What just happened?

Well, we just did a lot of things. Let's go over them one at a time, starting with the actual PauseLayer code.

Take a look at the PauseLayer.h file. It has just a few lines of code, but there is a difference there when compared to the previous layers we have made. The difference is that it inherits from CCColorLayer instead.

CCColorLayer allows you to set a color and opacity to it, so it is ideal for overlaying it on another layer. You could use it to show some kind of modal alert, hints, and varied information. Also, doing this takes practically no memory compared to using a full screen background image.

The code inside this new class should not be new to you. It just overrides the CCColorLayer's initWithColor method, so when initialized an image is placed in the middle. Then the ccTouchesBegan makes the layer be removed from its parent when touched, and calls the GameLayer's resumeGame method, which we will analyze in a moment.

The pauseGame method instantiates a new PauseLayer object with a color of our liking. The interesting part here is the call to the layer's onExit method, which we are overriding below. So if the game is not already paused, we pause it and call the onExit method of the layer's super class.

If you remember well, onEnter and onExit methods are called when a CCNode enters and leaves the stage respectively. Well, here we are calling them directly when pausing and resuming the game. Why do that? We do that because when a CCLayer's onExit method is called, it stops the layer from receiving touches and accelerometer events. Also its super class's onExit is called (CCNode), which in turn deactivates timers of that layer and its children.

When we call the resumeGame method we do the opposite, which is calling the layer's onEnter method.

If you receive a call or lock the screen and then return to the game, you will notice that it is not working as expected; we are still resuming the game instantly without letting the player get ready. Let's solve that little issue.

Time for action – pausing the game while inactive

In the previous part, we created a pause layer and had it shown when you touched a part of the screen. We now have to have it shown when the game stops playing by an external event like a call. We have almost everything done, so this should be quick to implement. We just have to work a little with the AppDelegate.

In order to make the game be still paused after returning from a call, just replace the contents of the applicationWillResignActive method with the following:

-(void) applicationWillResignActive:(UIApplication *)application
{
CCScene * current = [[CCDirector sharedDirector] runningScene];
if([current isKindOfClass:[GameScene class]])
{
if(![AerialGunAppDelegate get].paused)
{
GameLayer * layer = (GameLayer *)[current getChildByTag:KGameLayer];
[layer pauseGame];
}
}
[[CCDirector sharedDirector] pause];

}

That's all! Run the game now, press the lock button, unlock the screen, and you should see the same PauseLayer as when you touched the screen.

What just happened?

The preceding code calls the layer's pauseGame when the application is about to stop being active, just as we did when we paused the game by touching the screen.

There is a little noticeable difference here that is we are using the CDirector's runningScene method that gives us the scene that is currently running. Why do we do this? For starters, we need to know which scene is running, so we call the pauseGame method on the correct layer.

For this game, it makes no sense adding the PauseLayer in the main menu or splash screen. We just need to show it during the actual gameplay, so, that is why before doing all that we check whether we are running the GameScene.

One more thing; just as I placed a single image on the PauseLayer, you can do whatever you want. You are free to add menus, run actions on the PauseLayer's elements, and so on.

Summary

This article is officially over! We really made a lot of advancements here, as follows:

  • We used several layers in a single scene
  • You learnt how to pause your game and show a custom screen

Further resources on this subject:


Cocos2d for iPhone 0.99 Beginner's Guide Make mind-blowing 2D games for iPhone with this fast, flexible, and easy-to-use framework!
Published: December 2010
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Pablo Ruiz

Pablo Ruiz started his carreer as a developer working as PHP developer for a local government office in 2007. Before that, he would work in QA for a big multinational game development company.

While working as a PHP developer he would build big administrative and accounting systems which were used across all the offices for their daily work.

In 2008 he began working on some small C# games using the XNA framework in his spare time. At the time, the iPhone was gaining ground so he began working on a personal project, a little game called Aqua Rush. After releasing it and starting doing consultancy work for several companies he left his day job and began working as a freelance iPhone developer.

Half a year later, he founded, with his partner, their own company; Infinix Mobile Software Development in Argentina, where he employs and trains students to learn and master the art of programming.

Books From Packt

Drupal 7 Module Development
Drupal 7 Module Development

Panda3D 1.6 Game Engine Beginner's Guide
Panda3D 1.6 Game Engine Beginner's Guide

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

Blender 2.5 Materials and Textures     Cookbook: RAW
Blender 2.5 Materials and Textures Cookbook: RAW

Inkscape 0.48 Essentials for Web Designers
Inkscape 0.48 Essentials for Web Designers

3D Graphics with XNA Game Studio 4.0
3D Graphics with XNA Game Studio 4.0

Learning Ext JS 3.2
Learning Ext JS 3.2

PHP jQuery Cookbook
PHP jQuery Cookbook

No votes yet
Updating Score by
Starting from the top of this page, go to item 7. It's in the -(void)destroy method.
Updating Score by
I may be blind and not see it, but I can't seem to find the part where you update the score. I see the part where you change the number of lives, but haven't been able to find the updateScore section.

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
y
A
q
F
z
M
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software