Cocos2d for iPhone: Surfing Through Scenes

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.

Scenes are an essential part of any Cocos2d game. In this article by Pablo Ruiz, author of the book Cocos2d for iPhone 0.99 Beginner's Guide, we will look into the creation of many scenes to hold the different menus a game can have. Once we have a couple of scenes set up, you will learn how to go from one to another and with some nice transitions.

 

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

We'll be doing a lot of things in this article, so let's get started.

Aerial Gun, a vertical shooter game

Let's talk about the game we will be making.

Aerial Gun, as I said earlier, is a vertical shooter game. That means you will be in control of an airship which will move vertically. Actually, the airship won't be moving anywhere (well except to the left and right); what is really going to move here is the background, giving the sense the airship is the one moving.

The player will be able to control the airship by using accelerometer controls. We'll also provide some areas where the player can touch to fire bullets or bombs to destroy the enemies.

Enemies will be appearing at different time intervals and will have some different behaviors attached to them. For example, some of them could just move towards the airship, others may stay there and shoot back, and so on. We'll do it in a way you can create more custom behaviors later.

For this game we will be using the Cocos2d template again, just because it is the easier way to have a project properly set up, at least to begin our work.

So, follow the same steps as when we created the Coloured Stones game. Just open Xcode and go to the File menu, select to create a new project. Then choose the Cocos2d template (the simple one, without any physics engine) and give a name to the project. I will call it AerialGun. Once you do that, your new project should be opened.

Creating new scenes

We will begin this new game by first doing a couple of scenes other than the one that holds the actual game.

If you take a look at the class that the template generates, at first sight you won't find what would appear to be a scene. That is because the template handles that in a confusing fashion, at least for people who are just getting started with Cocos2d. Let's take a look at that. Open the newly generated HelloWorldScene.m file.

This is the same code in which we based our first game. This time we will analyze it so you understand what it is doing behind scenes. Take a look at the first method of the implementation of the HelloWorld Layer:

+(id) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];

// 'layer' is an autorelease object.
HelloWorld *layer = [HelloWorld node];

// add layer as a child to scene
[scene addChild: layer];

// return the scene
return scene;
}

If you remember well, this is the method that get's called in the AppDelegate when the Director needs to start running with a scene. So why are we passing the method of a layer instead of an instance of a CCScene class?

If you pay attention to the +(id)scene method of the layer, what it does is to instantiate a CCScene object, then it instantiates a HelloWorld object (the layer), and then it adds that layer to the scene and returns it.

This actually works as you have witnessed and is pretty quick to set up, and get you up and running. However, sometimes you will need to have you own custom scenes, that is a class that inherits from CCScene and extends it in some fashion.

Now we are going to create some of the scenes that we need for this game and add a layer to each one. Then when it is time to work with the game scene we will modify it to match our needs.

Before doing anything, please change the name of the HelloWorldlayer to GameLayer and the name of the HelloWorldScene to GameScene, just as we did back then with our first game.

Time for action – creating the splash and main menu scene

We will begin with a simple scene, the splash screen. A splash screen is the first thing that appears as you launch a game. Well, the second if you count the Default.png image. The objective of this screen is to show the player some information about the developer, other companies that helped, and so on. Most times you will just show a couple and logos and move to the main menu.

So, what we will do now is put a new Default.png image, then create the SplashScene.

The Default.png image is located in your project's Resources group folder and it is the first image that is shown when the application is launched. You should always have one, so that something is shown while the application is being launched. This happens before anything is initialized, so you can't do anything else other than show this image. Its dimensions must be 320 * 480 (when developing for older generation iPhones), so if your game is supposed to be in landscape mode, you should rotate the image with any graphics software before including it in your project.

The SplashScene is going to show a sprite for a moment, then fade it, show another one and then move to the GameScene.

Let's add a new file to the project. Select New File (CMD + N) from the File menu, then select Objective-C class (we are going to do it from scratch anyways) and name it SplashScene.

This file will hold both the SplashScene and the SplashLayer classes. The SplashLayer will be added as a child of the SplashScene, and inside the layer we will add the sprites.

Before writing any code, add to the project the three splash images I created for you. You can find them in the companion files folder. Once you have them added into your project we can begin working on the SplashScene.

The following is the SplashScene.h:

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

@interface SplashScene : CCScene {

}

@end


@interface SplashLayer : CCLayer {

}

@end

And the SplashScene.m file:

#import "SplashScene.h"

//Here is the implementation of the SplashScene

@implementation SplashScene

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

[self addChild:[SplashLayer node]];

}
return self;
}

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

@end

//And here is the implementation of the SplashLayer


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

isTouchEnabled = YES;

NSMutableArray * splashImages = [[NSMutableArray alloc]init];
for(int i =1;i<=3;i++)
{
CCSprite * splashImage = [CCSprite spriteWithFile:[NSString
stringWithFormat:@"splash%d.png",i]];
[splashImage setPosition:ccp(240,160)];
[self addChild:splashImage];
if(i!=1)
[splashImage setOpacity:0];

[splashImages addObject:splashImage];
}

[self fadeAndShow:splashImages];


}
return self;
}

//Now we add the methods that handle the image switching


-(void)fadeAndShow:(NSMutableArray *)images
{
if([images count]<=1)
{
[images release];
[[CCDirector sharedDirector]replaceScene:[MainMenuScene node]];
}
else
{

CCSprite * actual = (CCSprite *)[images objectAtIndex:0];
[images removeObjectAtIndex:0];

CCSprite * next = (CCSprite *)[images objectAtIndex:0];


[actual runAction:[CCSequence actions:[CCDelayTime actionWithDuration:2],
[CCFadeOut actionWithDuration:1],[CCCallFuncN actionWithTarget:self
selector:@selector(remove:)],nil]];
[next runAction:[CCSequence actions:[CCDelayTime actionWithDuration:2],
[CCFadeIn actionWithDuration:1],[CCDelayTime actionWithDuration:2],
[CCCallFuncND actionWithTarget:self selector:@selector(cFadeAndShow:
data:) data:images],nil]];

}


}

-(void) cFadeAndShow:(id)sender data:(void*)data
{
NSMutableArray * images = (NSMutableArray *)data;
[self fadeAndShow:images];
}

-(void)remove:(CCSprite *)s
{
[s.parent removeChild:s cleanup:YES];
}

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

@end

As you may notice, I have put together two classes in only one file. You can do this with any number of classes, as long you don't get lost in such a long file. Generally, you will create a pair of files (the .m and .h) for each class, but as the SplashScene class has very little code, I'd rather put it there.

For this to work properly, we need to make some other changes. First, open the AerialGunAppDelegate.m file and change the line where the the Director starts running the GameScene. We want it to start by running the SplashScene now. So replace that line with the following:

[[CCDirector sharedDirector] runWithScene:[SplashScene node]];

Also remember to import your SplashScene.h file in order for the project to properly compile.

Finally, we have to create the MainMenuScene, which is the scene the Director will run after the last image has faded out. So, let's create that one and leave it blank for now.

The following is the MainMenuScene.h file:

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

@interface MainMenuScene : CCScene {

}

@end


@interface MainMenuLayer : CCLayer {

}

@end

The following is the MainMenuScene.m file:

#import "MainMenuScene.h"


@implementation MainMenuScene

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

[self addChild:[MainMenuLayer node]];

}
return self;
}

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

@end

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

isTouchEnabled = YES;

}
return self;
}

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

That would be all for now. Run the project and you should see the first image appear. Then fade to the next one after a while and continue till the last one. Once the last one has faded out, we move on to the MainMenuScene.

What just happened?

Creating a new scene is as easy as that. You can see from the MainMenuScene code that what we are doing is quite simple; when the MainMenuScene gets created we just instantiate the MainMenuLayer and add it to the scene. That layer is where we will later add all the necessary logic for the main menu.

The MainMenuScene as well as the SplashScene both inherit from the CCScene class. This class is just another CCNode.

Let's take a little look at the logic behind the SplashScene:

NSMutableArray * splashImages = [[NSMutableArray alloc]init];
for(int i =1;i<=3;i++)
{
CCSprite * splashImage = [CCSprite spriteWithFile:[NSString
stringWithFormat:@"splash%d.png",i]];
[splashImage setPosition:ccp(240,160)];
[self addChild:splashImage];
if(i!=1)
[splashImage setOpacity:0];

[splashImages addObject:splashImage];
}

[self fadeAndShow:splashImages];

This piece of code is from the SplashLayer init method. What we are doing here is creating an array that will hold any amount of sprites (in this case, three of them). Then we create and add those sprites to it. Finally, the fadeAndShow method is called, passing that newly created array to it.

The fadeAndShow method is responsible for fading the images it has. It grabs the first image in the array (after that, the sprite is removed from the array). It then grabs the next one. Then it applies actions to both of them to fade them in and out.

The last action of the second sprite's action is a CCCallFuncND, which we use to call the fadeAndShow method again with the modified array. This occurs if there is more than one remaining sprite in the array. If there isn't, the fadeAndShow method calls the Director's replaceScene method with the MainMenuScene.

I have made the SplashLayer logic, so you can add more splash images to the array (or leave just one) if you like. Generally, just one or two images will be more than enough.

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

Transitioning through scenes

Did you notice how bad it looks when we go from the SplashScene into the MainMenuScene? We need to change the way we move from one scene to another, so the change is not so abrupt. In the previous example, we could have just faded out the last sprite and then replaced the scene to make the transition from one scene to another look smoother. However, what would happen if you had tens of sprites? Or what if you wanted to go from scene to scene by sliding them instead of doing a fade effect?

Fortunately, Cocos2d provides a variety of ways to transition from scene to scene. Cocos2d comes packed with more than 20 different transition classes for you to choose from; and of course, you are welcome to make your own if you find that none of them are suitable for your game style.

Time for action – moving through scenes in a nice way

Let's try a couple of transitions to move from the SplashScene to the MainMenuScene.

Open the SplashScene.m file and in the fadeAndShow method change the line:

[[CCDirector sharedDirector]replaceScene:[MainMenuScene node]];

Change it to the following:

[[CCDirector sharedDirector]replaceScene:[CCFadeTransition
transitionWithDuration:2 scene:[MainMenuScene node]
withColor:ccBLUE]];

Run the project again, and now you should see a nice fade out to blue, and then a nice fade in to the new scene.

Let's try some more interesting transitions. Change the preceding line to the following:

[[CCDirector sharedDirector]replaceScene:[CCZoomFlipAngularTransition
transitionWithDuration:2 scene:[MainMenuScene node] orientation:kOrien
tationLeftOver]];

Run the project one more time and take a look now. Did you like that one?

Cocos2d has lots of transitions that you can try, so take a look at the documentation and the transitions test to see all the options you have.

What just happened?

Transitions are really easy to apply and they look really good, adding a little production value to your game.

Every transition is an individual class that extends the CCTransitionScene class. This class defines a duration and the scene that we want to transit to. So those two parameters will always be needed, then each class may add another extra parameter. For example, the CCFadeTransition takes a color as an extra parameter and the CCZoomFlipAngularTransition takes an orientation.

Implementing the game logic

Now that we have the SplashScene and the MainMenuScene, we can start working on the game. Change the AerialGunAppDelegate to run the GameScene directly, so you can test the game quickly without having to wait for the splash to run each time you want to see the changes made in the game.

Let's begin by making some preparations for the game. Then we will start making each component of the game and then we'll put it all together.

Preparing for the game

AerialGun is a vertical shooter game, so let's begin by changing the orientation of the device. By default, the template sets the orientation to landscape, but we need it to be in portrait mode this time. So, open the AerialGunAppDelegate and search for the following line:

[[CCDirector sharedDirector] setDeviceOrientation:CCDeviceOrientationL
andscapeLeft];

Then, change it to:

[[CCDirector sharedDirector] setDeviceOrientation:CCDeviceOrientation
Portrait];

Good, now if you didn't do this before, take some time to change the HelloWorld files and classes that are created automatically by the template to a more descriptive name. Let's name the game scene GameScene and make the proper changes. Remember, you have to change the file names, the class names, and any imports that were lying around to match the new names. Also remove the label that is created by the template.

Finally, let's create the GameScene class as we did with the other scenes.

The following shows how your GameScene should look:

GameScene.h:

#import "cocos2d.h"

@interface GameScene : CCScene {

}

@end

@interface GameLayer : CCLayer
{
Hero * hero;

NSMutableArray * bullets;
bool playerFiring;

NSMutableArray * enemies;
float lastTimeEnemyLaunched;
float enemyInterval;

int score;
int lives;
int bombs;
int level;
}

@property (nonatomic,retain) Hero * hero;
@property (nonatomic,readwrite) bool playerFiring;
@property (nonatomic,readwrite) float lastTimeEnemyLaunched;
@property (nonatomic,readwrite) float enemyInterval;
@property (nonatomic,retain) NSMutableArray * enemies;
@property (nonatomic,retain) NSMutableArray * bullets;
@property (assign,readwrite) int score;
@property (assign,readwrite) int lives;
@property (assign,readwrite) int level;
@property (assign,readwrite) int bombs;

@end

 

GameScene.m:

#import "GameScene.h"

@implementation GameScene

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

[self addChild:[GameLayer node]];

}
return self;
}

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

@end

@implementation GameLayer

// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init] ))
{


}
return self;
}

// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
[super dealloc];
}
@end

If you run the game you should see a black screen, no more, no less. That is good for now; we have a blank slate to start our work.

Now we are ready to start making the game's components such as the main character, the enemies, and so on.

Let's begin with our hero.

Making a hero

This is the airplane that the user will control. It will be pretty simple for now, and then we will make it more and more complex as we advance through the article. Then, the Hero class should do the following things:

  • Render the image of the airship into the screen
  • Receive accelerometer feedback to update its position
  • Check collisions against enemies and the bullets fired by them
  • Get destroyed

Let's begin by creating the Hero class and getting it to display the airship image on the screen.

Time for action – creating the hero class

We will represent the hero with a single sprite which we'll later move around using the device's accelerometer. The following code is not new at all. We will create a sprite just as we have been doing it all this time:

  1. First, create a new class and add it to the project. Name it Hero.
  2. Now, change the Hero.h file to look like the following:

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

    @class GameLayer;

    @interface Hero : CCNode
    {
    CCSprite * mySprite;
    GameLayer * theGame;
    float lastTimeFired;
    float fireInterval;
    float firingSpeed;
    float movementSpeed;

    }

    @property (nonatomic,retain) CCSprite * mySprite;
    @property (nonatomic,retain) GameLayer * theGame;
    @property (nonatomic,readwrite) float lastTimeFired;
    @property (nonatomic,readwrite) float fireInterval;
    @property (nonatomic,readwrite) float firingSpeed;
    @property (nonatomic,readwrite) float movementSpeed;

    @end

  3. Next, change the Hero.m file as follows:

    #import "Hero.h"

    @implementation Hero

    @synthesize theGame,mySprite;

    - (id) initWithGame:(GameLayer *)game
    {
    self = [super init];
    if (self != nil) {

    self.theGame = game;
    mySprite = [CCSprite spriteWithFile:@"hero.png"];
    [theGame addChild:mySprite z:2];
    [mySprite setPosition:ccp(160,50)];

    self.lastTimeFired =0;
    self.fireInterval = 3;
    self.firingSpeed = 10;
    self.movementSpeed = 5;

    }
    return self;
    }

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

    @end

  4. Finally, create an instance of the Hero class in your GameLayer.

    In your GameLayer interface, add the following ivar:

    Hero * hero;

    In the GameLayer implementation, add the following line to the init method:

    hero = [[Hero alloc] initWithGame:self];

  5. Now, run the game. You should see your airship right there at the bottom of the screen, as shown in the following screenshot:

What just happened?

We just laid the foundations for our Hero class. It does not have much content right now, but as the article progresses we will be adding more stuff to it.

The preceding code just creates a CCSprite object with an image of the airship and places it on the screen. The rest of the properties that we added will be used later for handling the movement and fire rate of the hero.

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

Making yourself some enemies

What would a hero be without enemies?

We are now going to make enemies appear, we will start simple and later work a little more on this. For the time being, we will just have enemies appear one after another in order to get us going. We will be making four types of enemies, which will behave the same way except for their movement speed, their fire rate, and their endurance.

Time for action – throwing enemies at your hero

To make it simple, we will just make the simplest, dumbest enemies; those would be the ones that just move forward no matter what. They are slow and easy to shoot down and some of them will shoot back. For the time being, we will have them appear at regular time intervals. The following are the steps involved in the creation of the Enemy class:

  1. Add a new class to your project named Enemy.
  2. Replace the Enemy.h file's content with the following code:

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

    @interface Enemy : CCNode
    {

    CCSprite * mySprite;
    GameLayer * theGame;
    float lastTimeFired;
    float fireInterval;
    float firingSpeed;
    float movementSpeed;
    bool launched;
    int hp;
    int maxHp;

    }

    @property (nonatomic,retain) CCSprite * mySprite;
    @property (nonatomic,retain) GameLayer * theGame;
    @property (nonatomic,readwrite) float lastTimeFired;
    @property (nonatomic,readwrite) float fireInterval;
    @property (nonatomic,readwrite) float firingSpeed;
    @property (nonatomic,readwrite) float movementSpeed;
    @property (nonatomic,readwrite) bool launched;
    @property (nonatomic,readwrite) int hp;
    @property (nonatomic,readwrite) int maxHp;

    -(CGRect)myRecta;

    @end

  3. Do the same with the Enemy.m file. The following is the code that should go in there:

    #import "Enemy.h"


    @implementation Enemy

    @synthesize theGame,mySprite,lastTimeFired,fireInterval,firingSpeed,movementSpeed,launched,hp,maxHp;

    //The following method will create an enemy airplane with a random configuration

    - (id) initWithGame:(GameLayer *)game
    {
    self = [super init];
    if (self != nil)
    {

    self.theGame = game;
    self.lastTimeFired =0;

    int enType= arc4random() %4 + 1;

    mySprite = [CCSprite spriteWithFile:[NSString
    stringWithFormat:@"enemy%d.png",enType]];
    [theGame addChild:mySprite z:2];
    [mySprite setPosition:ccp(-500,200)];

    switch (enType)
    {
    case 1:
    self.movementSpeed = 5;
    self.fireInterval = -1;
    self.hp = self.maxHp = 1;
    break;
    case 2:
    self.movementSpeed = 3;
    self.fireInterval = 6;
    self.hp = self.maxHp= 1;
    break;
    case 3:
    self.movementSpeed = 1;
    self.fireInterval = 9;
    self.hp = self.maxHp= 2;
    break;
    case 4:
    self.movementSpeed = 1;
    self.fireInterval = 8;
    self.hp = self.maxHp= 2;
    break;
    default:
    self.movementSpeed = 5;
    self.fireInterval = -1;
    self.hp = self.maxHp= 1;
    break;

    }

    self.firingSpeed = -3- self.movementSpeed;

    }
    return self;
    }

    //We'll call the update method each frame, changing the enemy position

    -(void)update
    {
    [self.mySprite
    setPosition:ccp(self.mySprite.position.x,self.mySprite.position.y -
    self.movementSpeed)];

    if(self.mySprite.position.y <-20)
    {
    [self reset];
    }
    }

    -(void)launch
    {
    self.launched = YES;
    [self.mySprite setPosition:ccp(arc4random()% 260 + 30,520)];
    }

    -(void)reset
    {
    self.hp = self.maxHp;
    self.launched =NO;
    [self.mySprite setPosition:ccp(-500,200)];
    }

    @end

    Now we have to make some changes in the GameLayer class.

  4. Add the following properties to the GameLayer interface:

    NSMutableArray * enemies;
    float lastTimeEnemyLaunched;
    float enemyInterval;

  5. Now, add the following lines to the init method of the GameLayer class:

    enemies = [[NSMutableArray alloc]initWithCapacity:10];
    for(int i=0;i<10;i++)
    {
    Enemy * e = [[Enemy alloc]initWithGame:self];
    [enemies addObject:e];
    [e release];
    }

    lastTimeEnemyLaunched =0;
    enemyInterval = 20;
    self.lives = STARTING_LIVES; //#define STARTING_LIVES 3 at the top of the file.
    [self schedule:@selector(step:)];

  6. Finally, create the step method that we are scheduling above:

    -(void)step:(ccTime *)dt
    {
    for(Enemy * e in enemies)
    {
    if(e.launched)
    [e update];
    }

    if(self.lastTimeEnemyLaunched > self.enemyInterval)
    {
    Enemy * n = (Enemy *)[enemies objectAtIndex:arc4random() % [enemies
    count]];
    if(!n.launched)
    {
    [n launch];
    self.lastTimeEnemyLaunched=0;
    }
    }

    lastTimeEnemyLaunched +=0.1;
    }

  7. That is all. Now run the game, and you will be able to see enemies coming down at regular time intervals, as shown in the following screenshot:

What just happened?

We began by creating the Enemy class, which at first sight is similar to the Hero class. As a matter of fact, we could have made a general Airship class and have both of them inherit from that, but let's keep it simple for now.

So, the Enemy class has a couple more properties to handle its hp. That is the amount of bullets required to take that airship down.

In the init method of this class, we are defining the behavior of each different type of enemy. They will be initialized at random and each will have different speeds, hp, and fire rates. Notice this could also be broken up into several subclasses if you wanted.

The rest of the properties are the same that we placed in our Hero class. The one we are using for now is the movementSpeed, which, as the name suggests, controls the speed of the enemies. Try changing it if you'd like to have them fly faster or slower.

The Enemy class has a reset and a launch method. These methods control the position of the enemies when they are to be removed and placed on the screen.

We also added an update method to it, which will be called from the GameLayer constantly, that is as fast as the game is able to do it, while the Enemy object is launched. What it does is update the position of the enemy's sprite.

The logic that makes the enemies appear at regular intervals is in the GameLayer class. First we allocate a reasonable amount of enemies in the init method and add them to an array. There we also set two important variables to control the apparition interval: lastTimeEnemyLaunched and enemyInterval, which handle the time separation between one enemy and the next one. You can change that number to make them appear more or less regularly.

The step method is the method that does the magic; this method will be used a lot, not just for updating the enemies but also for the hero, the bullets, checking collisions, and so on.

Right now, what we do is loop through the enemies array and check whether they have launched. If they are, we call the update method, which makes the active planes move downwards. If we have not launched any enemy in a while, we activate one of them.

That was pretty simple but it is more than enough for us to continue working with the rest of the elements.

Have a go hero – enhancing the enemies behavior

The AI for the enemies is really simple as you can see. There are a lot of things you could do to improve that. For example, instead of having the enemies come at you one by one at regular time intervals, you could create a system that allows you to send wave after wave of them. You could customize the types of enemies that come in each wave as well.

One way to do that would be something like the following:

  1. Create a .plist file. This file would hold an array of "waves". Each wave would contain an array of enemies, which would be an NSDictionary.
  2. That NSDictionary would hold the types of enemies you want to send and their positions. For example, wave1 could be made of five fast enemies.
  3. Now in the GameLayer, you would have to initialize an array which will hold one of the wave arrays at a time.
  4. When the game starts, load the array with one of the arrays in the .plist.
  5. When the last of those enemies is off screen, load the array again and send those enemies.

Forging some bullets

Our hero and enemies need something to defend themselves. Let's create some bullets for them to shoot!

We'll do something similar to what we did with enemies; we have to create a pool of bullets which we will use and reuse at the request of an airship requests.

Time for action – creating and reusing bullets

The logic behind the Bullet class is quite similar to that of the Enemy class. We'll create a bunch of bullets outside of the screen, and use them when needed. If they are activated, that is on screen, we update their position depending on who fired them. Let's do that and have our enemies fire first.

  1. The following should go in the Bullet.h file:

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

    @class GameLayer;

    @interface Bullet : CCNode
    {
    CCSprite * mySprite;
    GameLayer * theGame;
    bool fired;
    int whoFired;
    int firingSpeed;
    }

    @property (nonatomic,retain) CCSprite * mySprite;
    @property (nonatomic,retain) GameLayer * theGame;
    @property (nonatomic,readwrite) bool fired;
    @property (nonatomic,readwrite) int whoFired;
    @property (nonatomic,readwrite) int firingSpeed;


    -(void)fire:(int)who position:(CGPoint)position fspeed:(int)fspeed;
    @end

  2. The following should go in the Bullet.m file:

    #import "Bullet.h"

    @implementation Bullet

    @synthesize theGame,mySprite,fired,whoFired,firingSpeed;

    - (id) initWithGame:(GameLayer *)game
    {
    self = [super init];
    if (self != nil)
    {

    self.theGame = game;
    mySprite = [CCSprite spriteWithFile:@"bullet1.png"];
    [theGame addChild:mySprite z:1];
    [mySprite setPosition:ccp(-100,-100)];
    [mySprite setVisible:NO];

    }
    return self;
    }


    -(void)update
    {
    switch (self.whoFired)
    {
    case 1:
    [self.mySprite
    setPosition:ccp(self.mySprite.position.x,self.mySprite.position.y +
    self.firingSpeed)];

    break;

    case 2:
    [self.mySprite
    setPosition:ccp(self.mySprite.position.x,self.mySprite.position.y +
    self.firingSpeed)];

    break;
    }

    if(self.mySprite.position.y >500 || self.mySprite.position.y <-20)
    {
    [self reset];
    }
    }

    -(void)reset
    {
    self.fired =NO;
    [self.mySprite setPosition:ccp(-100,-100)];
    }

    -(void)fire:(int)who position:(CGPoint)position fspeed:(int)fspeed;
    {
    self.firingSpeed = fspeed;
    self.whoFired = who;
    self.fired = YES;
    [self.mySprite setPosition:position];
    }

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

    @end

    Also don't forget to add the bullet's image to your project.
    Once you have those files, we have to create the pool of bullets in our GameScene.

  3. Add a new property to the GameLayer, NSMutableArray * enemies. This array will hold the bullets.
  4. Now in the init method of the GameLayer class, initialize that array with some Bullet objects:

    bullets = [[NSMutableArray alloc]initWithCapacity:50];
    for(int i=0;i<50;i++)
    {
    Bullet * c = [[Bullet alloc]initWithGame:self];
    [bullets addObject:c];
    [c release];
    }

  5. We have to tell all the bullets to update their positions if they were fired, so in the step method add the following lines of code:

    for(Bullet * b in bullets)
    {
    if(b.fired)
    {
    [b update];
    }
    }

    That would be all the code needed for creating the bullets and updating their positions when fired. If you run your project now you won't see any change, as we didn't define a way for them to appear. Let's give our enemies the ability to fire those bullets.

  6. Open the Enemy.m file and change the update method to look like the following:

    -(void)update
    {
    [self.mySprite
    setPosition:ccp(self.mySprite.position.x,self.mySprite.position.y -
    self.movementSpeed)];

    for(Bullet * b in theGame.bullets)
    {
    if(self.fireInterval>0 && !b.fired && self.lastTimeFired >
    self.fireInterval)
    {
    [b fire:2 position:self.mySprite.position fspeed:self.firingSpeed];
    self.lastTimeFired=0;
    }
    }

    self.lastTimeFired +=0.1;

    if(self.mySprite.position.y <-20)
    {
    [self reset];
    }
    }

  7. That's all, so run the project and wait. You should see some enemies firing at the hero as shown in the following screenshot:

What just happened?

That was all the code involved in the creation and manipulation of bullets as of now. Let's take a look at what we did.

The implementation of the Bullet class is quite simple. The init method just creates a sprite which gets added to the GameLayer.

We have the update method which checks who shot the bullet, and updates its position accordingly. Right now, we have only two possible behaviors: if the player shoots, the bullet has to move upwards, but if the enemies shoot, the bullets go downwards.

The fire and reset methods manage the state of the bullet and its position, so it is placed correctly when fired and when being removed from the screen.

In the GameLayer we made quite a few changes. First we created the NSMutableArray that holds the bullets just like we did with the enemies, then we added the required code to the step method to have the bullets update themselves.

The important part is in the update method of the Enemy class. There, we loop through all the bullets and if that particular enemy is allowed to shoot, he does so by calling the bullet's fire method, which places a bullet where the enemy is and has it start updating its position.

Have a go hero – upgrading your firepower

The way I devised for bullets to be shot is very simple and uninteresting. The following are a few ideas to enhance it:

  • Powerful enemies would be able to shoot in different ways, for example three-way shots, double shots, shots to their sides, and so on.
  • The hero might be able to like that too, once he has acquired some powerups.
  • Bullets don't necessarily have to move in the same direction all the time. They could be shot in curved paths, or have some advanced behavior, such as following the nearest target.

As you can see, there is a lot of room for improvement and if you desire to develop a real game of this type, you should do all this and more.

Summary

In this article:

  • We started a new game, a vertical-shooter action game; no more, no less
  • You learnt how to create new scenes and use transitions for going from one to another

In the next article, we will see how to handle accelerometer input and detect collisions.


Further resources on this subject:


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

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