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

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).

Box2D is a powerful physics library that comes with the Cocos2D game programming library for the iPhone. There’s a lot you can do with it, and a great way to start learning about how it works is to create a simple game with it!

In this tutorial, we are going to create a simple Breakout game step by step, complete with collision detection, a ball bouncing with physics effects, dragging the paddle via touches, and win/lose screens. (Jump to part two of the series.)

If you are new to Cocos2D or Box2D, it may help to go through the intro to Cocos2D tutorial and/or intro to Box2D tutorial before proceeding with this tutorial.

Allright, time for some Breakout!

An Ever-Bouncing Ball

Start by creating a new project with the cocos2d v2.x cocos2d iOS with Box2d template, and name your project “Box2DBreakout”. Clear out the template code and so you have an empty project to start with – see the intro to Box2D tutorial for instructions on how to do that.

Once you have a nice clean project, add the following import to the top of HelloWorldLayer.h:

#import "Box2D.h"

And add the following member variables to the HelloWorldLayer class:

b2World *_world;
b2Body *_groundBody;
b2Fixture *_bottomFixture;
b2Fixture *_ballFixture;

Just as in the previous tutorial, Box2D uses an internal unit of “meters” instead of pixels. We will use the same ratio to convert from pixels to “meters” that we discussed then, and this should already be defined in HelloWorldLayer.h.

Then add the following code to your init method of HelloWorldLayer.mm:

CGSize winSize = [CCDirector sharedDirector].winSize;
 
// Create a world
b2Vec2 gravity = b2Vec2(0.0f, 0.0f);
_world = new b2World(gravity);
 
// Create edges around the entire screen
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
_groundBody = _world->CreateBody(&groundBodyDef);
 
b2EdgeShape groundBox;
b2FixtureDef groundBoxDef;
groundBoxDef.shape = &groundBox;
 
groundBox.Set(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
_bottomFixture = _groundBody->CreateFixture(&groundBoxDef);
 
groundBox.Set(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
 
groundBox.Set(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 
    winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
 
groundBox.Set(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), 
    b2Vec2(winSize.width/PTM_RATIO, 0));
_groundBody->CreateFixture(&groundBoxDef);

Again, this is the same code that we had in our previous Box2D tutorial to create a bounding box around the screen. However, this time we set the gravity to zero, because in our breakout game there will not be any gravity! Also note we store a pointer to the bottom fixture for future reference (we’ll need it to keep track of when the ball hits the bottom of the screen).

Now download a copy of the image of a bouncy ball, and a retina version I created and drag it into the Resources folder of your project, making sure “Copy items into destination group’s folder (if needed)” is checked.

Let’s add a sprite for the ball into the layer. Add the following right after the last bit of code you added:

// Create sprite and add it to the layer
CCSprite *ball = [CCSprite spriteWithFile:@"ball.png"];
ball.position = ccp(100, 100);
ball.tag = 1;
[self addChild:ball];

There should be no surprises here, we’ve been doing this for a while now. Note that we set a tag on the ball for identification purposes (you’ll see why later in the tutorial).

Next let’s create a body for the shape:

// Create ball body 
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(100/PTM_RATIO, 100/PTM_RATIO);
ballBodyDef.userData = ball;
b2Body * ballBody = _world->CreateBody(&ballBodyDef);
 
// Create circle shape
b2CircleShape circle;
circle.m_radius = 26.0/PTM_RATIO;
 
// Create shape definition and add to body
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density = 1.0f;
ballShapeDef.friction = 0.f;
ballShapeDef.restitution = 1.0f;
_ballFixture = ballBody->CreateFixture(&ballShapeDef);

This should look familiar as well from our last tutorial. As a refresher, to create a body we need to create a body definition, then a body object, then a shape, then a fixture definition, and finally a fixture object.

Note that we set the parameters a bit differently this time: we’ve set the restitution to 1.0, meaning the when the ball collides with an object the collision will be perfectly elastic. In plain English, this means that the ball will bounce back with equal force to the impact.

Also note that we store the ball fixture for future reference (same reason as why we stored the bottom fixture).

Update: Also note that the ball is set to have no friction. Thanks to Steve Oldmeadow for pointing out that this is important in this case so that the ball bounces nicely off the walls, preventing the ball from frequently getting stuck bouncing back and forth in a straight up-down or left-right angle.

Ok, now to get the ball rolling (bouncing, actually). Add the following after the above:

b2Vec2 force = b2Vec2(10, 10);
ballBody->ApplyLinearImpulse(force, ballBodyDef.position);

This applies an impulse (you can think of it like a propulsion from a jet pack thruster) to the ball to get it to start moving in a particular direction (in this case, diagonally up to the right). We need this to get the ball moving in the first place!

One last thing for the init method: add the tick scheduling:

[self schedule:@selector(tick:)];

And then the tick method itself!

- (void)tick:(ccTime) dt {
    _world->Step(dt, 10, 10);    
    for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {    
        if (b->GetUserData() != NULL) {
            CCSprite *sprite = (CCSprite *)b->GetUserData();                        
            sprite.position = ccp(b->GetPosition().x * PTM_RATIO,
                                    b->GetPosition().y * PTM_RATIO);
            sprite.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
        }        
    }
 
}

Again no surprises with these two since we did the same thing in the last tutorial.

One last thing and then we’re ready to try it out: the cleanup method!

- (void)dealloc {
 
    delete _world;
    _groundBody = NULL;
    [super dealloc];
 
}

Ok, let’s try it out! When you compile and run the project, you should see a ball continuously bouncing around the screen – cool!

Ball Bouncing Screenshot

Adding the Paddle

It wouldn’t be a breakout game if we didn’t have a paddle. Download a copy of a graphic of a paddle, and the corresponding 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 member variable to HelloWorld in HelloWorldLayer.h:

b2Body *_paddleBody;
b2Fixture *_paddleFixture;

And then construct the paddle body in your init method:

// Create paddle and add it to the layer
CCSprite *paddle = [CCSprite spriteWithFile:@"paddle.png"];
paddle.position = ccp(winSize.width/2, 50);
[self addChild:paddle];
 
// Create paddle body
b2BodyDef paddleBodyDef;
paddleBodyDef.type = b2_dynamicBody;
paddleBodyDef.position.Set(winSize.width/2/PTM_RATIO, 50/PTM_RATIO);
paddleBodyDef.userData = paddle;
_paddleBody = _world->CreateBody(&paddleBodyDef);
 
// Create paddle shape
b2PolygonShape paddleShape;
paddleShape.SetAsBox(paddle.contentSize.width/PTM_RATIO/2, 
                     paddle.contentSize.height/PTM_RATIO/2);
 
// Create shape definition and add to body
b2FixtureDef paddleShapeDef;
paddleShapeDef.shape = &paddleShape;
paddleShapeDef.density = 10.0f;
paddleShapeDef.friction = 0.4f;
paddleShapeDef.restitution = 0.1f;
_paddleFixture = _paddleBody->CreateFixture(&paddleShapeDef);

I’m not going to explain this much because you should be a pro at creating bodies by this point. However, note a few differences this time:

  • When you create a CCSprite, you don’t need to specify the size of the sprite if you don’t want to. If you give it the filename, it can automatically determine the size.
  • Note that instead of using a circle shape, we use a polygon shape this time. We use a helper method to create the shape in the form of a box. Note: the height and width of the box are measured from the center point, so we use half of the actual values.
  • Note that there is an alternate SetAsBox method that allows you to specify the position of the shape relative to the body, which comes in handy when constructing complex shapes. However we don’t need to use that here, since we just want the shape centered on the body.
  • We make the paddle more dense than the ball, and tweak the other parameters as well.
  • We are storing paddleBody and paddleFixture for future reference.

If you compile and run this you’ll see our paddle in the scene, and the ball will bounce off it:

Paddle Added Screenshot

However this isn’t much fun, because we can’t move paddle yet!

Moving The Paddle

So let’s get moving! Moving the paddle is going to require touches, so enable touches in your init method:

self.touchEnabled = YES;

Then add the following member variable to your HelloWorld class in HelloWorldLayer.h:

b2MouseJoint *_mouseJoint;

Now let’s implement the touch methods! Let’s start with ccTouchesBegan:

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 
    if (_mouseJoint != NULL) return;
 
    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
 
    if (_paddleFixture->TestPoint(locationWorld)) {
        b2MouseJointDef md;
        md.bodyA = _groundBody;
        md.bodyB = _paddleBody;
        md.target = locationWorld;
        md.collideConnected = true;
        md.maxForce = 1000.0f * _paddleBody->GetMass();
 
        _mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md);
        _paddleBody->SetAwake(true);
    }
 
}

Wow, a lot of new stuff in here. Let’s discuss it bit by bit.

First, we convert the touch location to our Cocos2D coordinates (convertToGL) and then to our Box2D coordinates (locationWorld).

Then we use a method on our paddle fixture object that we’ve stored away to see if the touch point is within the fixture.

If it is, we create something called a “mouse joint.” In Box2D, a mouse joint is used to make a body move toward a specified point – in this case where the user is tapping.

When you set up a mouse joint, you have to give it two bodies. The first isn’t actually used, but the convention is to use the ground body. The second is the body you want to move – in our case the paddle.

Then you specify where you want the target to move – in our case where the user is tapping.

Then you tell Box2D that when bodyA and bodyB collide, treat it as a collision, rather than ignoring it. This is very important. When I was trying to get this working, I didn’t have this set, so when I was moving the paddle with my mouse it wouldn’t collide with the edges of the screen, and my paddle would fly off screen sometimes! This was very confusing and frustrating until I discovered this simple way to fix it :]

You then specify the max force with which to move the body. If you reduce this amount, the body will react more slowly to your mouse movements (which may be what you want sometimes!). But here we want the paddle to respond rather quickly to movements.

Finally we add the joint to the world, and store away the pointer for future reference. We also set the body to awake. We need to do this because if the body is asleep and we don’t awaken it, it won’t respond to the movements!

Ok, next let’s add the ccTouchesMoved method:

-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
 
    if (_mouseJoint == NULL) return;
 
    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
 
    _mouseJoint->SetTarget(locationWorld);
 
}

The beginning of this method is the same as ccTouchesBegan – we get the location of the touch in Box2D coordinates. The only thing we do here is update the target of the mouse joint (i.e. where we want the body to move) to be the current location of the touch.

Let’s wrap up by adding ccTouchesEnded and ccTouchesCancelled:

-(void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
 
    if (_mouseJoint) {
        _world->DestroyJoint(_mouseJoint);
        _mouseJoint = NULL;
    }
 
}
 
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if (_mouseJoint) {
        _world->DestroyJoint(_mouseJoint);
        _mouseJoint = NULL;
    }  
}

All we do in these methods is destroy the mouse joint because when the touches end, we’re done moving the object.

Give it a compile and run, and you should be able to move the paddle all around the screen to bounce the ball!

Paddle Moving Screenshot

Pretty cool… but wait a minute, this isn’t breakout, we shouldn’t be able to move the paddle anywhere we want, we should just be able to move it back and forth!

Restricting Movement of the Paddle

We can easily restrict movement of the paddle by adding another joint into the world called a prismatic joint. This lets us restrict the movement of one body to another along a specified axis.

So we can use this to restrict the movement of the paddle relative to the ground to only be able to move along the x-axis.

Let’s give this a shot in code. Add this to your init method:

// Restrict paddle along the x axis
b2PrismaticJointDef jointDef;
b2Vec2 worldAxis(1.0f, 0.0f);
jointDef.collideConnected = true;
jointDef.Initialize(_paddleBody, _groundBody, 
  _paddleBody->GetWorldCenter(), worldAxis);
_world->CreateJoint(&jointDef);

The first thing we do is specify the axis to be a vector along the x axis, but not at all along the y axis. We then specify the ever important collideConnected value so our paddle will correctly bounce against the edge of the screen rather than flying into never-never land.

We then initialize the joint specifying the paddle and the ground body and create the joint!

Give it a compile and run and now you should only be able to move the paddle back and forth instead of anywhere you want:

Paddle Restricted Screenshot

Finishing Touches

Now, as you’ve been playing around with this so far you may have noticed that sometimes the ball can get super-fast or super slow, depending on how you hit it with the paddle.

Update: The first time I tried to fix this, I tried to adjust the velocity of the ball directly by calling SetLinearVelocity. However, as Steve Oldmeadow also pointed out (thanks Steve!), this is a bad idea as it messes up the collision simulations, and it’s better to indirectly affect the velocity by increasing the linear damping. So that’s what we’ll do!

Add the following code to the tick method, after getting the user data:

// if ball is going too fast, turn on damping
if (sprite.tag == 1) {
    static int maxSpeed = 10;
 
    b2Vec2 velocity = b->GetLinearVelocity();
    float32 speed = velocity.Length();
 
    if (speed > maxSpeed) {
        b->SetLinearDamping(0.5);
    } else if (speed < maxSpeed) {
        b->SetLinearDamping(0.0);
    }
 
}

Here I check the tag of the sprite to see if it’s the tag for the ball object. If it is, I check the velocity and if it’s too too large, I increase the linear damping so it will eventually slow down.

If you compile and run you should see the ball goes back to a normal rate when the speed increases too much.

Gimme The Code!

Here’s the full code for the Cocos2D and Box2D Breakout Game that we’ve developed up to this point. More is coming in the next portion of the series!

What’s Next?

So far, we have a ball that bounces around the screen and a paddle we can move around via touch. In the next tutorial in the series, we pick it up from here and add some bricks that get destroyed when the ball collides into them, and some win/lose logic!

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

7 Comments

  • Dear Sir,
    Thanks for your nice tutorials,
    I am working on this breakout with ARC enabled i faced a few problems I solved some but when it gets to touches began
    The project runs fine but as soon I click it gives error and takes me direct to GetMass
    It highlights the (return m_mass) in b2Body header file

    If you can help me with this
    khalid
  • I'll admit this is not something I looked into when updating the tutorials. I know that there are some issues with ARC and Cocos2d, and there is a tutorial on getting Cocos to work with ARC here http://www.raywenderlich.com/23854/arc-and-cocos2d-v2-x. You might want to start there if you haven't already.

    I'm not sure if Box2D being in c++ causes any issues with ARC.
    Brian Broombcbroom
  • here i am going to paste all the code which is in Hello world layer some i do have if you please have a quick look at this. i wanted to upload the project but the max size for the file which is allowed is 256KB so i was not able to upload the project.

    but if you want to have a quick look to the project i can email it to you but i don't have your email address here is my email
    ( afridikhalid@yahoo.com )

    // Import the interfaces
    #import "HelloWorldLayer.h"

    // Needed to obtain the Navigation Controller
    #import "AppDelegate.h"

    #import "PhysicsSprite.h"


    #pragma mark - HelloWorldLayer

    @interface HelloWorldLayer()
    @end

    @implementation HelloWorldLayer

    +(CCScene *) scene
    {
    // 'scene' is an autorelease object.
    CCScene *scene = [CCScene node];

    // 'layer' is an autorelease object.
    HelloWorldLayer *layer = [HelloWorldLayer node];

    // add layer as a child to scene
    [scene addChild: layer];

    // return the scene
    return scene;
    }

    -(id) init
    {
    if( (self=[super init])) {

    // enable events
    CGSize winSize = [CCDirector sharedDirector].winSize;

    self.isTouchEnabled = YES;


    // Create a world
    b2Vec2 gravity = b2Vec2(0.0f, 0.0f);
    _world = new b2World(gravity);

    // Create edges around the entire screen
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0, 0);
    _groundBody = _world->CreateBody(&groundBodyDef);

    b2EdgeShape groundBox;
    b2FixtureDef groundBoxDef;
    groundBoxDef.shape = &groundBox;

    groundBox.Set(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
    _bottomFixture = _groundBody->CreateFixture(&groundBoxDef);

    groundBox.Set(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
    _groundBody->CreateFixture(&groundBoxDef);

    groundBox.Set(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO));
    _groundBody->CreateFixture(&groundBoxDef);

    groundBox.Set(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0));
    _groundBody->CreateFixture(&groundBoxDef);


    // Create sprite and add it to the layer
    CCSprite *ball = [CCSprite spriteWithFile:@"ball.png"];
    ball.position = ccp(100, 100);
    ball.tag = 1;
    [self addChild:ball];


    // Create ball body
    b2BodyDef ballBodyDef;
    ballBodyDef.type = b2_dynamicBody;
    ballBodyDef.position.Set(100/PTM_RATIO, 100/PTM_RATIO);
    b2Body *ballBody = _world->CreateBody(&ballBodyDef);
    ballBody->SetUserData((__bridge void*)ball);

    // Create circle shape
    b2CircleShape circle;
    circle.m_radius = 26.0 /PTM_RATIO;

    // Create shape definition and add to body
    b2FixtureDef ballShapeDef;
    ballShapeDef.shape = &circle;
    ballShapeDef.density = 1.0f;
    ballShapeDef.friction = 0.f;
    ballShapeDef.restitution = 1.0f;
    _ballFixture = ballBody->CreateFixture(&ballShapeDef);

    b2Vec2 force = b2Vec2(10,10);
    ballBody->ApplyLinearImpulse(force, ballBodyDef.position);


    // Create paddle and add it to the layer
    CCSprite *paddle = [CCSprite spriteWithFile:@"paddle.png"];
    paddle.position = ccp(winSize.width /2, 50);
    [self addChild:paddle];

    // Create paddle body
    b2BodyDef paddleBodyDef;
    paddleBodyDef.type = b2_dynamicBody;
    paddleBodyDef.position.Set(winSize.width/2/PTM_RATIO, 50/PTM_RATIO);
    b2Body *paddleBody = _world->CreateBody(&paddleBodyDef);
    paddleBody->SetUserData((__bridge void*)paddle);

    // Create paddle shape
    b2PolygonShape paddleShape;
    paddleShape.SetAsBox(paddle.contentSize.width/PTM_RATIO/2, paddle.contentSize.height/PTM_RATIO/2);

    // Create shape definition and add to body
    b2FixtureDef paddleShapeDef;
    paddleShapeDef.shape = &paddleShape;
    paddleShapeDef.density = 10.0f;
    paddleShapeDef.friction = 0.4f;
    paddleShapeDef.restitution = 0.1f;
    _paddleFixture = paddleBody->CreateFixture(&paddleShapeDef);


    [self schedule:@selector(tick:)];

    }
    return self;
    }


    -(void) dealloc
    {
    delete _world;
    _world = NULL;

    }

    -(void)tick:(ccTime) dt {
    _world->Step(dt, 10, 10);
    for (b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
    if (b->GetUserData() != NULL) {
    CCSprite *sprite = (__bridge CCSprite *)b->GetUserData();
    sprite.position = ccp(b->GetPosition().x * PTM_RATIO, b->GetPosition().y *PTM_RATIO);

    sprite.rotation = -1 *CC_RADIANS_TO_DEGREES(b->GetAngle());
    }
    }
    }


    -(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    if (_mouseJoint != NULL) return;
    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 locationWorld = b2Vec2(location.x / PTM_RATIO, location.y / PTM_RATIO);

    if (_paddleFixture->TestPoint(locationWorld)) {
    b2MouseJointDef md;
    md.bodyA = _groundBody;
    md.bodyB = _paddleBody;
    md.target = locationWorld;
    md.collideConnected = true;
    md.maxForce = 1000.0f *_paddleBody->GetMass();

    _mouseJoint = (b2MouseJoint*)_world->CreateJoint(&md);
    _paddleBody->SetAwake(true);

    }
    }



    -(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {
    if (_mouseJoint == NULL) return;

    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    b2Vec2 locationWorld = b2Vec2(location.x / PTM_RATIO, location.y / PTM_RATIO);

    _mouseJoint->SetTarget(locationWorld);
    }

    -(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    if (_mouseJoint) {
    _world->DestroyJoint(_mouseJoint);
    _mouseJoint = NULL;
    }
    }


    -(void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
    {
    if (_mouseJoint) {
    _world->DestroyJoint(_mouseJoint);
    _mouseJoint = NULL;
    }
    }
    khalid
  • Hi, nice tutorial!! The Box2D world is amazing!!!

    But I've a problem :)

    in ccTouchesBegan method the _paddleFixture->TestPoint check returns always false...

    I don't understand why...can you help me? thanks

    EDIT:
    I'm an idiot... :shock:
    I wrote the code of ccTouchesMoved in ccTouhcesCancelled and viceversa.
    Now all works perfect!!! :D
    Niccolò Passolunghinicopasso
  • How would I make only one sprite/body respond to touches using the mouseJoint. I would like the to move the ball only regardless where I am touching on the screen.
    cocoscoder
  • As for me, I'm using Cocos2dx v3.0 and everything is working fine (The translating code into C++ is quite easy too, thanks to the Cocos2dx dev team ^^).

    But now I'm facing a problem. My project is compiled and run fine on phone, but when testing on Windows 7 it always crash the very first time I click (which I believe a "touch").

    Debugger showed me that the whole b2mouseJoint seem not be initialized at all, and the paddleFixture->TestPoint always false is the cause of that... but I really don't get it: even if it is false, the program should run fine , just nothing happen to the paddle, right?

    Please give me some clue if you experienced this. Here's my code:

    -----------------------------------------------------------------

    #include "HelloWorldScene.h"

    Scene* HelloWorld::createScene()
    {
    // 'scene' is an autorelease object
    auto scene = Scene::create();

    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
    }

    // on "init" you need to initialize your instance
    bool HelloWorld::init()
    {
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
    return false;
    }

    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    // you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png",
    CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));

    closeItem->setPosition(Vec2(origin.x - closeItem->getContentSize().width/2 + visibleSize.width,
    origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label

    auto label = LabelTTF::create("Hello World", "Arial", 24);

    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
    origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    //auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    //sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    //this->addChild(sprite);

    // Add sprites of objects to the layer
    ball = Sprite::create("Ball.png");
    ball->setPosition((visibleSize.width - ball->getContentSize().width)/2,
    (visibleSize.height - ball->getContentSize().height)/2); // Center of the window
    this->addChild(ball);

    Sprite* paddle = Sprite::create("Paddle.png");
    paddle->setPosition((visibleSize.width - paddle->getContentSize().width)/2, 50); // Bottom of window
    this->addChild(paddle);

    // Create a world
    //b2Vec2 gravity = b2Vec2(0.0f, -9.8f); // Gravity of Earth
    b2Vec2 gravity = b2Vec2(0.0f, 0.0f); // Gravity of some God-live planets
    world = new b2World(gravity);

    // Ball body and shape
    b2BodyDef ballBodyDef;
    ballBodyDef.type = b2_dynamicBody;
    ballBodyDef.position = b2Vec2(ball->getPosition().x/PTM_RATIO,
    ball->getPosition().y/PTM_RATIO);
    ballBodyDef.userData = ball;
    body = world->CreateBody(&ballBodyDef);

    // Paddle body and shape
    b2BodyDef paddleBodyDef;
    paddleBodyDef.type = b2_dynamicBody;
    paddleBodyDef.position = b2Vec2(paddle->getPosition().x/PTM_RATIO,
    paddle->getPosition().y/PTM_RATIO);
    paddleBodyDef.userData = paddle;
    paddleBody = world->CreateBody(&paddleBodyDef);

    // Physical shape of objects. Better the same as their respective graphical shape
    b2CircleShape circle;
    circle.m_radius = (68.0/2)/PTM_RATIO;
    b2FixtureDef ballShapeDef;
    ballShapeDef.shape = &circle;
    ballShapeDef.density = 1.0f;
    ballShapeDef.friction = 0.2f;
    ballShapeDef.restitution = 1.0f;
    body->CreateFixture(&ballShapeDef);

    b2PolygonShape rect;
    rect.SetAsBox((paddle->getContentSize().width/PTM_RATIO)/2,
    (paddle->getContentSize().height/PTM_RATIO)/2);
    b2FixtureDef paddleShapeDef;
    paddleShapeDef.shape = ▭
    paddleShapeDef.density = 10.0f;
    paddleShapeDef.friction = 0.4f;
    paddleShapeDef.restitution = 0.1f;
    paddleFixture = paddleBody->CreateFixture(&paddleShapeDef);

    // Create edge around the entire screen
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0, 0);

    groundBody = world->CreateBody(&groundBodyDef);
    b2EdgeShape groundEdge;
    b2FixtureDef boxShapeDef;
    boxShapeDef.shape = &groundEdge;

    // Wall definition
    groundEdge.Set(b2Vec2(0, 0), b2Vec2(visibleSize.width/PTM_RATIO, 0));
    groundBody->CreateFixture(&boxShapeDef);
    groundEdge.Set(b2Vec2(0, 0), b2Vec2(0, visibleSize.height/PTM_RATIO));
    groundBody->CreateFixture(&boxShapeDef);
    groundEdge.Set(b2Vec2(visibleSize.width/PTM_RATIO, 0), b2Vec2(visibleSize.width/PTM_RATIO, visibleSize.height/PTM_RATIO));
    groundBody->CreateFixture(&boxShapeDef);
    groundEdge.Set(b2Vec2(0, visibleSize.height/PTM_RATIO), b2Vec2(visibleSize.width/PTM_RATIO, visibleSize.height/PTM_RATIO));
    groundBody->CreateFixture(&boxShapeDef);

    // Using acceleromater. The deprecated message for Scene isn't used here
    Device::setAccelerometerEnabled(true);
    auto accelerateListener = EventListenerAcceleration::create(CC_CALLBACK_2(HelloWorld::onAcceleration, this));
    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(accelerateListener, this);

    // Using touch
    auto touchListener = EventListenerTouchAllAtOnce::create();
    touchListener->onTouchesBegan = CC_CALLBACK_2(HelloWorld::onTouchesBegan, this);
    touchListener->onTouchesMoved = CC_CALLBACK_2(HelloWorld::onTouchesMoved, this);
    touchListener->onTouchesEnded = CC_CALLBACK_2(HelloWorld::onTouchesEnded, this);
    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);

    // Initialize interval vars
    tickInterval = 1.0/60.0;
    kickInterval = 2.0;

    // Update the graphic to the physic every 1/60 sec
    this->schedule(schedule_selector(HelloWorld::tick), tickInterval);

    // Apply force to the ball every 2 sec
    this->schedule(schedule_selector(HelloWorld::kick), kickInterval);

    return true;
    }

    void HelloWorld::menuCloseCallback(Ref* pSender)
    {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
    MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    return;
    #endif

    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
    #endif
    }

    void HelloWorld::tick(float dt) {
    world->Step(tickInterval, 10, 10);
    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
    if (b->GetUserData() != NULL) {
    Sprite* ballData = (Sprite*)b->GetUserData();
    ballData->setPosition(Vec2( b->GetPosition().x * PTM_RATIO,
    b->GetPosition().y * PTM_RATIO ));
    ballData->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
    }
    }
    }

    void HelloWorld::kick(float dt) {
    b2Vec2 force = b2Vec2(100, 100);
    body->ApplyLinearImpulse(force, body->GetPosition(), true);
    }

    void HelloWorld::onAcceleration(Acceleration* acc, Event* anEvent) {
    // Landscape left values
    b2Vec2 accGravity = b2Vec2(acc->y * 30, -acc->x * 30);
    world->SetGravity(accGravity);
    }

    void HelloWorld::onTouchesBegan(const std::vector& touches, Event *touchEvent) {
    if (mouseJoint != NULL) {
    CCLOG("mouseJoint != NULL. Returned.");
    return;
    }

    Touch* myTouch = new Touch();
    for( auto &item: touches) {
    myTouch = item;
    }

    Point location = myTouch->getLocationInView();
    location = Director::getInstance()->convertToGL(location);
    b2Vec2 worldLoc = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);

    CCLOG("Touch began at x = %f, y = %f", location.x, location.y);

    if (paddleFixture->TestPoint(worldLoc)) {
    CCLOG("Tested point. Now init the mouseJoint.");
    b2MouseJointDef mouseJointDef;
    mouseJointDef.bodyA = groundBody;
    mouseJointDef.bodyB = paddleBody;
    mouseJointDef.target = worldLoc;
    mouseJointDef.collideConnected = true;
    mouseJointDef.maxForce = 1000.0f * paddleBody->GetMass();

    mouseJoint = (b2MouseJoint*)world->CreateJoint(&mouseJointDef);
    paddleBody->SetAwake(true);
    }
    }

    void HelloWorld::onTouchesMoved(const std::vector& touches, Event *touchEvent) {
    if (mouseJoint == NULL) {
    return;
    }

    Touch* myTouch = new Touch();
    for( auto &item: touches) {
    myTouch = item;
    }

    Point location = myTouch->getLocationInView();
    location = Director::getInstance()->convertToGL(location);
    b2Vec2 worldLoc = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);

    CCLOG("Touch moved to x = %f, y = %f", location.x, location.y);

    CCLOG("before setting");
    mouseJoint->SetTarget(worldLoc);
    CCLOG("after setting");
    }

    void HelloWorld::onTouchesEnded(const std::vector& touches, Event *touchEvent) {
    if (mouseJoint != NULL) {
    world->DestroyJoint(mouseJoint);
    mouseJoint = NULL;
    }
    }

    void HelloWorld::onTouchesCancelled(const std::vector& touches, Event *touchEvent) {

    }
    ------------------------------------------------------------------------------
    Obelisk
  • Thank you all if you've ever read my post. I've just solved it by a little trying.

    For some reasons (that I'm very eager to hear the explains from you pros) when run on Windows PC my mouseJoint was created but all empty by itself. When touched, the HelloWorld layer called its callback OnTouchesBegan and immediatly return (due to the checking result), so the mouseJoint remained empty (that means no m_bodyB of its has existed yet).

    That's why the SetTarget, of which the detail code is

    -----------------------------------------------------------
    void b2MouseJoint::SetTarget(const b2Vec2& target)
    {
    if (m_bodyB->IsAwake() == false)
    {
    m_bodyB->SetAwake(true);
    }
    m_targetA = target;
    }
    -----------------------------------------------------------

    has thrown out an error and cause crashing. I've comment the code that check "mouseJoint != NULL" in my OnTouchesBegan and the program run fine on Windows PC.

    The strange is, as I said, when run in Windows platform, the mouseJoint is created, just empty, but on phone (Galaxy S2, Cyanogen mod - Android 4.3 FYI) it is NOT created at all... ???
    Obelisk

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!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How To Make a Tower Defense Game with Swift.

Suggest a Tutorial - Past Results

Hang Out With Us!

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


Coming up in December: The Great CALayer Tour

Sign Up - December

Our Books

Our Team

Tutorial Team

  • Jake Gundersen
  • Matthew Morey

... 59 total!

Update Team

  • Riccardo D'Antoni

... 14 total!

Editorial Team

... 22 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

  • Richard Casey

... 4 total!