The Chipmunk Physics Engine

While developing Star Fighter X2, I needed a way to handle a system of bodies that would interact with each other. This post will cover how I used the Chipmunk physics engine.

Background and Requirements

SFX2 used two main “actors” or “bodies” to represent the ships that players would control. I also had a number of other bodies and projectiles that would have to interact with my two ships, and themselves. For those of you who aren’t familiar with how to handle such interactions, you would normally make use of a Physics Engine.

Check out Wikipedia’s entry on what a physics engine is responsible for: http://en.wikipedia.org/wiki/Physics_engine.

Since I was using cocos2d, two popular 2D engines are available: Box2D and Chipmunk.

Box2D is open source (MIT), written in C++ and, from what I have read, is incredibly robust. It will provide all that you’ll ever need for 2D physics engine support.

Chipmunk is also open source (MIT), written in C (C99) and isn’t quite as feature-complete as Box2D. It is written by Scott Lembcke from Howling Moon software. I was fortunate to run into Scott and his colleague Andy at GDC 2010 and they are great, very intelligent guys.

How did I pick Chipmunk over Box2D?

Some points about Box2D:

  • written in C++ (sans STL)
  • reading through the documentation and APIs, I came to realize there would be quite the learning curve
  • large user base and community
  • memory management completely handled by the engine
  • frequently updated

Some points about Chipmunk:

  • written in C
  • docs and APIs seemed easy to understand
  • smaller user community than Box2D, but reading through both the cc2d-iphone and slembcke.net forums, there was lots of activity
  • handle all memory management yourself
  • frequently updated

I hadn’t done any physics programming before, so I needed to quickly ramp up on the concepts of how to represent the interactions. I tried both systems briefly, and I was able to get up and running quickly with Chipmunk.

None of the SFX2 code was written in C++, so I wanted to keep things straightforward and leave C++ objects out of it. I’m also a fan of having control of memory management.

There was still a decent learning curve, but I was able to find all that I needed from the Chipmunk docs and community forums. Chipmunk also shipped with a sample app that used relatively similar body interactions, so the decision was made to stick with it.

Integration

Getting started with cocos2d and Chipmunk is easy. Download the latest build from cocos2d.org, un-package, and run the install-templates.sh shell script. This will copy the supplied cocos2d project templates in place for new Xcode projects.

  1. From Xcode, go to File -> New Project
  2. From the New Project dialog, under the “User Templates” section on the left hand side, select the cocos2d item.
  3. The view on the right will switch to show the available templates. Double-click on the “cocos2d Chipmunk” template.
  4. Enter in the name of your new project and press enter.

The template will automatically setup the cocos2d headers and sources for you. Open your main AppDelegate source and you’ll see that it generated a bunch of basic cocos2d code for you to manage and initialize the Director and other necessary settings.

Important Concepts

Discussions on the concepts below are available in the official Chipmunk documentation, but I would like to relate them to how I used them.

Spaces

Spaces contain the physical bodies that you are modelling. You would initialize the primary space of your physics engine and add bodies to it. SFX2 had a single space that I would initialize at the beginning of a new game.

(see cpSpace in the Chipmunk API)

Bodies

A body, or rigid body, is an object that has physical properties such as mass, position, and velocity. Bodies do not represent a specific shape. SFX2 uses bodies for all objects during gameplay, from the two spaceships and asteroids to every power-up. I wanted to make sure that all objects had physical properties to allow collisions to be detected.

Bodies can also be static, meaning that they can be constructed to stay put. Assigning an infinite mass and moment of inertia to a body will make sure it obtains no velocity from collisions or effects from gravity. You could use these static bodies as boundary objects which could block other bodies from passing through.

SFX2 uses static bodies around the four sides of the main gameplay area and across the middle of the space to make sure players stay on their side.

(see cpBody in the Chipmunk API)

Shapes

Shapes are associated to bodies. They represent the boundaries used for collision detection. Chipmunk allows you to define different types of shapes: circle, polygon, line segment. You can assign multiple shapes to a body (or add more vertices to your polygon) to define a more complex shape.

An important thing to remember is that circle to circle collisions are more efficient to process. Poly to poly collisions get more expensive as the number of vertices increase. The simpler the shape, the faster collisions will be processed.

Another important thing to note is that a shape or body in Chipmunk really has nothing to do with a Sprite from cocos2d. The only thing you need to do is make sure your shape has similar dimensions to the sprite, and that the sprite has a similar position and rotation to the body. Sprites are only how you visualize your new physical bodies.

(see cpShape in the Chipmunk API)

Constraints and Joints

Constraints and joints define how bodies are connected to each other. You would apply a constraint that would contain a joint applied to specific bodies.

(see cpConstraint in the Chipmunk API)

Mmmmm… Code

After you created your new project in Xcode, there was likely a default CCLayer class created from the template (i.e. HelloWorldScene). View the layer source and you’ll see a whole bunch of good stuff. Let’s dive in with a quick overview.

[objc]
-(id) init {

….

// setup Chipmunk and prepare it to start
// processing collisions

cpInitChipmunk();

// create the main space

space = cpSpaceNew();
cpSpaceResizeStaticHash(space, 400.0f, 40);
cpSpaceResizeActiveHash(space, 100, 600);

space->gravity = ccp(0, 0);
space->elasticIterations = space->iterations;

// define a single static body
cpBody *staticBody = cpBodyNew(INFINITY, INFINITY);

cpShape *shape;

// static shape for a bottom boundary,
// use these as needed
shape = cpSegmentShapeNew(staticBody, ccp(0,0),
ccp(wins.width,0), 0.0f);
shape->e = 1.0f; shape->u = 1.0f;
cpSpaceAddStaticShape(space, shape);

….

// next define the sprites that would be
// associated to the shapes in the system
CCSpriteSheet *sheet = [CCSpriteSheet spriteSheetWithFile:
@"grossini_dance_atlas.png"
capacity:100];
[self addChild:sheet z:0 tag:kTagAtlasSpriteSheet];

// add a single body to the system at (x,y)=(200,200)
[self addNewSpriteX: 200 y:200];

// schedule the step method to start firing as
// fast as it can (no interval)
// check out the step definition below
[self schedule: @selector(step:)];

….

}
[/objc]

Use a helper method like this one to define the bodies required in your system.

[objc]

-(void) addNewSpriteX: (float)x y:(float)y {

int posx, posy;

CCSpriteSheet *sheet = // reference the sprite sheet

posx = (CCRANDOM_0_1() * 200);
posy = (CCRANDOM_0_1() * 200);

posx = (posx % 4) * 85;
posy = (posy % 3) * 121;

// create a new sprite from the sheet
CCSprite *sprite = [CCSprite spriteWithSpriteSheet:sheet
rect:CGRectMake(posx, posy,
85, 121)];
[sheet addChild: sprite];

sprite.position = ccp(x,y);

// IMPORTANT: define a set of vertices to be
// used for the new shape
int num = 4;
CGPoint verts[] = {
ccp(-24,-54),
ccp(-24, 54),
ccp( 24, 54),
ccp( 24,-54),
};

// Create a new body
cpBody *body = cpBodyNew(1.0f, cpMomentForPoly(1.0f, num,
verts, CGPointZero));

// setup the position of the body
body->p = ccp(x, y);

// add the body to the active set in the system
cpSpaceAddBody(space, body);

// define the shape using the set of vertices
// associate to the body and add to space
cpShape* shape = cpPolyShapeNew(body, num,
verts, CGPointZero);
shape->e = 0.5f; shape->u = 0.5f;
shape->data = sprite;
cpSpaceAddShape(space, shape);

}

[/objc]

Chipmunk uses iteration to solve the interactions between bodies. Each iteration is a step in time (think dt) and all constraints are analyzed to ensure smooth and accurate behaviour of elements in the system. The method below, step, allows you to track, control, and respond to action in the system.

step will be invoked as quickly as the timer can fire.

[objc]

-(void) step: (ccTime) delta {

int steps = 2;
CGFloat dt = delta/(CGFloat)steps;

for(int i=0; i>steps; i++){
cpSpaceStep(space, dt);
}

// iterate over the set of shapes in the system
cpSpaceHashEach(space->activeShapes, &eachShape, nil);
cpSpaceHashEach(space->staticShapes, &eachShape, nil);

}

[/objc]

The following method is invoked on each object in the system. You would use this to update the location and rotation of the sprite relative to the body.

[objc]
static void eachShape(void *ptr, void* unused) {

cpShape *shape = (cpShape*) ptr;
CCSprite *sprite = shape->data;
if( sprite ) {
cpBody *body = shape->body;

[sprite setPosition: body->p];

[sprite setRotation: (float)
CC_RADIANS_TO_DEGREES( -body->a )];
}

}
[/objc]

This far and we’re really only scratching the surface of Chipmunk. In a future post, I’ll delve into the aspects used the most in Star Fighter.

Depending on your experience with physics engines this might be a fair bit to absorb. I encourage you to spend some time with the sample code available for Chipmunk, check out the docs, and read through some of the forum posts on the Chipmunk forums.

This post is part of iDevBlogADay, a group of indie iOS development blogs featuring two posts per day. You can keep up with iDevBlogADay through the web site, RSS feed, or Twitter.

This entry was posted in Blog, Game Dev, iDevBlogADay, Star Fighter X2 and tagged , , , , . Bookmark the permalink.