Home > iPhone Development > Cocos2d Example – Bouncing Ball

Cocos2d Example – Bouncing Ball

In this example, we will use Cocos2d and ChipMunk physics engine to implement a bouncing ball. A ball falls towards the ground because of the force of gravity. The basic idea of ChipMunk is that you define some physical objects (including bodies and shapes, but they are invisible) in the space, then ChipMunk will simulate what happens to these objects. You should setup a callback function to update these objects’ visual representation (e.g., a sprite created with image).

Step 1: Create a Cocos2d Application and name it with “BouncingBall”. We assume you have installed Cocos2d project template properly.
1

Step 2: Add this image to Resources Group.

Step 3: Define the class BallLayer, which is a subclass of Layer. The codes are shown below:
// BallLayer.h

#import “cocos2d.h”
#import “Layer.h”
#import “chipmunk.h”

@interface BallLayer : Layer {
Sprite* ballSprite;
cpSpace* space;
}
@end
// BallLayer.m

#import “BallLayer.h”

void updateShape(void* ptr, void* unused){

cpShape* shape = (cpShape*)ptr;

Sprite* sprite = shape->data;

if(sprite){

cpBody* body = shape->body;

[sprite setPosition:cpv(body->p.x, body->p.y)];

}

}

@implementation BallLayer

-(void)tick:(ccTime)dt{

cpSpaceStep(space, 1.0f/60.0f);

cpSpaceHashEach(space->activeShapes, &updateShape, nil);

}

-(void)setupChipmunk{

cpInitChipmunk();

space = cpSpaceNew();

space->gravity = cpv(0,-2000);

space->elasticIterations = 1;

[self schedule: @selector(tick:) interval: 1.0f/60.0f];

cpBody* ballBody = cpBodyNew(200.0, INFINITY);

ballBody->p = cpv(150, 400);

cpSpaceAddBody(space, ballBody);

cpShape* ballShape = cpCircleShapeNew(ballBody, 20.0, cpvzero);

ballShape->e = 0.8;

ballShape->u = 0.8;

ballShape->data = ballSprite;

ballShape->collision_type = 1;

cpSpaceAddShape(space, ballShape);

cpBody* floorBody = cpBodyNew(INFINITY, INFINITY);

floorBody->p = cpv(0, 0);

cpShape* floorShape = cpSegmentShapeNew(floorBody, cpv(0,0), cpv(320,0), 0);

floorShape->e = 0.5;

floorShape->u = 0.1;

floorShape->collision_type = 0;

cpSpaceAddStaticShape(space, floorShape);

}

-(id)init{

self = [super init];

if(nil != self){

ballSprite = [Sprite spriteWithFile:@"ball.png"];

[ballSprite setPosition:CGPointMake(150, 400)];

[self add:ballSprite];

[self setupChipmunk];

}

return self;

}

@end

In class BallLayer, we defined a pointer ballSprite to point to a ball sprite that is initialized in init method. We also defined a cpSpace pointer space which points to a ChipMunk space. In init method, we invoked setupChipmunk function to setup ChipMunk stuff.

In setupChipmunk method, we first created a Chipmunk space and assigned some paremeters. (Note: The parameter elasticIterations should be greater than 1 if you want the ball bounces off the ground). We uses schedule:interval: method to schedule a selector tick: with an interval time. tick: function will call cpSpaceStep to update the space. In addition, cpSpaceHashEach function will call updateShape on each active shape. You will have chance to update the sprite’s (i.e., the ball’s) position in updateShape function (updateShape function is the callback function we metioned at the begining of this tutorial).

Next, the ball’s body and shape are created. We add ball’s shape to the ChipMunk space.  In this case, we used segment to describe the shape of the ground and set the thickness of segment to be 0. Because the ground doesn’t move, the function cpSpaceAddStaticShape is used to add the shape of ground to the space. (Note: You can adjust the elasticity value (i.e., parameter e) of ball and ground to determine how high the ball can bounce).

Step 4: The project template has created MyScene class by default. We need to make the following modifications:

// MyScene.h

#import

#import “Scene.h”

#import “BallLayer.h”

@interface MyScene : Scene {

BallLayer* ballLayer;

}

@property (nonatomic, retain) BallLayer* ballLayer;

@end

// MyScene.m

#import “MyScene.h”

@implementation MyScene

@synthesize ballLayer;

-(void)dealloc{

[ballLayer release];

[super dealloc];

}

-(id)init{

self = [super init];

if(nil != self){

BallLayer* layer = [[BallLayer alloc]init];

self.ballLayer = layer;

[layer release];

[self add:ballLayer];

}

return self;

}

@end

Step 5: In BouncingBallAppDelegate.m file, applicationDidFinishLaunching method, change the following code:

[director setLandscape:YES];

to

[director setLandscape:NO]; 

Build&Go. You will see the following result:

Update: You can download the sourcecodes from here. In addition, in the codes above, -> was not displayed properly, now fixed. 

Categories: iPhone Development
  1. demetriusb
    May 21st, 2009 at 21:48 | #1

    Could you post the source code. Tried running the example and got too many errors to list.

  2. admin
    May 22nd, 2009 at 00:25 | #2

    to demetriusb,
    Now you can download the soucecode in the post. Thanks.

  3. Tom McNamee
    May 24th, 2009 at 13:00 | #3

    Many thanks for this demo. It serves as a great starting point for development. I placed several circular static shapes in the drop path of the ball, and the ball whizzes right past. Playing with the floorShape, I noticed there are values for r, gravity, and ball diameter where the ball will ‘miss’ the floor. I suspect that the collision gets missed during the position update.
    Assuming I haven’t made a programming error, can you suggest ways I might get the ball to interact with a new obstacle? Is it just playing with the variables, or is there another principle involved?
    Many thanks for your efforts.

  4. admin
    May 24th, 2009 at 18:06 | #4

    to Tom,
    I don’t know what’s going on with your codes, but I suggest you pay attention
    to the time step of updating the space. In ChipMunk documents, “The number of iterations,
    and the size of the time step determine the quality of the simulation.
    More iterations, or smaller time steps increase the quality. ”
    You can use the following code to adjust the time step and to see if it can solve your problem.
    int steps = 2;
    cpFloat dtime = dt/(cpFloat)steps;
    for(int i=0; i<steps; i++){
    cpSpaceStep(space, dtime);
    }

  5. Austin
    May 24th, 2009 at 21:35 | #5

    Thanks! Great tutorial!

  6. May 27th, 2009 at 09:22 | #6

    I tried to go a step further and define the ceiling and right/left screen bounds so a ball would stay on the screen. I did this for the ceiling but it doesn’t bounce the ball back – it just passes right through.

    cpBody *ceilingBody = cpBodyNew(INFINITY, INFINITY);
    ceilingBody->p = cpv(0, 320);
    cpShape *ceilingShape = cpSegmentShapeNew(ceilingBody, cpv(0,320), cpv(480,320), 0);
    ceilingShape->e = 0.5; //elasticity
    ceilingShape->u = 0.0; //friction
    ceilingShape->collision_type = 0;
    cpSpaceAddStaticShape(space, ceilingShape);

    any ideas on how to detect the screen boundaries better? the floor works great – per this tut and I did the same to define the left side & that works, but ceiling and right side do not.

  7. June 2nd, 2009 at 19:35 | #7

    Hi, good post. I have been wondering about this issue,so thanks for posting. I’ll definitely be coming back to your site.

  8. Sean Carmody
    June 9th, 2009 at 23:51 | #8

    Justin, are you working in portrait (as the tutorial) or in landscape (like most games)? I was having trouble also and learned that Chipmunk uses bottom left corner as (0,0) while the standard iPhone code uses top left corner as (0,0). To remedy this I had to rotate the gravity vector when using the accelerometer. I’m not sure if this is causing your problem.

    To admin, thanks for the tutorials, they are very helpful. Keep up the good work!

  9. June 27th, 2009 at 12:01 | #9

    So just making sure I’ve got this right. If I wanted to have several bouncing balls, I would take care of all the chipmunk setup in my app delegate and pass the space to the ballLayer class, add the sprite to the space in the class, then handle the updating in the app delegate.

    That sound about right?

  10. Selva
    July 22nd, 2009 at 04:47 | #10

    Hi

    Really, This is great tutorial.
    Please do keep updating…:)

  11. angellixf
    November 27th, 2009 at 01:51 | #11

    Thanks…

  12. December 3rd, 2009 at 10:15 | #12

    This tutorial needs updating for cocos2d v0.8.2 and iPhone OS 3.1.2.

    The following are my changes as the original code does not work in the latest iPhone OS 3.1.2.

    In BallLayer.m:
    - Change [self add:ballSprite] to [self addChild:ballSprite]

    Also in MyScene.m:
    - Change [self add:ballLayer] to [self addChild:ballLayer]

    You can add the optional z-order like [self addChild:ballLayer z:0].

    In BouncingBallAppDelegate.m:

    This method [director setLandscape:YES] is deprecated in cocos2d v0.8.2.

    Current version already provides in the template support for device orientation, like [[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationPortrait].

    The above changes made this tutorial work in cocos2d v0.8.2 and iPhone OS 3.1.2.

  13. Lt_Ender
    February 10th, 2010 at 16:42 | #13

    more changes are required to update to cocos2d version 0.9 or 0.99

    - class names have changed, most now use prefix “CC”, for example “CCLayer” instead of “Layer”

  14. admin
    February 10th, 2010 at 16:47 | #14

    Hi Lt_Ender,

    Exactly, some APIs change, more features included. This post
    just provides a basic idea. :)

  15. March 31st, 2010 at 02:04 | #15

    Great tutorial and introduction.

    One thing I found while messing around though was that if I “dropped” the ball from a greater height (500), the ball would pass right through the floor.

    Is there any reason why this would happen?

  16. April 8th, 2010 at 16:11 | #16

    Hi Guys,

    It is one of the brilliant example I came across for Cocos2D, first of I am using 0.99, and I agree class names has been changed so I changed in this example, but somehow it seems there is no cpContact point in collision function anymore, and second thing I wanna know if anyone of you came across to move an static sprite by action and it never called collision function to the floor static bodies/shapes, even I defined separate collision type for them.

    I am making a game where you have two balls one you control by accelerometer and second one I animate using random actions. Now when first ball touch any of the floor shape collision function gets called and I showed a little particle effects but when second ball which has static body touch the floor no collision function gets called.

    Now I know I have to update static body position according to sprite, thats exactly I am doing like this:
    cpSpaceHashEach(space->activeShapes, &updateShape, self);
    cpSpaceHashEach(space->staticShapes, &updateStaticShap, self);
    cpSpaceRehashStatic(space);

    It seems a dead end to my game, any help would be extremely appreciative.

    regards,
    Shafiq

  17. Mark
    April 16th, 2010 at 12:19 | #17

    @Shafiq – could the problem be as simple as a typo? Probably not, but I thought I’d ask just in case – because this:

    &updateStaticShap

    looks like you might have meant:

    &updateStaticShape

    (with an “e” at the end)

  18. dude
    June 30th, 2010 at 04:44 | #18

    this blog desperately needs some code-formatting love.

  1. May 17th, 2009 at 02:04 | #1
  2. May 20th, 2009 at 03:57 | #2
  3. May 25th, 2009 at 18:51 | #3
  4. June 11th, 2009 at 12:31 | #4
  5. July 12th, 2009 at 19:17 | #5
  6. March 7th, 2010 at 04:52 | #6
  7. April 26th, 2010 at 17:35 | #7