How To Create A Breakout Game with Box2D and Cocos2D 2.X Tutorial: Part 2

Brian Broom Brian Broom
Simple Breakout Game Screenshot

Simple Breakout Game Screenshot

Update 1/9/2013: Fully updated for Cocos2D 2.1-beta4 (original post by Ray Wenderlich, update by Brian Broom).

This is the second and final part of a tutorial on how to create a simple breakout game using the Box2D physics library that comes with Cocos2D. If you haven’t already, make sure you go through part 1 first!

We left off with a box that bounces around the screen and a paddle we could move with our fingers. Let’s start adding in some game logic by making the player lose if the ball hits the bottom of the screen!

Box2D and Collisions

To find out when a fixture collides with another fixture in Box2D, we need to register a contact listener. A contact listener is a C++ object that we give Box2D, and it will call methods on that object to let us know when two objects begin to touch and stop touching.

The trick to a contact listener, however, is according to the Box2D User Manual, you cannot perform any operation that alters game physics within the callback. Since this is something we will probably want to do (such as destroy an object when two objects collide), instead we will just keep references to the collisions so we can deal with them later.

Another tricky bit is we can’t just store references to the contact points that are sent to the listener, because they are reused by Box2D. So we have to store copies of them instead.

Ok enough talk, let’s try this out for ourselves!

When We’ve Hit Rock Bottom

Note that in this section we’re going to be using some C++ and the standard template library (STL) a bit. If you are unfamiliar with C++ or the STL, don’t worry about it too much – you can just copy and paste the code, it is general purpose and should work in your projects as well.

Ok. From the menu, add a new file (File\New File), click “Cocoa Touch” on the left, and choose “Objective-C class”, and click Next. Name your class MyContactListener, verifying that NSObject is selected in the “Subclass of” field, then click Next. Make sure “Box2DBreakout” is checked in the list of targets at the bottom of the window, and click Create.

Click to select MyContactListener.m and click again to rename the file to MyContactListener.mm. This is because we are actually creating a C++ class in this file, and the convention when you are using C++ in a file is to have the file end with mm.

Then replace the contents of MyContactListener.h with the following file:

#import "Box2D.h"
#import <vector>
#import <algorithm>
 
struct MyContact {
    b2Fixture *fixtureA;
    b2Fixture *fixtureB;
    bool operator==(const MyContact& other) const
    {
        return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);
    }
};
 
class MyContactListener : public b2ContactListener {
 
public:
    std::vector<MyContact>_contacts;
 
    MyContactListener();
    ~MyContactListener();
 
    virtual void BeginContact(b2Contact* contact);
    virtual void EndContact(b2Contact* contact);
    virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);    
    virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);
 
};

Here we define the structure that we will use to keep track of the data we’re interested in from the contact notifications. Again, we need to store a copy because the contact points passed in are reused. note we have to declare an equality operator here, because we’re going to use a the find() method to look for matching objects in the vector, which requires this method.

After that we declare our contact listener class, which derives from b2ContactListener. We just declare the methods we need to implement, as well as a STL vector that we will use to buffer our contact points.

Now replace the contents of MyContactListener.mm with the following:

#import "MyContactListener.h"
 
MyContactListener::MyContactListener() : _contacts() {
}
 
MyContactListener::~MyContactListener() {
}
 
void MyContactListener::BeginContact(b2Contact* contact) {
    // We need to copy out the data because the b2Contact passed in
    // is reused.
    MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };
    _contacts.push_back(myContact);
}
 
void MyContactListener::EndContact(b2Contact* contact) {
    MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };
    std::vector<MyContact>::iterator pos;
    pos = std::find(_contacts.begin(), _contacts.end(), myContact);
    if (pos != _contacts.end()) {
        _contacts.erase(pos);
    }
}
 
void MyContactListener::PreSolve(b2Contact* contact, 
  const b2Manifold* oldManifold) {
}
 
void MyContactListener::PostSolve(b2Contact* contact, 
  const b2ContactImpulse* impulse) {
}

We initialize our vector in the constructor. Then the only two methods we actually implement are BeginContact and EndContact. In BeginContact we make a copy of the fixtures that just collided, and store them in our vector. In EndContact, we look to see if the contact point is in our vector and remove it if so.

Ok, now let’s put this to use. Switch over to HelloWorldLayer.h and make the following modifications:

// Add to top of file
#import "MyContactListener.h"
 
// Add inside @interface
MyContactListener *_contactListener;

Then add the following code to your init method:

// Create contact listener
_contactListener = new MyContactListener();
_world->SetContactListener(_contactListener);

Here we create our contact listener object, and call a method on the world object to set the contact listener.

Next add the cleanup code to dealloc before we forget:

delete _contactListener;

And finally add the following code to the bottom of your tick method:

//check contacts
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin(); 
  pos != _contactListener->_contacts.end(); ++pos) {
    MyContact contact = *pos;
 
    if ((contact.fixtureA == _bottomFixture && contact.fixtureB == _ballFixture) ||
        (contact.fixtureA == _ballFixture && contact.fixtureB == _bottomFixture)) {
        NSLog(@"Ball hit bottom!");
    }
}

This iterates through all of the buffered contact points, and checks to see if any of them are a match between the ball and the bottom of the screen. For now, we just log this out with a NSLog message because it’s time to check if it’s working!

So run your project, and make sure your console is visible by selecting View -> Debug Area -> Activate Console in the menu. Whenever the ball intersects the bottom you should see a message in your log that reads “Ball hit bottom!”

Adding a game over scene

Add the GameOverLayer.h and GameOverLayer.m files that we developed in the how to make a simple game with Cocos2D tutorial. Note that you’ll have to rename GameOverLayer.m to GameOverLayer.mm since we’re dealing with C++ code now or you will get compilation errors.

Then add the import to the top of your HelloWorldLayer.mm file:

#import "GameOverLayer.h"

Then replace the NSLog statement with the following code:

CCScene *gameOverScene = [GameOverLayer sceneWithWon:NO];
[[CCDirector sharedDirector] replaceScene:gameOverScene];

Allright, we’re getting somewhere! But what fun is a game where you can’t win?

Adding some blocks

Download a copy of a block image and retina version I made and drag it to the Resources folder of your project, making sure “Copy items into destination group’s folder (if needed)” is checked.

Then add the following code to your init method:

for(int i = 0; i < 4; i++) {
 
    static int padding=20;
 
    // Create block and add it to the layer
    CCSprite *block = [CCSprite spriteWithFile:@"block.png"];
    int xOffset = padding+block.contentSize.width/2+
      ((block.contentSize.width+padding)*i);
    block.position = ccp(xOffset, 250);
    block.tag = 2;
    [self addChild:block];
 
    // Create block body
    b2BodyDef blockBodyDef;
    blockBodyDef.type = b2_dynamicBody;
    blockBodyDef.position.Set(xOffset/PTM_RATIO, 250/PTM_RATIO);
    blockBodyDef.userData = block;
    b2Body *blockBody = _world->CreateBody(&blockBodyDef);
 
    // Create block shape
    b2PolygonShape blockShape;
    blockShape.SetAsBox(block.contentSize.width/PTM_RATIO/2,
                        block.contentSize.height/PTM_RATIO/2);
 
    // Create shape definition and add to body
    b2FixtureDef blockShapeDef;
    blockShapeDef.shape = &blockShape;
    blockShapeDef.density = 10.0;
    blockShapeDef.friction = 0.0;
    blockShapeDef.restitution = 0.1f;
    blockBody->CreateFixture(&blockShapeDef);
 
}

You should understand this code pretty well by now. We create a body just the same way we did for the paddle, except this time we do it in a loop so we can easily create four blocks along the top. Also notice that we set the tag on the block sprite to 2, for future reference.

Compile and run this code, and you should now have blocks you can mess around with your ball!

Destroying the Blocks

To be a true breakout game, we need to destroy the blocks when the ball intersects them. Well we’ve already added the code to keep track of collisions, so all we need to do is modify the tick method!

Modify the code in your tick method after the “check contacts” comment to be the following:

//check contacts
std::vector<b2Body *>toDestroy;
    std::vector<MyContact>::iterator pos;
    for (pos=_contactListener->_contacts.begin();
         pos != _contactListener->_contacts.end(); ++pos) {
        MyContact contact = *pos;
 
        if ((contact.fixtureA == _bottomFixture && contact.fixtureB == _ballFixture) ||
            (contact.fixtureA == _ballFixture && contact.fixtureB == _bottomFixture)) {
            //NSLog(@"Ball hit bottom!");
            CCScene *gameOverScene = [GameOverLayer sceneWithWon:NO];
            [[CCDirector sharedDirector] replaceScene:gameOverScene];
        }
 
        b2Body *bodyA = contact.fixtureA->GetBody();
        b2Body *bodyB = contact.fixtureB->GetBody();
        if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
            CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
            CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
 
            //Sprite A = ball, Sprite B = Block
            if (spriteA.tag == 1 && spriteB.tag == 2) {
                if (std::find(toDestroy.begin(), toDestroy.end(), bodyB) == toDestroy.end()) {
                    toDestroy.push_back(bodyB);
                }
            }
 
            //Sprite A = block, Sprite B = ball
            else if (spriteA.tag == 2 && spriteB.tag == 1) {
                if (std::find(toDestroy.begin(), toDestroy.end(), bodyA) == toDestroy.end()) {
                    toDestroy.push_back(bodyA);
                }
            }
        }
    }
 
    std::vector<b2Body *>::iterator pos2;
    for (pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
        b2Body *body = *pos2;
        if (body->GetUserData() != NULL) {
            CCSprite *sprite = (CCSprite *) body->GetUserData();
            [self removeChild:sprite cleanup:YES];
        }
        _world->DestroyBody(body);
    }

Ok, let’s explain this. We go through the contact points again, but this time after we check for collisions between the ball and the bottom of the screen, we take a look at the bodies that are colliding. We can get to the bodies by calling the GetBody() method on the fixtures.

Once we have the bodies, we check to see if they have user data. If they do, we cast them to sprites – because we know that’s what we’ve set the user data to.

Then we look to see what sprites are colliding based on their tags. If a sprite is intersecting with a block, we add the block to a list of objects to destroy.

Note that we add it to a list to destroy rather than destroying the body right away. This is because if we destroy the body right away, the world will clean up a lot of pointers leaving us with garbage data in our contact listener. Also note that we only should add it to the list if it isn’t there already!

Finally, we go through the list of bodies we want to delete. Note that we not only have to destroy the body from Box2D’s world, we also have to remove the sprite object from our Cocos2D scene.

Give it a compile and run, and you should now be able to destroy bricks! Yay!

Winning the Game

Next we need to add some logic in to let the user actually win the game. Modify the beginning of your tick method to read as follows:

- (void)tick:(ccTime) dt {
 
    bool blockFound = false;
    _world->Step(dt, 10, 10);    
    for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {    
        if (b->GetUserData() != NULL) {
            CCSprite *sprite = (CCSprite *)b->GetUserData();     
            if (sprite.tag == 2) {
                blockFound = true;
            }
//...

All we’re doing here is looking to see if we ever come across a block while we’re iterating through the objects in the scene – if we do find one we set the blockFound variable to true – otherwise it is false.

Then add the following code at the end of the function:

if (!blockFound) {
    CCScene *gameOverScene = [GameOverLayer sceneWithWon:YES];
	[[CCDirector sharedDirector] replaceScene:gameOverScene];
}

Here we just display a game over scene if no blocks were found. Give it a compile and run, and see if you can win the game!

You Win Screenshot

Finishing Touches

The game is quite cool, but we need some sound of course! You can download the awesome background music I made and a cool blip sound I made to use. As usual, drag them to your resources folder once you’ve downloaded them.

Note: When I dragged the sound files into the project, they were not automatically added to the build target, which crashed the app when I ran it. When you drag the files into your project, there is a checkbox at the bottom of the window for the files to be included in the target. You can check this setting by selecting the audio file (or any file, actually) and looking in the File Inspector in the right sidebar. There is a section called “Target Membership”, and there is a checkbox next to your app name. Check the box if it is not checked.

Check the target membership setting for the audio files.

By the way – I made the sound effect with an awesome program called cfxr that one of our commenters – Indy – pointed out. Thanks Indy this program pwns!

Anyway – once you’ve added the files to your project, add the following to the top of HelloWorldScene.mm:

#import "SimpleAudioEngine.h"

And the following to your init method:

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"];

And finally the following at the end of your tick method:

if (toDestroy.size() > 0) {
    [[SimpleAudioEngine sharedEngine] playEffect:@"blip.caf"];   
}

And there you have it – your own simple breakout game with Box2D physics!

Gimme The Code!

Here’s the full code for the Cocos2D and Box2D Breakout Game that we’ve made in this tutorial.

Where To Go From Here?

Obviously this is a quite simple implementation of breakout, but now that you have this working there’s a lot more you can do. You could extend this code to give the blocks hit points and make the ball have to hit them a number of times before they are destroyed. You could add new blocks, let the paddle shoot lasers toward the blocks, whatever you dream up!

Let me know if you have any tips or suggestions for better ways to do things, and hope this comes in handy!

Brian Broom
Brian Broom

Brian has been tinkering with computers since writing basic programs on IBM PC (with Two! floppy drives). He has done web, database, C++, ruby, and now iOS development. Brian has spent the last few years as a computer science teacher and trainer.

You can reach him by email or on Twitter.

User Comments

11 Comments

  • Great tutorial website!

    @rwenderlich what is the typical procedure for yourself and your tutorial team for learning how to implement specific features (ex: how to create a server for a multiplayer game) for your tutorials? The reason I ask is because I am new to coding and often find myself having a difficult time trying to use your tutorials as a base and build upon them to implement the specific features I need. Tips in the right direction would be very helpful. I plan on being fruitful and adding to your tutorials and I believe that's why you have your website setup.

    Thanks for your reply
    hype96
  • I see this as two different questions, and I'll answer them in terms of what I have done as someone newish to iOS. I can't answer for any of the other team members.

    1) How did I learn the specific features to write the tutorial?

    I look around for other information. Are there other tutorials on related things? Is there a website for the library I want to use? Does it have examples and documentation? I tend to start with making something almost trivial work, and then add things to it as I try to figure things out. Sometimes there are other tutorials or starting points that can help me get started. When something doesn't work, I google the error or the problem and try to work out the issues that way (sites like stackoverflow.com can be very helpful). At some point I feel like I have enough worked out to be a viable lesson.

    2) How do you extend your knowledge once you have a tutorial mastered?

    This is hardener in some ways. Here are some things that I try to do. Can I (mostly) reproduce the tutorial items on my own? Do I know what the parts do? Can I add something slightly different (different image, color, shape, direction, etc)? Can I put two on the screen at the same time?

    At this point I'd suggest trying to find the online documentation for the topic you've been working on. If its something with Cocos2D, can you find the class reference in the documentation, or the Apple documentation for iOS classes. What other methods does the class have, and can I get those to work? etc. I have found the Apple documentation as a whole to be pretty readable, so don't be afraid to look these up.

    I think the hardest part is when you try to integrate the knowledge you have from several tutorials together into one idea. The only suggestion I have is to give it a try, and look for answers to issues that come up.

    I'm not sure I've answered you very well, but hope it helps some.
    Brian Broombcbroom
  • awesome job brian on the updates, can't wait to go through them!
    Charlie Fultoncharlie
  • Thanks a lot for your reply Brian. And I do agree, trying to integrate what you learned from multiple tutorials is the hardest part
    hype96
  • Thanks for the updated tutorials. They will prove to be very helpful.
    Orochi_X
  • I finish tutorial : breakout game with box2d
    But, when Ball collide blocks, velocity of blocks reduce. So Why??
    HeavenHell
  • Hello

    I am trying to port this to Mac and when I do, I always get errors with the contact listener, is there something different I need to do for Mac, basically I want to use Cocos2d and Box2d to detect collision (specific collisions) so I can do something when 2 certain bodies/sprites collide.

    Thanks Frahaan Hussain
    Frahaan
  • I am having a memory management problem, running this product, converted to ARC. I followed the steps suggested in other tutorials on the site converting the initial Box2D template to arc then proceeding with the tutorial, but the problem comes in when I need to hold onto the CCSprites in the userData. I have the choice to use CFBridgingRetain/Release functions or the __bridge qualifier. I've tried it in various combinations and still get EXC_BAD_ACCESS errors at points of contact, either with one of the blocks in the game or with the paddle. I tried Profiling with Zombies, and only get confirmation that it's releasing the object one time too many. Any general rules of thumb for hanging on to these sprites in userData, in ARC?
    howardor
  • Hi. I'm trying to make an app where I have a player that jumps across platforms trying to avoid falling off the screen, and for this I need a way to create random shaped rectangles. Everywhere I've looked people just use sprites with image files, is there any way to create a rectangle without using an image, and to make it have physical properties?
    Zepsilon
  • Nice tutorial!

    I ran into a rather strange problem where my blocks were not getting evenly distributed -- even when your sample app worked correctly and I copied-and-pasted all of your code directly into my app. It turns out I fixed it by changing the line:

    blockBodyDef.position.Set(xOffset/PTM_RATIO, 250/PTM_RATIO);

    to...

    blockBodyDef.position.Set((float)xOffset/PTM_RATIO, 250.0f/PTM_RATIO);

    Not sure why it's not consistently working (or broken) across both, but there ya go...
    toddarooski
  • tadrooski in your header file just replace #define PTM_RATIO 32 with #define PTM_RATIO 32.0

    Most of people get this problem as standard template use 32 which when later define with another number so the division also returns integer which can be bad in most cases. Thats why your explicit conversion to float make it work
    Sheikh Umair

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Jake Gundersen

... 50 total!

Update Team

  • Riccardo D'Antoni

... 14 total!

Editorial Team

... 23 total!

Code Team

  • Orta Therox

... 3 total!

Translation Team

  • David Hidalgo
  • Cosmin Pupaza
  • David Xie

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!