Starting work on my new project, I’ve found Box2D to be a far superior physics engine than chipmunk. It is more mature, the API more flexible, and it seems to even perform faster. However, the cocos2d code for it was rather space, so here is a sort of helper file I had to create to make it work with my game.
Keep in mind this game is in progress and I’ve only been using this for a few days, so it may have issues that might pop up later on. It seems to be working very well for now though, with 40-50 objects on screen moving around with ~40 FPS.
Here is the header
// // Box2DEngine.h // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. // #import "Box2D.h" // made this an extern constant to avoid obj c lookup overhead extern b2World *bb_world; @interface Box2DEngine : CocosNode { } + (Box2DEngine *)sharedSingleton; - (void)createWorld:(CGSize)size; - (void)deleteWorld; - (void)runSimulation; @end
The .mm file
// // Box2DEngine.mm // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. // #import "HookActor.h" #import "Box2DEngine.h" #import "SuperBox2DActor.h" b2World* bb_world; const float32 timeStep = 1.0f / 60.0f; const int32 velocityIterations = 10; const int32 positionIterations = 10; #define MAX_NUM_COLLISIONS 2048 // TODO make sure this buffer is the right size b2ContactResult contactResultCache[MAX_NUM_COLLISIONS]; int32 contactResultCount = 0; class MyContactListener : public b2ContactListener { public: void Add(const b2ContactPoint* point) { } void Persist(const b2ContactPoint* point) { } void Remove(const b2ContactPoint* point) { } void Result(const b2ContactResult* point) { // TODO we are making a deep copy of every contact point here // check the box2d contact masks to make sure we minimize unwanted contact results contactResultCache[contactResultCount++] = *point; } }; void handleCachedContactResults() { b2ContactResult contactResult; for(int i = 0; i < contactResultCount; i++) { #ifdef BBDEBUG if(contactResultCount >= MAX_NUM_COLLISIONS) { NSLog(@"RAN OUT OF BUFFER"); assert(NO); } #endif contactResult = contactResultCache[i]; SuperBox2DActor* actorOne = (SuperBox2DActor*)contactResult.shape1->GetBody()->GetUserData(); SuperBox2DActor* actorTwo = (SuperBox2DActor*)contactResult.shape2->GetBody()->GetUserData(); if(!actorOne.isDead && !actorTwo.isDead) { [actorOne collisionResultOne:&contactResult withActor:actorTwo]; [actorTwo collisionResultTwo:&contactResult withActor:actorOne]; } } // clear the collision cache contactResultCount = 0; } void removeDeadActors() { b2Body* node = bb_world->GetBodyList(); while (node) { b2Body* b = node; node = node->GetNext(); SuperBox2DActor* actor = (SuperBox2DActor*)b->GetUserData(); if (actor.isDead) { // remove from physics engine bb_world->DestroyBody(b); // remove from game engine (cocos2d) [[StandardGameController sharedSingleton] removeGameActor:actor]; } } } @implementation Box2DEngine + (Box2DEngine*)sharedSingleton { static Box2DEngine* sharedSingleton; if (!sharedSingleton) sharedSingleton = [[Box2DEngine alloc] init]; return sharedSingleton; } - (void)createWorld:(CGSize)size { b2AABB worldAABB; worldAABB.lowerBound.Set(0, 0); worldAABB.upperBound.Set(size.width * BOX2D_SCALE_FACTOR_INVERSE, size.height * BOX2D_SCALE_FACTOR_INVERSE); // TODO this shouldnt be inverse? b2Vec2 gravity(0.0f, -30.0f); bool doSleep = true; bb_world = new b2World(worldAABB, gravity, doSleep); bb_world->SetContactListener(new MyContactListener()); } - (void)deleteWorld { [self unschedule:@selector(step:)]; delete bb_world; bb_world = NULL; } - (void)runSimulation { [self schedule:@selector(step:)]; } - (void)step:(ccTime)dt { // step the world bb_world->Step(dt, velocityIterations, positionIterations); // TODO do i use timestep or dt here? //BBLog(@"Num of contact results is %d",contactResultCount); // do stuff with collisions handleCachedContactResults(); // remove all actors that are marked as dead removeDeadActors(); // update cocosnode positions for (b2Body* b = bb_world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != NULL) { SuperBox2DActor *actor = (SuperBox2DActor*)b->GetUserData(); b2Vec2 position = b->GetPosition(); actor.position = b2toCGPoint(position); actor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()); //NSLog(@"obj %@ %4.2f %4.2f\n",actor, position.x, position.y); } } } @end
#1 by Mark Townsend on July 28th, 2009
Hmm. GCC fails to compile on the line:
contactResultCache[contactResultCount++] = *point;
Is SuperBox2DActor a subclass of (Atlas)Sprite or a custom CocosNode?
#2 by Eric on July 28th, 2009
What is the error?
SuperBox2DActor is a subclass of CocosNode, as I only make it a node to allow to to have a cocos2d timer by adding it to my game scene object.
#3 by CJ Hanson on July 29th, 2009
Hello, thanks for the example.
I am having a problem however… in the step function at the point inside the condition that userData is not NULL, if userData happens to be something other than the desired Actor type it drops into the debugger on the first line to access something specific to that class. Is there some way to filter by class before accessing the class?
I tried this…
void *uData = b->GetUserData();
if (uData) {
if(![uData isKindOfClass:[ActorBox2D class]]){
But as soon as you try to call “isKindOfClass” on uData (which apparently isn’t a class that responds to this selector) it dumps to debugger again.
Thanks again
#4 by Eric on July 29th, 2009
Instead of using
void *uData = b->GetUserData();
do this instead
id uData = b->GetUserData();
Though I would suggest making all objects stored in the physics engine a subclass of a class the implements the various functions, even if it doesn’t do anything with them. This way you can avoid some crazy code hacks that might happen later on as you add in more objects with various behaviors.
That’s more of a design decision though, and both methods will work (I used a lot of IsKindOfClass in StickWars, and the code gets VERY ugly, error prone, and hard to debug..but it works).
For example, I have a single Box2DActor that is nothing but all the static shapes that make up the boundaries for the map, and when my function updates that object’s position nothing happens in the function, since it is simply a CocosNode that isn’t added to any parent. It still exists in the physics engine, but the visual representation of it (CocosNode) isn’t added to the environment so it doesn’t do anything.
#5 by christopher truman on August 5th, 2009
thanks! all you have written is really helpful!
A great resource I have found is
http://blog.zincroe.com/2009/05/iphone-and-box2d/
a sample “game” project and other info
the project seems to allow bodies to get stuck in corners or walls or when allowed to come to a stop…
any idea why that is?
would love to see any tutorials on this topic in the future
#6 by Eric on August 5th, 2009
Yea, what is probably happening is Box2D allows objects to ’sleep’ if they stop moving for a bit. This is great because it allows saves the CPU processing on inactive objects, but if you enable this feature sometimes you need to make sure that objects aren’t sleeping when you don’t want them to.
#7 by James Bowen on August 17th, 2009
Thanks for giving so much back to the developer community! As a newbie iphone dev i really appreciate it. I was wondering, are you using cocos2d version 0.8 with its “experimental” support of Box2D still? Or have you found it worthwhile to push into the Beta 0.8.1 (with theoretically better Box2D support)?
#8 by Eric on August 17th, 2009
In StickWars, I’m working with 0.8 as I started out using Chipmunk and don’t want to change it over.
For my other project, which I’m using Box2D, I will definitely push into beta 0.8.1 as soon as I get back to working on it. When using cocos2d, I’d suggest paying very close attention to updates and the new features coming out, because often there will be some awesome new feature that will save you a lot of time on your own.
For example…I pretty much wrote all the code I needed to get Box2D support working well, about 2 weeks before 0.8.1 was released with better out-of-the-box support
.
#9 by James Bowen on August 17th, 2009
Thanks for the quick reply!
Just goes to show how quickly cocos2d is progressing!
Haha, yeah, from your initial post date here it looked like you were all synched up with 0.8 (guessing from the changelog dates) so I wondered if 0.8.1 didn’t make some of your hard-earned foundation work redundant.
#10 by Xun Cai on August 20th, 2009
Hi Eric,
Thanks for your articles, they r very helpful.
We r making a game called 9000 BC and we nearly finish the game. However, we have some problem in the sound engine. When we play the bg music, the game doesn’t run smoothly. Can u tell me which sound engine you use in stick war? Your sound engine seems to work very well.
Thanks :>
9000 BC development team
#11 by Eric on August 20th, 2009
Check out CocosDenshion from the latest download of cocos2d-iphone. It has a nice simple sound engine that will allow you to use the hardware mp3 decoder. Follow the instructions to make sure you aren’t using software decoding of compressed sound files (only playing 1 mp3 file at a time, using .wav or something uncompressed for short sound files).
#12 by author on October 21st, 2009
comment
#13 by Sridhar on October 28th, 2009
Hi Eric,
Can you please post the project source code , so that it is easy for beginners to just compile and run it to see what is happening.
Thanks
Sridhar
#14 by Eric on October 28th, 2009
I would like to, but I’m afraid I don’t have time to build a working ‘demo’ game to publish to help people out. The only code base I have are games I’m working on to release soon and I can’t release the code for those right now.
If I find some time I’d try to make a ‘demo game’ that goes beyond the standard cocos2d template to show how I actually organize and use all the various frameworks (admob, cocos2d, box2d, openfeint) together. I’ll hopefully able to include a lot of tricks to make things work that are common questions people ask.
#15 by Evana on November 18th, 2009
Hey Eric,
I am newbie in cocos2d framework. i need some solution of acceleration with rotation i can do it bt it not smooth can you pls give me the solution pls.
Thanks in advance.
#16 by Evana on November 18th, 2009
Hey Eric,
I am newbie in cocos2d framework. i need some solution of acceleration with rotation i can do it bt it not smooth can you pls give me the solution pls.
Thanks in advance.
Regards
Evana
#17 by leo on December 30th, 2009
i am looking for an ichalky clone with a skeleton that can be rigged can you give me a quote on that please
#18 by Eric on December 30th, 2009
Hi, I’m sorry but I don’t have the time currently to take on new projects. Good luck with your hunt though!