Categories
Featured iPhone Development Resources

Game Programming Tutorial – Beginners Action Game

Welcome to my game programming tutorial on how to create a beginners action game for the iOS. This is a step by step tutorial for creating an iPhone action game. The principles taught also apply to the iPad.

You can see the result of a game running the code in the video below:

The graphics are a bit different with the tutorial (as some of those in the video are being used in an actual project) but that is the result of running the actual code within the tutorial.

Specificially the tutorial features:

  • Loading/Placing Images
  • Animation
  • Touch Events
  • Text
  • Sound Effects
  • Background Music
  • Buttons
  • Resetting The Game

Pre-requisites For This Game Programming Tutorial

Now before we start there are a few pre-requisites. You should have a basic understanding of Objective-C and will need a Mac with the iPhone SDK installed. The game was created using iPhone SDK 4.0, and the Sparrow Framework. Also note that I created this to be as easy to follow as possible so I used a minimum number of classes/methods to make it as easy as possible for a beginner to follow – I’ve even exclusively used autorelease objects  so you don’t need to worry about memory management (one of the facets of Objective-C many beginners have trouble with).

Why Am I Using The Sparrow Framework In This Game Programming Tutorial?

In case you’re wondering why I chose to go the route of using the Sparrow Framework it’s because I believe it is the easiest iPhone game development framework available for free for a beginner to start with, and it has already been used in a number of games in the app store.

Please share this tutorial by tweeting it or sharing using one of the buttons at the bottom.

You can navigate through the tutorial by using the page numbers at the bottom of each page.

If you’d like to jump right in you can download a project with all the resources loaded in here.

Getting The Code

You can download the code using the download links at the end of each page, or you can use the Github project found here:
https://github.com/maniacdev/iOS-Balloon-Game-Tutorial

Otherwise if you’d like to go the route of installing Sparrow, and setting everything up you can visit the Sparrow Framework getting started page here which explains the process of setting up an Xcode project that utilizes the Sparrow Framework. You can get just the game assets (graphics/sound) here.

The tutorial is definitely not perfect, and there are probably some spelling/grammar mistakes, but it is the most extensive single tutorial I’ve seen around and might never be released if I tried to perfect it 🙂

The first thing we’re going to do is start the game scene by displaying in a background image, adding the score and level text, and playing some music.  To navigate through this game programming tutorial use the page numbers at bottom, now go to page 2 by clicking that number at the bottom of this article to go to that page.

Displaying The Game Background, Score And Level Text, And Playing Background Music

In this tutorial everything will happen in the Game class files, the Game class extends the Sparrow Stage (SPStage) and

Let’s open up the Game class implementation (game.m) and in the initWithWidth method after the if statement opens we are going to load an image with the line

SPImage *backgroundImage = [SPImage imageWithContentsOfFile:@"tutorialbackground.png"];

The reason why I used the imageWIthContentsOfFile: method is because this is the only background image in the game and so I did not need to save the image information as a texture for use elsewhere.

Now we’ll draw the background image to the screen.

[self addChild:backgroundImage];

Notice that this places the image in the top left corner of the screen which is the (0,0) screen co-ordinate when using the Sparrow Framework, the point in the top left corner is the anchor point for images.

Next we are going to display the score and level text.  The first thing we are going to do is declare class wide variables for the score and level in the game.h interface file (place this code inside the squiggly brackets)

int score;
int level;

We’re also going to declare two SPTextField variables in the interface so that we can update the score and level text throughout the class.

SPTextField *scoreTextField;
SPTextField *levelTextField;

Now getting Back to the Game.m file we are going to initialize the score and level values, create our two text fields and initialize them to contain the score and level values.

score = 0;
level = 1;

scoreTextField = [SPTextField textFieldWithText:[NSString stringWithFormat:@"Score: %d", score]];
levelTextField = [SPTextField textFieldWithText:[NSString stringWithFormat:@"Level: %d", level]];

Notice that I used textFieldWithText to initialize the text fields and immediately fill them with text. This string of text is immutable, and we will need to change the SPTextField’s text attribute each time we want to update the score and level.

Now we’ll change the text field font, and place them in the upper corners

scoreTextField.fontName = @"Marker Felt";
scoreTextField.x = 160;
scoreTextField.y = 7;
scoreTextField.vAlign = SPVAlignTop;
scoreTextField.hAlign = SPHAlignRight;
scoreTextField.fontSize = 20;

[self addChild:scoreTextField];

levelTextField.fontName = @"Marker Felt";
levelTextField.x = 0;
levelTextField.y = 7;
levelTextField.vAlign = SPVAlignTop;
levelTextField.fontSize = 20;

[self addChild:levelTextField];

The fontName attribute of the text field was used to change the font, and the x and y attributes to place the location of the text field.

I also changed the vertical alignment with vAlign attribute and the horizontal alignment with the hAlign attribute because by default the text receives centered alignment.

Notice that I used the addChild method to add the text fields to the screen.

Now we are going to get the background music playing.  The first thing we are going to do is load in the music using the SPSound class.

SPSound *music = [SPSound soundWithContentsOfFile:@"music.caf"];

The reason why we loaded it in like this, and stored it in a variable is because in order to have a sound looping with the sound player we will need to create a channel, we’ll do that with this code:

We’ll now set things up so that the channel loops, turn down the volume a little bit, and start the actual playing.

SPSoundChannel *channel = [[music createChannel] retain];
channel.loop = YES;
channel.volume = 0.25;
[channel play];

If you were to were not retain the SPSoundChannel is that it would stop playing shortly after this init method had finished execution.

Now running the on the iPhone or in the simulator your screen should look like this and music should play:

You can download the project up to this step here.

In the next part of the tutorial we are going to load in the balloon textures and send a balloon upward through the screen.

Loading In The Balloon Textures And Creating Upward Animation

The first thing we are going to do is load in the balloon png files.  We’ll add an array variable to the game interface file (Game.h) as we will want to access the balloons from other places within the class.

We’re also going to add in a Sparrow sprite, sprites are display object containers, and we will use this sprite to hold the balloon images so that we can easily stop and reset the game.

 NSMutableArray *balloonTextures;
SPSprite *playFieldSprite;

Now we’ll head back to the game implementation file and load the textures into the array.

balloonTextures = [NSMutableArray array];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"bluetutorial.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"greentutorial.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"indigotutorial.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"orangetutorial.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"redtutorial.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"violettutorial.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:@"yellowtutorial.png"]];
[balloonTextures retain];

Now the reason why i loaded these balloons as SPTexture objects rather than SPImage objects is because I will be using these textures to create images throughout the class, and rather than loading them in each time I can use the texture objects to quickly create images (you’ll see this in a moment).

Now let’s create the sprite for our playing field and add that sprite to the stage.

playFieldSprite = [SPSprite sprite];
[self addChild:playFieldSprite];

This sprite will hold the balloons, and content used to reset the game so that we can easily clear the screen and stop animations.

Now we’re going to create a new method and call that addBalloon (make sure to add it to the interface too):

-(void)addBalloon

We’re going to use this method to create a balloon and send it upwards.  Don’t forget to also declare this method in the Game.h file.

The first thing we are going to do is add in a balloon image of a random color, and place it at a random location below the screen. Place this code in the addBalloon method we just created.

SPImage *image = [SPImage imageWithTexture:[balloonTextures objectAtIndex:arc4random() % balloonTextures.count]];

image.x = (arc4random() % (int)(self.width-image.width));
image.y = self.height;

[playFieldSprite addChild:image];

Notice here that i used imageWithTexture to use the texture object. Also you can see that I used the x and y variables of the object to place the image at the desired location, and also used image and height variables which are available in any display object.

In case you are not familiar with the arc4random() function it is a function used to generate random numbers (supposedly providing a greater degree of randomization than the rand() function), and does not need to be seeded.

Now we’re going to send the balloon upwards, and there are two ways to do that one way would be to modify the x and y coordinates of the balloon image directly, but we’ll take the easy way out and use the SPTween class.

SPTween *tween = [SPTween tweenWithTarget:image
time:(double)((arc4random() % 5) + 2)
transition:SP_TRANSITION_LINEAR];

Notice that I supplied the SPTween class with the image file, how long I wanted the animation to last for (in seconds) and a transition. There are many different transitions available (which can be found in the SPTransitions.h file included with the Sparrow Framework) the linear transition is simply an animation which is done with completely linear timing from start to finish.

Now we’re going to create the actual animation by changing a couple of properties.

[tween animateProperty:@"x" targetValue:arc4random() % (int)(self.width-image.width)];
[tween animateProperty:@"y" targetValue:-image.height];

All we had to do was change the x and y properties of the image, and the Sparrow Framework modifies these properties over the duration we specified as time when we created the Tween. If you’d like to see the properties available for an image you can check the SPDisplayObject.h file.

Finally we will add this tween to the “juggler”.

The juggler handles the timing for all animations, and is associated with the stage the image we are animating is on.

[self.juggler addObject:tween];

Now to add a balloon to the stage we are going to add a call to the drawBalloon method at the bottom of the if statement in the initWitWidth method.

[self addBalloon];

You should now see a single balloon rise through the top of the screen when you run the code like in this screenshot:
If you have any trouble you can download a project to bring you up to this point here.

In the next part of this tutorial we are going to make it so that when this balloon is touched it immediately falls through the bottom of the screen.

Adding Touch Events

To program in our response to the balloon being touched we are going to use the Sparrow Framework’s event system. Add this code to the end of the addBalloon method.

[image addEventListener:@selector(onTouchBalloon:) atObject:self forType:SP_EVENT_TYPE_TOUCH];

There are different SPEvent types, and SP_EVENT_TYPE_TOUCH is run whenever a touchable object is touched.

You can see that we used the selector for a method called “onTouchBalloon” this method will be executed whenever a touch occurs.

We will create the onTouchBalloon method as specified as the event listener, add this into both the Game.h and Game.m files.

-(void)onTouchBalloon:(SPTouchEvent*)event

Notice that this method takes an SPTouchEvent.

We will need to respond whenever a balloon is first touched, and the first thing we will need to do is determine which object has been touched.

SPDisplayObject* currentBalloon = (SPDisplayObject*)[event target];

We will remove the touch listener similar to how we added it simply by changing the word add to remove.

[currentBalloon removeEventListener:@selector(onTouchBalloon:) atObject:self forType:SP_EVENT_TYPE_TOUCH];

We are going to place the following code to increase the score and update the score text.

score+=10;
scoreTextField.text = [NSString stringWithFormat:@"Score: %d", score];

We’re also going to play a popping sound.

[[SPSound soundWithContentsOfFile:@"balloonpop.caf"] play];

Notice that all I did here was execute the play method on the SPSound. We don’t need to do any more here as we don’t need to loop or change the volume as we did with the background music. This is the simplest way to play a sound using the Sparrow Framework.

Let’s stop the upward animation of the balloon by removing all animations related to the touched balloon from the Juggler.

[self.juggler removeTweensWithTarget:currentBalloon];

Now we’re going to change the scale of the balloon to give the balloon a thinning effect like the air is flowing out from it.

SPTween *tween = [SPTween tweenWithTarget:currentBalloon time:0.35 transition:SP_TRANSITION_LINEAR];
[tween animateProperty:@"scaleX" targetValue:0.75];
[tween animateProperty:@"scaleY" targetValue:1.25];
[self.juggler addObject:tween];

Notice that I changed the scale of the balloon by adjusting the scaleX and scaleY properties.

Now we’re going to send the balloon out through the bottom of the screen.

tween = [SPTween tweenWithTarget:currentBalloon time:(self.height-currentBalloon.y)/self.height transition:SP_TRANSITION_LINEAR];
[tween animateProperty:@"y" targetValue:self.height+currentBalloon.height];
[self.juggler addObject:tween];

If everything is done right when the project is run a single balloon should be sent up the screen, and fall through the bottom of the screen when touched like in this screenshot:

If you have any problems you can download a project file to bring you to this point here.

In the next section we are going to draw more balloons as they are popped and increase the score and level.

Triggering Events From Other Events

The first thing we are going to do is create a simple method with a for-loop to create more balloons as the level goes up.  Add the code below into the Game.m file, and declare the method in the Game.h file.

-(void)drawBalloons
{
for(int i = 0; i < level; i++)
{
[self addBalloon];
}
}

Now we’re going to add an event listener (similar to what we did with touches) so that we can execute some code when the balloon has exited the screen. Place this code at the bottom of the addBalloon method.

[tween addEventListener:@selector(movementThroughTopOfScreen:) atObject:self forType:SP_EVENT_TYPE_TWEEN_COMPLETED];

Noticed that I added this event listener to the tween variable that we created for the balloon’s rising animation. The tween completed event occurs when this animation completes and with this code the movementThroughTopOfScreen method will be executed.

Now we’re going to create the movementThroughTopOfScreen method, and make it so that the animation stops.  Add the following code into the Game.m file, and declare the movementThroughTopOfScreen method in the Game.h file.

-(void)movementThroughTopOfScreen:(SPEvent*)event
{
[self.juggler removeAllObjects];
}

Executing the removeAllObjects method on the juggler ends the execution of the tween’s that we created.

Now we’re going to add another listener this time to the end of the onTouchBalloon method.

[tween addEventListener:@selector(balloonPopped:) atObject:self forType:SP_EVENT_TYPE_TWEEN_COMPLETED];

We’re going to create the method for this listener now that executes when the balloon has completely fallen through the screen. This is the method header to add to the Game class files.

-(void)balloonPopped:(SPEvent*)event

We’re going to need to remove the balloon from the playfield so just as we did in the ontouchballon event we’re going to get the target only this time cast it is a tween so we’re going to need to cast it as such, and get the target of the tween which is the balloon, and what we really want.

SPTween *animation = (SPTween*)[event target];
SPDisplayObject *currentBalloon = (SPDisplayObject*)[animation target];

We’re going to remove the balloon completely from the playing field.

[playFieldSprite removeChild:currentBalloon];

Now if all balloons have been removed we need to increase the level, and send out more balloons onto the playing field. This is the code we’ll use to do that.

if(playFieldSprite.numChildren == 0)

The numChildren variable as you can probably guess tells us how many children a sprite has.

The rest of the code on this page will go within this if statement.

Let’s increase the level, and update the display.

level++;
levelTextField.text = [NSString stringWithFormat:@"Level: %d", level];

Now to start up the next level we are going to execute the drawBalloons method.

[self drawBalloons];

Now when we run the project as we pop balloons more will show up, and if one exits through the top of the screen the game will pause.

This screenshot shows how multiple balloons should appear on the screen once you start popping balloons:

You can download a project file that brings you to the end of this part of the tutorial here.

In the next part of the tutorial we’re going to add a reset button, and restart the game so we can keep playing.

Resetting The Game

The first thing we are do here is add a classwide boolean variable so that this code will not execute multiple times so place this code in the interface in the Game.h file.

BOOL resetButtonVisible;

The reason I added this was because if multiple balloons exit the screen at the same time the movementThroughTopOfScreen method is executed multiple times.

Now add this if statement into the movementThroughTopOfScreen method.

if(resetButtonVisible == NO)
{
resetButtonVisible = YES;

The reason I added this was because if multiple balloons exit the screen at the same time the movementThroughTopOfScreen method is executed multiple times.

Now we’re going to add a semi-transparent image covering the screen.

SPImage *backgroundImage = [SPImage imageWithContentsOfFile:@"screenoverlay.png"];
[playFieldSprite addChild:backgroundImage];

An added side effect of running this code is that touches are prevented from reaching any ballons.

Now we’re going to add a button to the center of the screen.

SPButton *resetButton = [SPButton buttonWithUpState:[SPTexture textureWithContentsOfFile:@"reset_button.png"]];
resetButton.x = self.width/2-resetButton.width/2;
resetButton.y = self.height/2-resetButton.height/2;
resetButton.fontName = @"Marker Felt";
resetButton.fontSize = 20;
resetButton.text = @"Reset Game";

Notice that in creating this button we needed to provide an image, however the animation causing the image to shrink is all built in to the Sparrow Framework. As you can see the properties of buttons are very similar to text fields.

Now we’re going to add an event listener to the button.

[resetButton addEventListener:@selector(onResetButtonTriggered:) atObject:self
forType:SP_EVENT_TYPE_TRIGGERED];

The triggered event occurs when the button is pressed.

Add the button to the playing field with this code:

[playFieldSprite addChild:resetButton];

Now we’re going to create the onResetButtonTriggered event which runs when the button is pressed.

This method is pretty simple we just clear the screen, and then make things just as they were when the game first started.

- (void)onResetButtonTriggered:(SPEvent*)event
{
[playFieldSprite removeAllChildren];

resetButtonVisible = NO;

level = 1;
score = 0;
levelTextField.text = [NSString stringWithFormat:@"Level: %d", level];
scoreTextField.text = [NSString stringWithFormat:@"Score: %d", score];

[self addBalloon];
}

And that’s the end, when you run this tutorial you should see a reset button and be able to restart the game after a ballon exits through the top of the screen like in the screenshot below:

You can download the completed project here with extensive comments in the Game implementation file.

I hope you enjoyed this tutorial please re-tweet and share it by using the buttons below. If you have any questions then please enter them in the comments below.  I’d also love to here what you’d like to see next with this (menu, highscores, powerups?).  Thanks.

52 replies on “Game Programming Tutorial – Beginners Action Game”

Thanks a lot for that great tutorial! It's very well written and as easy to follow as possible, for a complex topic like that. I'm sure this was a lot of hard work …!

Thanks.. it was a lot more than i anticipated.. I hacked out the original code in about 20 minutes, and wrote the comments pretty quickly.

Next time I'll tackle a tutorial like this in more bite-sized chunks. I did want to see though if a more encompassing tutorial would be more popular.

It was better tutorial than all my iPhone books about development. Thank You Very Much.

BTW I reach level 19 🙂

Gr8 tutorial! Could u pls just explain how the SPDisplayObject *currentBalloon=(SPDisplayObject*)[event target] assigns the clicked-balloon?

Hi Marcio.. that gets you the clicked balloon because [event target] returns the object that threw the event which in that case is the balloon that was clicked.

The balloon is an image which is an SPDisplayObject.. (SPDisplayObject*) casts the target as an SPDisplayObject (we need to do this here because [event target] returns an id and we want to do some things that require it to be an SPDisplayObject).

ok one last thing. about sparrow. it says to create a new folder to which all builds will be placed into. so from now on, the build products will not be placed into each apps corresponding project folder but rather into this new directory? Isnt that avoidable? it just seems neater to have everything in the same directory.

I dl sparrow and placed it in /documents/iphoneapps/ which is where all my project folders are. I then created a new folder in iphoneapps called sparrow builds and set it from my build folders in preferences as directed and added the sparrow/src as instructed. But when I run the simulator it just gave me a black screen and hung and crashed Xcode. Everytime I opened Xcode it would crash, I'd reopen, crash, 3x until the 4th reopen it would open correctly so I'm reinstalling Xcode. This ever happen before? 🙁

thx, i got it working after reinstalling xcode again. something must have gone haywire during the original install. got the game working fine now. thx for the tutorial. this is my first intro into a game. i did look into the iphonecoredatarecipes tutorial linked in your site and id like to ask, could i ask u some questions about that?

This is a really good tutorial, thank you!

However, i'm having a little trouble displaying the score and level textfields. The score one shows at the top right fine (and i've experimented with different values to see how to place it in other areas), but the level one doesn't show at all. I've been over and over my code and I don't think i've omitted anything.

Also, in your example, why do you initialise the level textfield again just before you set the fontName etc? Hasn't it already been initialised a long with the score in the previous statements? I've tried mine with and without this line and it makes no difference, “level: 1” still won't show.

Thanks, i've read over the whole tutorial to get a general idea. It's really good, and I hope you'll do more of these.

The re-declaration was a mistake thank you for pointing that out, I corrected the page. Can't check the code sample right now as I am away from home without a Mac at my disposal.

What happens when you run the project on the bottom of that page? Should display both the score and level when run.

All I can think of is that the [self addChild:levelTextField]; line is missing from your code.

Thanks for the reply.

Yes, the project at the bottom of the post runs fine and I can see both textfields. I do have [self addChild: levelTextField]; in my code so i'm at a loss to explain it myself.

Don't worry about it though, i'll see how I get on when I need to do something similar in my own project. Enjoy the rest of your day=)

Hey, it was no problem.. I was just taking care of a friend's kids got bored watching them play Wii so I decided to check for comments 🙂

Hope you were able to find what was missing.

Maybe this was mentioned somewhere, but I couldn’t find it…

I had to add the media files to my project before any reference to them would work (the app just crashes until then).

Add them by choosing Add > Existing Files from the Actions menu in your main project window, or by choosing Project > Add to Project in the main menu.

Maybe this isn’t necessary for all users, but it took me a while to figure it out.

I’m in XCode 3.2.3 using SDK 4.0.2

Very helpful, thanks!

However, the app will not start in XCode4 in the iPhone Simulator. I read the comment about adding the media files again.. not sure that worked in Xcode4 .. or am I doing something wrong?

Interesting.. I will check it out. I have modified the project on my end so I need to try the original.

I downloaded the project with all the resources loaded. I followed the instruction on the page 2 that added the background image, textfields for score and level, and music. But when I build and run on the simulator, it just display black screen and terminate the program. I’m not sure what I missed. I’m running the xCode 4. Any help would be appreciated. Thanks !!!

I got the following messages on the console:

2011-04-11 10:36:28.111 BalloonGameTutorial[1521:207] One Tap (Touch Event)
AudioStreamBasicDescription: 2 ch, 44100 Hz, ‘lpcm’ (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved
2011-04-11 10:36:30.299 BalloonGameTutorial[1521:207] failed to create framebuffer: 8cd6

Thankfull for help!

On XCode 4 its displaying the empty screen with a console message

AudioStreamBasicDescription: 2 ch, 44100 Hz, ‘lpcm’ (0x00000C2C) 8.24-bit little-endian signed integer, deinterleaved

Dont ahve any idea. I am a nob in game development…

Hi.. don’t get that, but I assume this error is in the simulator? Unfortunately the simulator has issues with sound.

Outstanding! This really sells Sparrow for me.
The only issue I encountered was in the for loop for the addBalloons method. You show the entity for less than, not the less than sign. Easy and obvious fix:

for(int i = 0; i < level; i++)
…should be…
for(int i = 0; i < level; i++) {

I will be showing this to my iPhone class.
Thanks so much!

Thanks Brad, it looks like one of the many WP or plugin updates has messed things up in the code area.. will have to look as to why that is happening.

everything worked except when i touch the balloon, the app crashes. What do i need to fix because xcode can’t find any errors. i am testing this in the iphone simulator

Sounds like a sound issue – the iOS simulator has quite a few problems playing sound. I will look into it. Which version of the SDK/Simulator were you running in?

This is such a huge help to me.  Thank you for posting this.  Exactly what I need to jump start my iOS development.

I downloaded the game files and got it to work on the first try. OSX 10.5 iOS SDK 4.3

Hello!  Thank you for the tutorial!
I noticed just a small detail: the page #2 and the following ones are tagged as “c++” as they should be tagged as “objective-C”.

Here with Xcode 4.2.1 and IOS 5 SDK, to make the tutorial compile, i had to comment out one line in SPPoolObject.m

Same problem here, throws “Arithmetic on pointer to interface ‘id’, which is not a constant size in non-fragile ABI”

Hey thanks for this tutorial! One thing though, when I click run and it opens up the simulator, it stays on a black screen for at least 2-4 minutes before starting the app. Any reason you can think of as to why?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.