Build a Caterpillar Game with Cocos2D: Collision Detection

This is the sixth installment of our Cocos2D tutorial series on cloning Centipede for iOS. Make sure you have completed the previous parts before beginning.


Last Time...

In the last tutorial, I showed you how to create an array of missile objects and fire a constant stream of them. You also learned about basic player interaction in Cocos2D in order to move the player.

In today's tutorial, we will be exploring how to set up basic collision detection in Cocos2D. While this isn't always the optimal solution, it's definitely the quickest and easiest to implement.


Step 1: Missile/Sprout Collision

The missile collision with the sprouts is much like the player collision with the sprouts. We simply check the bounds of each missile in play against the bounds of each sprout in play and determine if any collide. When a sprout has been hit, we decrement its life, modify its opacity, and remove it if its life reaches 0.

Open up Missile.m, import Sprout.h, and add the following code to the bottom of the update method:

As each missile updates, it enumerates all of the sprouts in play, checking to see if their bounding rects intersect. If there is a collision, we set the missile to "dirty" and decrement the sprout's lives accordingly. We have already written the code to modify the sprouts opacity based on its lives, so if you run the game at this point, you should see the sprouts changing when they get hit. The problem is they are still in play. We need to remove sprouts with 0 lives.

This can be done inside of the update: method in GameLayer.m. Open GameLayer.m and add the following code to the bottom of the update: method:

  1. We enumerate each of the sprouts looking for a dead one. To simplify things, we only clear one sprout per iteration.
  2. If a dead sprout exists, we remove its sprite from the batch node and remove the sprout from our sprouts array.

Now, run the game and you will see the sprouts fading when hit and eventually disappearing.


Step 2: Caterpillar Collision

We now need to add collision detection between the missile and the caterpillar. This interaction is what truly makes your app a game. Once hit, the caterpillar should split at the hit segment and each new "caterpillar" should travel in separate directions. The segment that was hit then gets turned into a sprout.

Start by opening Missile.m, importing Caterpillar.h and Segment.h, and add the following code to the bottom of the update: method:

  1. Enumerate all of the caterpillars in play and enumerate each of their segments.
  2. Check for a collision and remember which segment of which caterpillar was hit.
  3. If we have a hit, we split the caterpillar at the current segment. Don't worry, we will implement this method shortly.

Now that we have the missile colliding with the caterpillar, there are a few things we need to do. The first is to write the logic to split the caterpillar on a given segment. This will be done in GameLayer.m. First, open GameLayer.h and add the following forward class declarations.

Next, declare the following method:

Before we begin with the implementation, we need to add two files to the project. Download NSArray+Reverse, unzip it, and drag both files into your project. It's simply a category on NSMutableArray that gives us a reverse method. Now, open GameLayer.m, import Segment.h and NSArray+Reverse.h, and implement the following method:

  1. If we hit a single segment caterpillar (just a head), we remove that caterpillar from the game and convert it to a sprout.
  2. Remove the sprite of the segment that was hit from the batch node.
  3. Convert the hit segment to a sprout (we will implement this method momentarily).
  4. We need to split the caterpillar into two arrays, the head section and the tail section.
  5. Determine where the other segments fall (head or tail section) and add them to the appropriate arrays.
  6. Check if there are any tail segments.
  7. Reverse the tail segments array. This is a category on NSMutableArray that was mentioned above. We need to reverse the segments in order to move the caterpillar in the opposite direction.
  8. Create a new caterpillar object using the tail section. We will implement this method momentarily.
  9. This is the guts of this method. Here we determine the current direction (left or right) and current overall direction (up or down) of the caterpillar that was hit in order to send the new caterpillar in the opposite direction.
  10. If there is still a head left, we just set the caterpillar that was hit's segments to the remaining head segments.

Wow, that was a lot to take in. Are you still with me? There are a couple methods we used here and in the previous code that still need to be implemented. The first method to implement is createSproutAtPosition:. Add the following method declaration to your private interface at the top of GameLayer.m:

Now, implement the following method:

  1. This translates our position on the screen coordinates to grid coordinates. Using our 8th grade algebra skillz, we derive this from the positioning code written in part 2.
  2. Create a new sprout and add it to the game.

The last method we need to implement to make this all work is initWithGameLayer:segments:level: in the caterpillar class. This method will be responsible for building a new caterpillar from the segments that were passed in. Open up Caterpillar.h and add the following method declaration:

Now, open Caterpillar.m and implement the following method:

This method is almost identical to our previous initWithGameLayer:level:position: method. The only difference is rather than allocating an array of segments, it sets the segments array to the incoming segments that are passed in.

Go ahead and run the game at this stage. You should be able to fully kill the caterpillar that's in play.

Caterpillar Killer

Step 3: Player Caterpillar Collision

The final thing we need to to complete the collision detection circle of life is to implement the collisions between the caterpillar and the player. If any part of the caterpillar hits the player, we want to reduce the player's number of lives. In addition to that, the player will become invincible for a brief moment, so that the caterpillar doesn't just blaze right through him.

Start by opening GameConfig.h and adding the following option:

Now, open up Caterpillar.m, import Player.h and add the following code to the bottom of the update: method just before you set the caterpillar's position:

  1. Enumerate each of the segments to determine if they collide with the player.
  2. If the player was hit, decrement their lives and post the notification. This should automatically be reflected in the UI based on the code your wrote in part 3.
  3. If the player was hit, check if they were still in the invincible period. If not, reset the invincible counter so they are able to get hit again.

Now, run the game and let the caterpillar hit you. It should remove one of your lives from the top of the screen.


Conclusion

Collision detection is never an easy task and we have only scratched the surface. If you want to dig deeper into collision detection, take a look at using Box2D with Cocos2D.


Next Time

In the next and final tutorial of the series, we will discuss the game polish. This will include the winning conditions, score, sounds, game over, and high score.

Happy Coding!

Tags:

Comments

Related Articles