How to add polygons to Box2D world in your ios / Cocos2d appPosted: February 17, 2012
With hundreds of tutorials and books explaining and demonstrating how to add objects to a Box2D physics world, and use a sprite as a visual representation of that object, if you got here other than by a search engine, you might be wondering “why is this hard”.
Short answer is that it is not. But most of the examples either create a ball as a quick “circle” item, or use “convenience methods”, thus avoiding issues of how to specify the array of vertices in more complex cases. There a million examples, including many good ones in the tutorials from the excellent bloggers listed on the blogroll here. Alas, if you are lucky enough to find a tutorial that a) uses the current version / same version of Cocos2d you are using; b) using the version of Box2D you are using; c) is not wrapping the Box2D cpp routines in an objective C layer “to make it easier” (there are a couple of these); or d) has not moved on to the next (say 2.X) version of Cocos2D etc., then you may find something that illustates the straightforward conceptual explanation as found, for example in the excellent Itterheim book. Except here you get code such as like:
polygonShape.SetAsBox(1, 1); //a 2x2 rectangle
and you pause. What if i don’t want a box? what if i want different values for resilience, or friction? After endless frustrations trying to find a good set-every-value, do every operation yourself example, I just wrote my own. I like to use polygon operations for creating even square boxes or rectangles because it is easy to switch between triangles and other odd shapes, but still do them the same way, giving me practice that reinforces one way of doing things rather than giving me more things to learn (when right now i just want to get a prototype of my game written.)
So here is my example. A short piece of code that a) creates an array of vertices for a triangle shape object, the “top righthand (s)chute ” in my game (hence the trs in the variable names); b) creates a b2BodyDef and establishes it’s type and position (b2-staticBody for walls and things that don’t move; otherwise dynamic — and if you don’t know how to get Xcode to enumerate your options, FIND OUT NOW! you can’t use “bear skins and bone knives” and get anywhere near the productivity you will need to succeed. Maybe I will do a post on that later.). Note these two points: 1) declare your vertices counter-clockwise, starting with the zero array element and moving up to N-1, where you have N (but 8 or fewer) vertices in your convex polygon shape; and 2) must make these coordinates make sense relative to each other. I advise to include the 0,0 location at the bottom left, or otherwise center the shape around a 0,0 origin. Don’t confuse where you will put the object on the screen with the coordinates in the vertices array; that is something you set separately (see below).
So, here is a simple bit of sample code for your cpp Layer implementation in your Cocos2D / Box2d game to create a static object in your Box2D physics world. Note of course that these are in the implementationi (or .mm) file; yes you will need to declare the b2Body (topRightShuteBody and a sprite if you use it in your header file. I say “if you use it” because some of my objects are drawn on the background, and if they are static, there is no real need to associate the object with a sprite. I used to do this out of habit — give every object a sprit — and realized gradually this was dumb. But maybe the static background is what is ‘dumb’ — if everything is a sprite you can change it easily…
b2Vec2 trsVertices; trsVertices.Set(200.0f /PTM_RATIO, 100.0f /PTM_RATIO); trsVertices.Set(0.0f, 0.0f ); trsVertices.Set(200.0f /PTM_RATIO, 0.0f); b2BodyDef topRightShuteBodyDef; topRightShuteBodyDef.type = b2_staticBody; topRightShuteBodyDef.position.Set(546.0f / PTM_RATIO, 809.0f / PTM_RATIO); topRightShuteBody = world->CreateBody(&topRightShuteBodyDef); b2PolygonShape trsShape; int32 trsVert = 3; rectShape.Set(trsVertices, trsVert); b2FixtureDef trsFixtureDef; trsFixtureDef.shape = &rectShape; trsFixtureDef.density = 1.6f; trsFixtureDef.friction = 0.4f; trsFixtureDef.restitution = 0.2f; topRightShuteBody -> CreateFixture(&trsFixtureDef);
One other note: you will in most of the examples you find that, for objects of type “b2_dynamicBody”, the CCSprite is usually stored in the “userdata” field of the b2Body. And of course that really means “a pointer to the CCSprite”. This is so the “update” / game tick event can easily grabe the sprite and move it to the new position as determined by the physics engine. But since this is a piece of “furnishing” or “the world” that does not move (but does interact with the dynamic objects), it won’t be moving and so doesn’t need a sprite and in fact without anything in the b2body.userdata field the update routines will not process this object even to see that it does not move.
Hope this is useful to some of you. With complete “tile board” and “mario brothers” type runner games available in toto as demos from many sources, this example will only be useful for game developers that want to create more arcade-style games. Hmm. Maybe my game should be called “Angry Words”? For now I will stay with WordPiles ….
And yes, i plan to release the source in a more comprehensive toturial / training vehicle sometime down the road.