Friday, December 3, 2010

Kill 'em All - Part II (Rotating the cannon)

Hello Guyz today we will be starting with what we left in our previous post. For those who werent following: In our last post we made a bird shooting game named Kill 'em All. You can view the post here.
Today we will continue from the spot making the cannon rotate as we shoot the birds (high in the sky :D)

1. Adding and replacing some Resources


2. Keeping the track of our cannon and next Projectile


3. The main code


4. Tap....rotate.....bang.....veeeeEEeeee!!!!

Sunday, November 28, 2010

My first game - Kill 'em all! (Part I)

Hello guys.
It took me nearly a month to get my first simple game up on my device. So here I am writing this post for all my fellow iphone game beginners to make sure they get their first game up in less than 24 days. Seriously guys I do mean it!!!

So hitting the deck, things you will need :
1. XCode
2. iPhone SDK
3. Cocos2d

XCode+iPhone SDK is a big download and can be found in Apple's website. So I suggest you start downloading now. (You can download the both for free here but you need to sign up first).

Cocos2d is the world's most famous iphone game engine for 2d games. Most of the developers use Cocos2d for their games. Famous games like StickWars and Gorillaz that are on app store were created using cocos2d. Some of the famous games for inspirations written with cocos can be found here.  So, what are you waiting for? Install the framework now (download page). For instructions on how to install the framework visit here.

If you are still with me and have followed the instructions on the page you should have now helloWorld scene running on your simulator/device. Before diving right into the code it is vital that you know about
1. Scenes
2. Layers
3. Directors
4. Sprites
A short and basic description about these can be found here

Now when everything is lined up lets get going with our new game. Its a bird shooting game where you will have to shoot the birds flying in the air.  When we are completed our game will look something like this :

Final Version of the game you are about to build


1. Starting the new cocos project

Launch XCode and under files menu click New Project. Click on the first template (the most simplest one) cocos2d-application as shown in the figure. Name the project as "Kill Them All". 

Click on the first template


Hit build and go and you should see the hello world screen.

Screen that appears in the simulator

2. Adding our cannon!

Now expand the classes folder and HelloWorldScene.m and find the function init, and remove everything inside the if (self=[super init]) block. Actually this is where our hello world label was being created. We will add our code to add some sprites over here. The new init will look something like this:

-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
}
return self;
}

Before adding the new sprites let us add some resources onto our project. You can download the images that I made :

bird.png

cannon.png

cannonball.png

Download the images and drag them over to your resources folder. Check copy if necassary option if unchecked. Now we have all the resources we require for our game. Time to add them to our scene!

We will going to add the cannon first in the middle of the screen (horizontally) and at the bottom (vertically). We will do this by making a sprite with the cannon.png and setting its position to required co-ordinates. In HelloWorldScene.m find the init function and add these lines of code.

-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
CGSize winSize=[[CCDirector sharedDirector] winSize];

CCSprite * cannon = [CCSprite spriteWithFile:@"cannon.png" rect:CGRectMake(0, 0, 30, 40)]; 
cannon.position=ccp(winSize.width/2,cannon.contentSize.height/2);
[self addChild:cannon];
}
return self;
}
First of all we ask the director (singleton object) for the size of the current scene. Our current scene is the size of the iphone screen (480*320) in landscaped position. Next we make a sprite using our cannon.png. Now for the important part, placing the cannon. ccp (cocos 2d point) takes (x,y) position. While programming in cocos framework our bottom left corner is (0,0). Value of x increases horizontally and y increases vertically. A sprite is always places according to its midpoint. Refer to the picture for more precise details.












Before adding birds let us change the color of our screen to white as the color of our birds is black. To do so open HelloWorldScene.h and change the parent class of HelloWorldScene from CCLayer to CCColorLayer

#import "cocos2d.h"

// HelloWorld Layer
@interface HelloWorld : CCColorLayer
{
}

// returns a Scene that contains the HelloWorld as the only child
+(id) scene;

@end
Now in the init function of the HelloWorldScene.m change within the if block so that the code looks like
if( (self=[super initWithColor:ccc4(255, 255, 255, 255)] )) {








Now you should have something like this : 







iPhone Simulator
3. Making the birds fly by!

Easy till now, eh ? Well be with me when I say the rest of the game is equally easy. Now first of all we select the region we want to get the birds fly. Refer to the picture for details. The shaded region shows the area where the birds will fly.

Birds will come from left of the screen randomly from anywhere b/w range(max-min) and dissapear randomly in the same range. Its not necassary that they will go in straight line. We will set so that they can go diagonally.

If its a bit hard-to-get at the moment , do not worry too much for you will understand as soon as we write some code. First of all in HelloWorldScene.m open the init function and these lines of code right after you have added cannon.

[self schedule:@selector (addBirds:) interval:0.5];
This line tells the compiler that we want the function addBirds to be called after every 0.5 of a second. Next step is to add the function addBirds().


-(void)addBirds:(ccTime) dt{

//Initing the sprite with the bird.png
CCSprite * bird=[CCSprite spriteWithFile:@"bird.png" rect:CGRectMake(0, 0, 29, 30)];
//Setting min and max y
CGSize winSize=[[CCDirector sharedDirector] winSize];
int maxY=winSize.height-bird.contentSize.height/2;
int minY=winSize.height/2;
int rangeY=maxY-minY;
int actualY=arc4random()%rangeY+minY;
//Placing the bird
bird.position=ccp(winSize.width+bird.contentSize.width/2,actualY);
[self addChild:bird];
//Moving the bird
int minSpeed=2.0;
int maxSpeed=4.0;
int rangeSpeed=maxSpeed-minSpeed;
int actualRange=arc4random()%rangeSpeed+minSpeed;
int actualDestinationY=arc4random()%rangeY+minY;
id moveTo=[CCMoveTo actionWithDuration:actualRange position:ccp(-bird.contentSize.width/2,actualDestinationY)];
id removeFromScreen=[CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)];
[bird runAction:[CCSequence actions:moveTo,removeFromScreen,nil]];
}
First of all we initiate our sprite with bird.png. Then we calculate the range by setting min and max values of Y. After having placed our bird on the screen (a bit left from the main screen) we move towards the more pressing matter: moving the birds. After setting a duration in the same manner as we have set the range we look forward to three important functions:

  • CCMoveTo
  • CCCallFunc
  • runAction
CCMove takes duration and point and moves the target towards that point. In this case we pass random duration b/w 2 and 4 we have set. We know destinationX is a bit right from screen while Y again is set random. CCCallFunc calls a function passing itself as argument. We pass it to spriteMoveFinished: (we will write it shortly to dispose off the sprite that have completed the move). Finally we run the action on our sprite with the stated sequence. Lets write spriteMoveFinished:

-(void)spriteMoveFinished:(id)sender{
CCSprite * sprite=(CCSprite *)sender;
[self removeChild:sprite cleanup:YES];
}
Hit build and run and vohoooooOOOoo . Now you have flying birds passing by :) . It must look something like this:

iPhone Simulator

4. Kill 'em All-Time to shoot!


Yes peeps its time to shoot now. The cannon balls that we had in arsenals are about to get loose with the touch of your finger. So unlike the soldiers on the field get this line in your init function right at the start of the if block.
self.isTouchEnabled=YES;
Guess dont have to explain that one, eh ? :)

We have to do a little maths here. When we touch our screen we will get a point. Remember in CCMoveTo function we need two parameters: duration and destinationPoint. So the cannonball that will be released from our cannon needs duration and destination point both. They both will be calculated by maths as initially we dont know what they are. Refer to the picture for more detailed description:

Explaination-I

Explaination-II

Now we are going to shoot a cannon ball as soon as our touch ends, that is finger is raised from the screen. To do so, add the function -(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{} with following lines of codes:

-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
//Choose one of the touch
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location=[[CCDirector sharedDirector] convertToGL:location];
//Setup initial position of the projectile
CGSize winSize=[[CCDirector sharedDirector] winSize];
CCSprite * cannonball = [CCSprite spriteWithFile:@"cannonball.png" rect:CGRectMake(0, 0, 20, 20)];
cannonball.position=ccp(winSize.width/2,40);
//Determining the offSet of the touch from projectile
int offX= location.x-cannonball.position.x;
int offY= location.y-cannonball.position.y;
//Discard if touch's y<=0
if (offY<=0) return;
//Adding the projectile
[self addChild:cannonball];
//Determine where to shoot the projectile to 
int realY = winSize.height+cannonball.contentSize.height/2;
float ratio=(float)offY/(float)offX;
int realX=(realY/ratio)+cannonball.position.x;
CGPoint realDestination = ccp(realX,realY);
//Determining the lenght of how far we are shooting so as to to make the speed uniform 
int offRealX=realX-cannonball.position.x;
int offRealY=realY-cannonball.position.y;
float distance=sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity=325
float duration=distance/velocity;
//Move projectile
[cannonball runAction:[CCSequence actions:
  [CCMoveTo actionWithDuration:duration position:realDestination],
  [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)],
  nil]];
}

Remember from high school physics that s=vt. We need all the cannon balls to go uniformly. Thus we keep the v constant. We calculate S by pythagoras theorem for calculating length of the hypotenuse. Lastly we calculate time by t=s/v. Fairly simple! Hit build and go and now you should be able to shoot cannon balls!!!
iPhone Simulator



4. What? Are they mortal?!?

Guess not. We need to add some collision detection for our compiler to know that the birds have been hit by the cannon. After the collision is detected we will remove both the bird and the cannon from the screen. Doing this is as easy as it sounds.

First of all we need to keep the count of the cannons as well as birds that are on the screen. We will do so by NSMutableArray .We will add the birds in the array as soon as they appear on the scrren. And delete them from the array as soon as they disappear or get hit by the cannon. Same goes for cannon balls. So in HelloWorldScene.h, declare two NSMutableArrays _cannonBalls and _birds.

@interface HelloWorld : CCColorLayer
{
NSMutableArray * _birds;
NSMutableArray * _cannonBalls;
}
And initiatate the arrays in init
_birds=[[NSMutableArray alloc] init];
_cannonBalls=[[NSMutableArray alloc] init];
Also release them in the dealloc funciton in HelloWorldScene.m
- (void) dealloc
{
[_birds release];
[_cannonBalls release];
[super dealloc];
}
Now as soon as the birds enter the scene add and tag them. You will see why tagging is being done in a moment. In the addBirds: function add these lines at the end.
bird.tag=1;
[_birds addObject:bird];

And in the touchend method where the cannon balls are being created add these lines at the end
cannonball.tag=2;
[_cannonBalls addObject:cannonball];

Now remove them from the array as soon as the move is finished otherwise memory leak issues will arrise. This is done in the spriteMoveFinished method:
if (sprite.tag==1) {
[_birds removeObject:sprite];
}
else if (sprite.tag==2) {
[_cannonBalls removeObject:sprite];
}
Now when we have the count for the cannonballs and the birds lets proceed towards the collision detection. For the purpose open the HelloWordScene.m file and in the init add these lines right after [self schedule:@selector (addBirds:) interval:2.0];
[self schedule:@selector (update:)];

Note that unlike adding the birds in which we called addBirds: after 2.0 seconds, we dont pass any duration period here. This is because we want update method to be called as soon as possible. Now for adding the update method.
-(void)update:(ccTime)dt{
NSMutableArray * cannonBallsToDelete=[[NSMutableArray alloc]init];
for(CCSprite * ball in _cannonBalls) {
CGRect ballRect = CGRectMake(ball.position.x-ball.contentSize.width/2
  ball.position.y-ball.contentSize.height/2
  ball.contentSize.width
  ball.contentSize.height);
NSMutableArray * birdsToDelete=[[NSMutableArray alloc] init];
for (CCSprite * bird in _birds) {
CGRect birdRect = CGRectMake (
bird.position.x - (bird.contentSize.width/2), 
bird.position.y - (bird.contentSize.height/2), 
bird.contentSize.width
bird.contentSize.height);
if (CGRectIntersectsRect(birdRect,ballRect)) {
[birdsToDelete addObject:bird];
}
}
for (CCSprite * bird in birdsToDelete) {
[_birds removeObject:bird];
[self removeChild:bird cleanup:YES];
}
if (birdsToDelete.count>0) {
[cannonBallsToDelete addObject:ball];
}
[birdsToDelete release];
}
for (CCSprite *ball in cannonBallsToDelete) {
[_cannonBalls removeObject:ball];
[self removeChild:ball cleanup:YES];
}

[cannonBallsToDelete release];
}
The logic of the update function is that of a bubble sort or in simple words two nested for loops. For every cannonball we will check every bird that is on the screen. We do so by making a cannonRectangle with the help of cannonBall and a birdRect with the help of bird.  Finally we check for the intersection of the two rects. If found we add the objects in birds-to-delete and cannonBallstoDelete array. Note that we cannot directly remove objects from _birds and _cannonBalls because during iteration you cannot do so in NSMutableArrays.

Hit Build and Run!!! Smack That all on the floor,... lolz
Finally we almost have a working game.. You deserve a pat on the shoulders now... :)
The birds and cannons shall disappear after colliding.


5. Adding a decent background

Apart from the fact that our game is functional now, no game is complete without decent graphics and sounds. You will me amazed to see how easy cocos has made to add both of them in your game!!!

We will  start by adding a background. Now professionally you would add a different layer for the background but for beginners we will add a new sprite with a background on the same layer. We will make sure the background sprite is beneath all of the other sprites otherwise all of the birds, cannonballs and our cannon will become invisible under the bg.
You can use this background that I made. Save this to your disk and drag'n'drop to your resources folder.

Save this bg and add to your resources folder of xcode

Now open HelloWorldScene.m and make sure you add the lines of code BEFORE you add the cannon otherwise cannon will hide. 

My whole init function uptill now looks something like this 
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super initWithColor:ccc4(255, 255, 255, 255)] )) {
self.isTouchEnabled=YES;
_birds=[[NSMutableArray alloc] init];
_cannonBalls=[[NSMutableArray alloc] init];
CGSize winSize=[[CCDirector sharedDirector] winSize];

CCSprite * background = [CCSprite spriteWithFile:@"background.png" rect:CGRectMake(0, 0, 480, 320)];
background.position=ccp(winSize.width/2,winSize.height/2);
[self addChild:background];
CCSprite * cannon = [CCSprite spriteWithFile:@"cannon.png" rect:CGRectMake(0, 0, 30, 40)];
cannon.position=ccp(winSize.width/2,cannon.contentSize.height/2);
[self addChild:cannon];
[self schedule:@selector (addBirds:) interval:0.7];
[self schedule:@selector (update:)];
}
return self;
}
Add the bold lines to the game.

5. Chime Time!

Now for adding the sound. We will use three sounds in our game. One for the background, one for cannon ball shoot and the last one for killing the birds. 
You can get the background sound from here. (This music is a property of Raywenderlich and only due to the fact that he has marked his link as a free download I am pasting the link here.) Its licensed as free or you may use any other sound you like (of the type .aiff,.caf,.wav). Just download and add into your resources folder.


In HelloWorldScene.h import SimpleAudioEngine.h
#import "SimpleAudioEngine.h"

and in the  HelloWorldScene.m in the init function get the audio engine to play the background music.
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"raywenderlich"];
Simple, aint it ?
Now for the other two sounds,  I have choosen  this(click.wav).
Download and add it as well to the resources folder of your project.  

Add this line in your cctouchesended function :
[[SimpleAudioEngine sharedEngine] playEffect:@"click.wav"];
and in the update: method where you are detecting the collision, that is the if block of the intersection check
if (CGRectIntersectsRect(birdRect,ballRect)) {
[birdsToDelete addObject:bird];
[[SimpleAudioEngine sharedEngine] playEffect:@"click.wav"];
}}  
Hit build and go and you will start to like the game even more :)





6. This is a game and it gets over, you know :/






Now lastly for adding the game over and the win scene.


Select the classes folder and hit opt-N or select new file from File menu. Make a class with name GameOverScene and make sure .h option is selected.
Once made open GameOverScene.h and replace the contents with these lines of code here
#import "cocos2d.h"

@interface GameOverLayer : CCColorLayer {
CCLabel * _label;
}
@property (nonatomic, retain) CCLabel *label;
@end

@interface GameOverScene : CCScene {
GameOverLayer *_layer;
}
@property (nonatomic, retain) GameOverLayer *layer;
@end
and replace the contents of the GameOverScene.m with these lines
#import "GameOverScene.h"
#import "HelloWorldScene.h"

@implementation GameOverScene
@synthesize layer = _layer;

- (id)init {
if ((self = [super init])) {
self.layer = [GameOverLayer node];
[self addChild:_layer];
}
return self;
}

- (void)dealloc {
[_layer release];
_layer = nil;
[super dealloc];
}

@end

@implementation GameOverLayer
@synthesize label = _label;

-(id) init
{
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite * background = [CCSprite spriteWithFile:@"background.png" rect:CGRectMake(0, 0, 480, 320)];
background.position=ccp(winSize.width/2,winSize.height/2);
[self addChild:background];
self.label = [CCLabel labelWithString:@"" fontName:@"Arial" fontSize:32];
_label.color = ccc3(0,0,0);
_label.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:_label];
[self runAction:[CCSequence actions:
[CCDelayTime actionWithDuration:3],
[CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)],
nil]];
}
return self;
}

- (void)gameOverDone {
[[CCDirector sharedDirector] replaceScene:[HelloWorld scene]];
}

- (void)dealloc {
[_label release];
_label = nil;
[super dealloc];
}

@end
I am sure up till now these lines have become self explanatory!!!

Now for telling the HelloWorldScene when to display this scene. We will make our game logic as:
1. Game over if 3 birds pass by.
2. You win if you kill 10 birds.

So add these ints to HelloWorldScene.h
int birdsKill;
int birdsPass;
After adding 
#import "GameOverScene.h"
in HelloWorldScene.m add these lines to your spriteMoveFinished function. The lines will go inside the if (sprite.tag==1) because this is the place where we know our bird has flown by:

if (sprite.tag==1) {
[_birds removeObject:sprite];
NSLog(@"bird removed");
birdsPass++;
if (birdsPass==3) {
birdsPass=0;
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
Code to display the you win tag will go in the if (intersectionRect) block of the update because that is where we see if we have shot any bird.
Add the bolded lines :


if (CGRectIntersectsRect(birdRect,ballRect)) {
[birdsToDelete addObject:bird];
[[SimpleAudioEngine sharedEngine] playEffect:@"click.wav"];
if (birdsKill==10) {
birdsKill=0;
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];


}

Build hit and go and enjoy your own ready made challenging game!!!
Final game should look like this

iPhone Simulator


7. Show me the Codeeeee!!!!






Congratz..... *hand shake* you have successfully completed your first simpe full working game in no time! You can find the code of this whole game here. Also see the next post where we put some animations into this game to make it more realistic!!! Till thn... Audios