Building a Caterpillar Game with Cocos2D: Sprites & Gameboard

In this series, we will be recreating the popular Atari game Centipede using the Cocos2D game engine for iOS. This is the second installment, where you will learn how to setup the game board and populate it with sprites.

Where We Left Off...

In the first tutorial in this series, I showed you how to download, install, and create a new Cocos2D project. I also showed you how you can prepare your assets using Texture Packer and load them into your game.

Today, I will go into more detail about setting up the user interface for the game as well as drawing the game board.

Step 1: The Sprout Field - Game Object

In the traditional version of the centipede game, mushrooms were you used to define the play area of the centipede. We are going to use that same idea and create what I'm calling sprouts.

Before we can begin talking about any of the "in-game objects", we need to define a superclass that will contain some of the common methods and properties that every object will need. This will save us some redundant code in the future.

Create a new file called GameObject.m and, in the header file, write the following code:

Here is a brief description of each of the properties in here. I'll go into detail about the methods when I show you the implementation.

  • sprite: Every object in the game will need something to visually represent it. This will be the sprite/image object used to draw _this_ object.
  • gameLayer: Simply a reference to the main game layer. This will be useful for responding to collisions as well as letting objects add their own sprites to the drawing area.
  • position: The location of this object in the game world.

Now, let's take a look at the implementation. Open GameObject.m and add the following code:

1. Since our object's position is different than the sprite's postion, we need to overwrite the setter for the position property to ensure that the sprite gets moved on the screen.

2. This method will be used a little later when we implement our collision detection. It bascially determines the bounding box for our object and returns it.

Step 2: The Sprout Field - Sprout Object

Now that we have the GameObject in place, the Sprout object becomes quite simple. The only purpose for the sprouts is to serve as an obstacle for the player and the centipede.

Create a new GameObject subclass called Sprout. Again, we need to subclass GameObject in order to get some of those boilerplate properties and methods.

Open up Sprout.h and add the following code:

The only addition the Sprout object makes to the GameObject is that it has a lives property. When the player shoots the sprout we want it to get hit a certain number of times (in this case 3) before it disappears.

The implementation of a Sprout object is also quite simplistic. Add the following code to Sprout.m.

1. Since we are overwriting the initWithGameLayer method, we need to call the super class' version of it as well. This will ensure that everything gets set up properly.

2. This sets up our current sprite to a sprite coming out of our CCSpriteBatchNode with the name sprout.png.

3. Adds the sprite to the batch node layer to be drawn.

4. Sets the number of lives of the sprout to the kSproutLives configuration setting (to be defined in a moment)

5. We want the user to be provided with some feedback when they hit a sprout and some idicator as to how much health is left. I have done this by dropping the opacity by a third ever time it has been hit.

As I mentioned in 4, we need to set up some basic game configuration. Luckily, Cocos2D has provided a file called GameConfig.h to do just that. Open it up and add this line:

Now that we have created a basic Sprout object, we can use this to generate an entire field for our game.

Step 3: The Sprout Field - Random Locations

The map for our game will be generated at random with a minimum amount of sprouts in the game at any given time. To do this, we need to keep track of where we have already placed sprouts. Also, the number of sprouts that get added to the map will be based on the current level: the more sprouts on the map, the harder the game is for the player. So, open up GameLayer.h and add an ivar and two properties. I'm only going to show the additions rather than reposting the code that we previously wrote.

1. This is a 2D array of boolean values used to denote where on the grid we have already placed Sprout objects. True means one exists and false means one doesn't.

2. This is the current level of the game. Many other events will rely on this property as well. The other property holds the array of Sprout objects currently in play. Make sure to @synthesize these properties in the implementation file as well as release the sprouts array. This is the last time I'll mention synthesize and release for properties. From now on, I'm going to assume you already know that! ;)

One other thing to note is you need to import GameConfig.h in order to access the kRows and kColumns constants. Don't worry about these for now, we will define them a little later.

Now open up GameLayer.m. Start by adding a private interface for GameLayer at the top so that we can define a few methods that we will be using. Also, be sure to add the import statement for Sprout.h as we will be making heavy use of it here.

Above the @implementation line, add the following code:

These are two helper methods we will be using in order to set up our field. I will go into a little more detail about them during implementation.

Now let's add the following code to the bottom of your init method (inside of the if( (self=[super init])) statement braces.

1. Start the game off at level 1

2. Loop over _locations, initialize them all, and set them to NO

3. Loop kStartingSproutsCount times and place random sprouts on the grid

This code introduces a few more constants that we have yet to define. Before we move on, let's define these constants and a few more in our GameConfig.h file.

The last step here is to implement the two methods that we defined above. Add the following methods to your GameLayer.m file:

placeRandomSprout

1. We start by initializing a new Sprout object and fetching a random location to put it. At this stage, the location is in grid space, we need to convert it to world space in order to draw it.

2. We do some basic math here to map this sprout from grid space to world space based on the size of the play area and cell size.

3. Finally, the Sprout object gets added to the array of live sprouts

randomEmptyLocation

1. This loop will not complete until we find an empty location. I guess there _could_ be a time when there are no locations available and it will continue forever, however by that stage, the game will be at some ridiculous speed in which no player _should_ be able to play. An optimization might be to put a check in here to ensure that every space isn't filled first.

2. Pick a random row and column

3. Check if a sprout exists at that location, if not, update the _locations ivar and notify the loop to break.

4. Finally, we just return the new point that was created.

At this point, if you run your code you should see the sprout field created and spread around the play area. I have also made it so sprouts never spawn in the first or last rows to make things interesting for the player and caterpillar.

Your game should now look something like this:

Sprout Field

Step 4: The Player Object

The next logical step in creating our interface is to build the Player object. The player object will again be extremely simply thanks to the help of our super class.

Create a new GameObject subclass called player and paste the following code into the Player.h file.

A player adds lives and score to the base GameObject. We will use these properties to display the remaining lives as well as the score in the next section.

Now, open up Player.m and add the following code:

The initWithGameLayer method for the Player is exactly the same as the Sprout's method, except it adds a different sprite to the game.

That's all for the Player object. The final step is to initialize the player and add him to the game.

We must first declare a Player object in GameLayer.h. Make sure you also import the Player object here too.

Now, add the following code directly below the code you wrote in the last section the init method:

Again, this introduces a constant that we need to add to GameConfig.h

If you run the game at this point, you should now see the red player square added to the game.

Player

Step 5: Displaying The Player's Lives

We need to give the player some sort of feedback as to how many lives they have remaining. The way I have chosen to implement this is to display the player's sprite in the upper left corner. So, if the player has 3 lives, it will display 3 red squares.

There are a few ways to handle this requirement. I have chosen to maintain an array of player sprites and use that to manage the display of the player's life count. Start by adding (you guessed it) another property to the GameLayer class called livesSprites:

Next, add the following code to the init method:

Notice that I have added the GameLayer as an observer to a notification named kNotificationPlayerLives. Since we might not be decrementing the player's life inside of the GameLayer, we need a way to make the UI respond when they player gets hit. This is the easiest way to make that happen. Also notice that I am posting the notification right away. Since this method is responsible for drawing the player's lives, we need to call it during init to display the lives initially. This also adds another constant called kNotificationPlayerLives. Make sure you add it to your GameConfig.h file. We are going to do the same thing with player score in a moment, so you might as well add the constants for both:

Now, we are going to implement the updateLives method in our code. This method will be responsible for drawing the lives of the player and eventually transitioning to the game over screen when the player's lives go to 0. Add the following method to your GameLayer.m file:

1. First, we completely clear out the livesSprites array and remove their sprites from the scene.

2. Next, we loop based on the life the player has left, create a new sprite, and add it to the scene.

Now that this is complete, you can Run your application and you should see the player's life along the top:

Player Life

Step 6: Displaying The Player Score

This process is going to be very similar to what we just did with the player's lives, only we are going to be using a CCLabelTTF to display the player's score. CCLabelTTFs are used in Cocos2D to display text in your games.

Start by opening GameLayer.h and adding the following property:

Next, go into the init method of GameLayer.m and initialize the label and notifications with the following code:

This code should look very straight forward given the explanation of the player's lives. The next step is to implement the updateScore method to get the score to display. Add the following method to your GameLayer.m file:

A CCLabelTTF works very similar to a UILabel in UIKit. You set the string and you are good to go! Now, whenever we update the score in the game, we simply post that score notification and the UI will update accordingly.

The final version of your game should look like this:

Player Score

The project is really starting to look like a game now!

Next Time

The next tutorial will cover the most complex (and most fun) part of this series. We will be implementing the caterpillar object along with its AI.

Happy Coding!

Tags:

Comments

Related Articles