Posts
Search
Contact
Cookies
About
RSS

Raylib and Chipmunk2d

Added 16 Aug 2019, 9:37 p.m. edited 18 Jun 2023, 5:54 p.m.

There are a number of options for 2d physics, and Chipmunk has been around for some time and is stable both in terms of reliability and its API, its written in C which is a bonus.


Chipmunk has a number of useful utility functions to allow you to iterate through all of its various structures that you'll use, however I prefer to keep a collection of data together in my own structure just so I have everything to hand and don't have to keep calling functions to look things up...

typedef struct phys {
    enum shapeType type;
    Vector2 size;
    cpBody* body;
    cpShape* shape;
    bool sharedBody;
} phys;

Because box shapes are actually polygon type shapes inside chipmunk I have my own enum to identify shape types, for circles, size only uses the x component of the vector as its radius, the physics body the shape is using can be used by more than one shape, so we need to mark if a shape is using a shared body.

Obj[o].body = cpSpaceAddBody(space, cpBodyNew(0, 0));
Obj[o].sharedBody = false;
Obj[o].shape = cpSpaceAddShape(space, cpBoxShapeNew(Obj[o].body, Obj[o].size.x, Obj[o].size.y, 0.0));
cpShapeSetFriction(Obj[o].shape, 0.6);
cpShapeSetMass(Obj[o].shape, 8);

The first shape in a multi shape body is marked as not shared so as to ensure that the body is freed. Notice also that the body is created without mass and moment, as we add each shape to a body and set the shapes mass, chipmunk will workout the accumulated mass and moment for us.

It's possible to have callback that are run in quite a variety of circumstances, for lol's I decided that fast energetic collisions should make some kind of noise...

cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space, 0, 0);
handler->postSolveFunc = (cpCollisionPostSolveFunc)postSolve;

Our collision handler isn't using collision filtering, so all out bodies are collision "type" 0, hence we are handling collisions between types 0 and 0 only. The post solve function is called after collisions have be sorted out, this means all sorts of handy info is calculated and ready for us...

void postSolve(cpArbiter *arb, cpSpace *space, cpDataPointer data)
{
if (cpArbiterIsFirstContact(arb)) {
float f = cpArbiterTotalKE(arb);

All that's happening here is we are checking to see if this callback has happened on this collisions first contact, if it is we query the total force of the collision, from there its easy to set a threshold (slight brushes or gentle bumps don't make a sound, there's no point swamping the audio!) the total energy can also be a guide as to how loud to play the sound...

The only constraint or joint that's in use is the pivot, that will tend to continue rotating until something physically stops it, I like to add a little bit of rotational "friction"

    cpBodySetAngularVelocity(Obj[0].body, cpBodyGetAngularVelocity(Obj[0].body)*drag);

where drag is a fairly high fraction of 1.0 such as 0.999 for example, various effect can be had by varying this.

The only thing left to discuss is the rendering of the shapes - remember you can just iterate all the shapes with chipmunk if you like, you can even save a userdata pointer to your own custom struct with each shape too if you like. As discussed previously I've gone my own way, each approach works just fine, horses for courses as they say... Don't forget that chipmunk expresses angles in radians as you'd expect, alas raylib does use degrees for some of the 2D stuff, so look out for that!

While I'm dealing with randomly sized shapes, often you'll probably have fairly uniform sizes within types of objects, matching up a polygon shape to your graphical sprite... now that would make a good project for someone, a sprite collision polygon editor tool.....

You can grab the discussed code here