When making a game with Cocos2D, sometimes you may find yourself wanting to use Cocos2D actions to move your objects instead of using the Box2D physics engine. However that doesn’t mean you can’t still make use of the great collision detection support in Box2D!
This tutorial will show you how to use Box2D for just collision detection – not physics – step by step. We will create a simple demo of a car driving around the screen, laughing merrily as it runs over cats. Yes I know, I can be sadistic sometimes ;]
While we’re doing this, along the way we’ll also coves a couple new and interesting concepts, such as using sprite sheets, the Zwoptex utility, Box2D debug drawing, and the new VertexHelper utility.
This tutorial assumes you have gone through the other Box2D and Cocos2D tutorials, or that you have equivalent knowledge.
And before I forget – special credit goes to Kim from the comments section for suggesting writing this tutorial!
Sprites and Sprite Sheets
Before we begin, we’re going to take a brief detour to talk about sprites and sprite sheets.
So far when we have been using Cocos2D, we have been using the CCSprite class directly and passing in individual sprite images. But according to the Cocos2D best practices page, it’s a lot more efficient to use sprite sheets instead.
The above is an example of a sprite sheet, part of a sprite sheet included in the Cocos2D sample code. Sprite sheets are basically one big image that can be chopped-up into sub images. To specify where each sub-image lies within the sprite sheet, you have to give Cocos2D the coordinates for each image. For example, here’s what it looks like to pull out the first four sprites from that sprite sheet:
CCSpriteSheet *sheet = [CCSpriteSheet spriteSheetWithFile:@"grossini_dance_atlas.jpg" capacity:1]; CCSprite *sprite1 = [CCSprite spriteWithTexture:sheet.texture rect: CGRectMake(85*0, 121*1, 85, 121)]; CCSprite *sprite2 = [CCSprite spriteWithTexture:sheet.texture rect: CGRectMake(85*1, 121*1, 85, 121)]; CCSprite *sprite3 = [CCSprite spriteWithTexture:sheet.texture rect: CGRectMake(85*2, 121*1, 85, 121)]; CCSprite *sprite4 = [CCSprite spriteWithTexture:sheet.texture rect: CGRectMake(85*3, 121*1, 85, 121)]; |
As you can imagine, writing a lot of code like that with hard-coded coordinates can be quite tedious. Luckily, Robert Payne has written an incredibly handly web tool called Zwoptex that makes both creating sprite sheets and importing the coordinates into Cocos2D extremely easy.
Creating Our Sprite Sheet
Before we begin, you’ll need some images to work with. You can either use the car and cat images created by my lovely wife, or use your own.
Next, load up a web browser to Zwoptex. You will see a big blank screen that looks something like this:
Once it’s loaded up, inside Zwoptex click File, and then Import Images. Select the car and cat images that you downloaded and click select, and your images should appear on top of each other. Drag your images apart a bit so you can see them more clearly.
Note that the images have been automatically cropped to remove extra white space. This isn’t what we want (you’ll see why later), so drag a box around both of the images to select them and click Modify\Untrim Selected Images.
Now let’s get the images nice and straightened. Click Arrange\Complex by Height (no spacing) and they will line up nicely for you.
Finally, let’s get the canvas size down to a reasonable size. Click Modify\Canvas Width and set the width to 128px. Do the same with Modify\Canvas Height and set the height to 64px. Your screen should now look something like this:
Finally, time to export our sprite sheet and coordinates! Click on File\Export Texture and save the sprite sheet as “sprites.jpg”. Then click on File\Export Coordinates and save the coordinates as “sprites.plist”. Note that it is important that both the sprite sheet and coordinates have the same filename (before the extension), as the sprite sheet code assumes that they do.
Just for fun, open up sprites.plist. You can see that what Zwoptex did is automatically fill up a property list with the original names of each sprite, and the coordinates for each sprite. We can provide this to Cocos2D instead of typing in the coordinates manually!
Adding Our Sprites From the Sprite Sheet
Ok time to write some code!
Create a new project in XCode, choose the cocos2d-0.99.1 Box2d Application template, and name the project Box2DCollision. Then clear out the sample code to get an empty starting point like the way we did in the bouncing balls tutorial.
Also, make sure you add the following to the top of your HelloWorldScene.mm:
#define PTM_RATIO 32.0
If you’re confused what that means or why you need it, check out the bouncing balls tutorial for more information.
Next, let’s add the sprite sheet and coordinates property list into our project. Drag sprites.jpg and sprites.plist into the Resources folder of your project, verifying that “Copy items into destination group’s folder (if needed)” is checked.
Then, add the following member variable to the HelloWorld class in HelloWorldScene.h:
CCSpriteSheet *_spriteSheet; |
Now let’s modify the init method in HelloWorldScene.mm to load up our sprite sheet and property list. Modify your init method to look like the following:
- (id)init { if ((self=[super init])) { // Create our sprite sheet and frame cache _spriteSheet = [[CCSpriteSheet spriteSheetWithFile:@"sprites.jpg" capacity:2] retain]; [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"sprites.plist"]; [self addChild:_spriteSheet]; [self spawnCar]; [self schedule:@selector(secondUpdate:) interval:1.0]; } return self; } |
The first thing we do is to create a sprite sheet, which is an object that efficiently draws all of its CCSprite children. For this to work, they obviously must all share the same texture. When we add our car and cat image to the scene, we will add them as children of the CCSpriteSheet. Special thanks to Victor from the comments section for pointing this out!
Then we use CCSpriteFrameCache to load up our sprite sheet and give it the property list. This function actually looks for a image with the same name, which is why it was important to name the sprite sheet “sprites.jpg” since the property list was named “sprites.plist.”
After that we call a function to spawn a car into the scene, and a periodic 1 second update function, which we are about to write.
We’ll start with spawning the car. Let’s have him move in a triangle pattern across the middle of the screen, repeating forever. Add the following function to HelloWorldScene.mm above the init method:
- (void)spawnCar { CCSprite *car = [CCSprite spriteWithSpriteFrameName:@"car.jpg"]; car.position = ccp(100, 100); car.tag = 2; [car runAction:[CCRepeatForever actionWithAction: [CCSequence actions: [CCMoveTo actionWithDuration:1.0 position:ccp(300,100)], [CCMoveTo actionWithDuration:1.0 position:ccp(200,200)], [CCMoveTo actionWithDuration:1.0 position:ccp(100,100)], nil]]]; [_spriteSheet addChild:car]; } |
Note that to create our Sprite, we use the method spriteWithSpriteFrameName rather than spriteWithFile. This tells it to use the part of the sprite sheet texture representing the Car image.
Also note that instead of adding the car as a child of the HelloWorld layer, we add it as a child of the sprite sheet for more efficient drawing.
The rest of this function should be familiar to you at this point. So let’s spawn some cats! Add the following methods above the spawnCar method:
- (void)spawnCat { CGSize winSize = [CCDirector sharedDirector].winSize; CCSprite *cat = [CCSprite spriteWithSpriteFrameName:@"cat.jpg"]; int minY = cat.contentSize.height/2; int maxY = winSize.height - (cat.contentSize.height/2); int rangeY = maxY - minY; int actualY = arc4random() % rangeY; int startX = winSize.width + (cat.contentSize.width/2); int endX = -(cat.contentSize.width/2); CGPoint startPos = ccp(startX, actualY); CGPoint endPos = ccp(endX, actualY); cat.position = startPos; cat.tag = 1; [cat runAction:[CCSequence actions: [CCMoveTo actionWithDuration:1.0 position:endPos], [CCCallFuncN actionWithTarget:self selector:@selector(spriteDone:)], nil]]; [_spriteSheet addChild:cat]; } - (void)spriteDone:(id)sender { CCSprite *sprite = (CCSprite *)sender; [_spriteSheet removeChild:sprite cleanup:YES]; } - (void)secondUpdate:(ccTime)dt { [self spawnCat]; } |
Again you should be familiar with all of the above code by now. Compile and run the code, and if all goes well you should see a car moving around the screen, with cats moving right to left. But the cats are getting away scott free! We’ll have to do something about that.
Creating Box2D Bodies for the Sprites
The next step is to create Box2D bodies for each sprite so Box2D knows where they are – and hence can tell us when they collide! We will do this similar to how we did in the previous Box2D tutorials.
However the difference is instead of updating our sprites to be where Box2D tells us they should be, we are going to update the Box2D bodies to where the sprites are and let Cocos2D control their movement.
So let’s begin by creating our world. Open up HelloWorldScene.h and add the Box2D header to the top of the file:
#import "Box2D.h" |
Then add the following member variable to HelloWorldScene.h:
b2World *_world; |
Then add the following code to your init method in HelloWorldScene.mm:
b2Vec2 gravity = b2Vec2(0.0f, 0.0f); bool doSleep = false; _world = new b2World(gravity, doSleep); |
Note two important things here. First, we set the gravity vector to 0 since we don’t want these objects moving around artificially. Second we tell Box2D not to let the objects go to sleep. This is important because since we’re artificially moving the objects, they will tend to fall asleep unless we set this.
Then add the following method above spawnCat:
- (void)addBoxBodyForSprite:(CCSprite *)sprite { b2BodyDef spriteBodyDef; spriteBodyDef.type = b2_dynamicBody; spriteBodyDef.position.Set(sprite.position.x/PTM_RATIO, sprite.position.y/PTM_RATIO); spriteBodyDef.userData = sprite; b2Body *spriteBody = _world->CreateBody(&spriteBodyDef); b2PolygonShape spriteShape; spriteShape.SetAsBox(sprite.contentSize.width/PTM_RATIO/2, sprite.contentSize.height/PTM_RATIO/2); b2FixtureDef spriteShapeDef; spriteShapeDef.shape = &spriteShape; spriteShapeDef.density = 10.0; spriteShapeDef.isSensor = true; spriteBody->CreateFixture(&spriteShapeDef); } |
This code should look familiar to you – it’s the same as we did in the breakout tutorials. However, there is one difference – we set isSensor to true on the shape definition.
According to the Box2D Manual, you should set isSensor to true when you want to know when objects will collide without triggering a collision response. This is exactly what we want!
Next call that method from spawnCat, right before you call addChild:
[self addBoxBodyForSprite:cat]; |
And do the same thing for spawnCar:
[self addBoxBodyForSprite:car]; |
We need to remember to destroy the Box2D bodies when the sprites get destroyed. So replace your spriteDone method with the following:
- (void)spriteDone:(id)sender { CCSprite *sprite = (CCSprite *)sender; b2Body *spriteBody = NULL; for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) { if (b->GetUserData() != NULL) { CCSprite *curSprite = (CCSprite *)b->GetUserData(); if (sprite == curSprite) { spriteBody = b; break; } } } if (spriteBody != NULL) { _world->DestroyBody(spriteBody); } [_spriteSheet removeChild:sprite cleanup:YES]; } |
Now, the most important part of all of this. We need to update the positions of the Box2D bodies periodically as the sprites move. So add the following to your init method:
[self schedule:@selector(tick:)]; |
And then write your tick method as the following:
- (void)tick:(ccTime)dt { _world->Step(dt, 10, 10); for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) { if (b->GetUserData() != NULL) { CCSprite *sprite = (CCSprite *)b->GetUserData(); b2Vec2 b2Position = b2Vec2(sprite.position.x/PTM_RATIO, sprite.position.y/PTM_RATIO); float32 b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite.rotation); b->SetTransform(b2Position, b2Angle); } } } |
This is similar to the tick method we wrote in our breakout project, except this time we are updating the position of the Box2D object based on the position of the Cocos2D sprite rather than the other way around.
Compile and run the project… and it should look exactly the same as before. So similar, in fact, that you might wonder if this is even working. Well let’s wonder no more – time to enable debug drawing!
Enabling Box2D Debug Drawing
Since you started with the Box2D template, your project should already have the files GLES-Render.h and GLES-Render.mm included in the project, which has all of the code necessary for Box2D debug drawing. All we have to do is turn this on in our project.
Let’s start by adding the following include at the top of HelloWorldScene.h:
#import "GLES-Render.h" |
Then add the following member variable to the HelloWorld object:
GLESDebugDraw *_debugDraw; |
Next, add the following to your init method:
// Enable debug draw _debugDraw = new GLESDebugDraw( PTM_RATIO ); _world->SetDebugDraw(_debugDraw); uint32 flags = 0; flags += b2DebugDraw::e_shapeBit; _debugDraw->SetFlags(flags); |
This is the code that creates an instance of the GLESDebugDrawClass and registers it with the world object. We pass it a flag specifying what we want it to draw – here we specify that we want it to draw the Box2D shapes. For a list of the other flags you can set, take a look at b2WorldCallbacks.h.
Next, we need to add a draw method. Add the following underneath the init method:
-(void) draw { glDisable(GL_TEXTURE_2D); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); _world->DrawDebugData(); glEnable(GL_TEXTURE_2D); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); } |
To be honest I am still new to OpenGL so I’m not quite sure what these OpenGL calls are doing. However, this is the standard boilerplate code from the template to get debug drawing set up and it works :]
One final note, let’s add our dealloc method while we’re thinking of it:
- (void)dealloc { delete _world; delete _debugDraw; [super dealloc]; } |
Now when you compile and run the project, you will see some pink shapes around the sprites that show where the Box2D shapes are. If all goes well, they should be moving along with the sprites, proving out that the box2D shapes are where we want them to be.
Detecting the Collision
Now it’s time to run over some cats!
We’re going to set up a contact listener on the world just like we did in the breakout game. Download the generic contact listener code we wrote in that project, and add MyContactListener.h and MyContactListener.mm to your project.
While you’re at it, also download this amusing sound effect I made and add it to the project too. I bet you know what’s coming! :]
Back to code. Add the following imports to the top of HelloWorldScene.h:
#import "MyContactListener.h" #import "SimpleAudioEngine.h" |
And the following member variable to HelloWorld:
MyContactListener *_contactListener; |
Then add the following code to your init method:
// Create contact listener _contactListener = new MyContactListener(); _world->SetContactListener(_contactListener); // Preload effect [[SimpleAudioEngine sharedEngine] preloadEffect:@"hahaha.caf"]; |
And finally the following to the bottom of your tick method:
std::vector<b2Body *>toDestroy; std::vector<MyContact>::iterator pos; for(pos = _contactListener->_contacts.begin(); pos != _contactListener->_contacts.end(); ++pos) { MyContact contact = *pos; b2Body *bodyA = contact.fixtureA->GetBody(); b2Body *bodyB = contact.fixtureB->GetBody(); if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) { CCSprite *spriteA = (CCSprite *) bodyA->GetUserData(); CCSprite *spriteB = (CCSprite *) bodyB->GetUserData(); if (spriteA.tag == 1 && spriteB.tag == 2) { toDestroy.push_back(bodyA); } else if (spriteA.tag == 2 && spriteB.tag == 1) { toDestroy.push_back(bodyB); } } } std::vector<b2Body *>::iterator pos2; for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) { b2Body *body = *pos2; if (body->GetUserData() != NULL) { CCSprite *sprite = (CCSprite *) body->GetUserData(); [_spriteSheet removeChild:sprite cleanup:YES]; } _world->DestroyBody(body); } if (toDestroy.size() > 0) { [[SimpleAudioEngine sharedEngine] playEffect:@"hahaha.caf"]; } |
This code is all the same stuff we already learned how to do in the breakout tutorial, so you should be familiar with all of the above.
One more thing: let’s add some more cleanup code that we forgot to add so far (thanks to Indy for reminding me of this!) Add the following inside your dealloc method:
delete _contactListener; [_spriteSheet release]; |
So just give the code a run, and now there should be some squished cats!
Fine Tuning the Bounding Boxes
As you may have noticed, the problem with the current setup is the shapes for the Box2D objects do not accurately represent the shapes of the sprites. For some games this is good enough, but there’s an extra degree of realism if we can define a shape that matches up somewhat to the actual shape of the sprite object.
In Box2D, you can define the vertices for shapes yourself by specifying the point for each vertex. However, hard coding all of these points is error-prone and time intensive. Luckily, Johannes Fahrenkrug has just released a neat tool called VertexHelper that makes defining these vertices and importing them into Box2D a snap.
Go ahead and visit the above site and download VertexHelper. Is is a Mac application with source code, so you’ll have to open VertexHelper.xcodeproj and compile and run the app. When you run the app you will see a screen like the following:
Go ahead and drag sprites.jpg into VertexHelper on top of the “Drop Sprite Image here” label, and in the Rows/Cols section set the number of rows to 1 and the number of columns to 2. VertexHelper will automatically draw lines on the image showing how the image is divided up.
Note that of course the image must be equally spaced for this to work – this is why we told Zwopple not to auto-chop the images earlier in the tutorial.
Next, click the check box for “Edit Mode” up top and start to define your vertices by clicking in counter-clockwise order around the sprites for each image. Note that Box2D will automatically close up the last point you click with the first point, so no need to connect them yourself.
Another very important thing to note pointed out by toadkick in the comments section that I wasn’t aware of at first. When you are defining the vertices, you need to make sure that the polygon you create is convex. This means that none of the internal angles can be greater than 180 degrees, or in more simple terms, there can be no “indentations.” If you are interested in more info on this, check out this great page that shows you a Javascript demo of convex vs. concave polygons.
Finally, note that Box2D defines a variable b2_maxPolygonVertices that limits how many vertices you can add per shape, which defaults to 8. You can change this in b2Settings.h if you need to, but for now just make sure that you draw a border around each sprite with at most 8 vertices.
This is best shown through a video, so here’s a quick clip that shows how to define the vertices for each sprite:
Once you have that complete, in the type dropdown choose Box2D, and for the Style pick “Initialization”. In the box to the right it will put a bunch of code which we’ll be able to copy and paste into our project.
Ok so let’s fix our shapes up in our project! Open up HelloWorldScene.mm and modify the addBoxBodyForSprite method according to this template. Start by commenting out the spriteShape.SetAsBox call and then follow the instructions in the comments below:
/*spriteShape.SetAsBox(sprite.contentSize.width/PTM_RATIO/2, sprite.contentSize.height/PTM_RATIO/2);*/ if (sprite.tag == 1) { // Uncomment this and replace the number with the number of vertices // for the cat that you defined in VertexHelper //int num = 6; //b2Vec2 verts[] = {b2Vec2(4.5f / PTM_RATIO, -17.7f / PTM_RATIO), //b2Vec2(20.5f / PTM_RATIO, 7.2f / PTM_RATIO), //b2Vec2(22.8f / PTM_RATIO, 29.5f / PTM_RATIO), //b2Vec2(-24.7f / PTM_RATIO, 31.0f / PTM_RATIO), //b2Vec2(-20.2f / PTM_RATIO, 4.7f / PTM_RATIO), //b2Vec2(-11.7f / PTM_RATIO, -17.5f / PTM_RATIO)}; // Then add this //spriteShape.Set(verts, num); } else { // Do the same thing as the above, but use the car data this time } |
A side note: Again, toadkick pointed out that it’s better to use the “Initialization” style and the b2PolygonShape::Set method rather than the “Assignment” style so that Box2D can automatically compute the centroid and the normals for the shape for us.
Once you have that done, compile and run your project. You should notice that the debug draw shapes around the sprites now match up much better, and the collisions are much more realistic!
Now you can see the true power of using Box2D for collision detection – defining bounding shapes like that would be a bit more tricky to do yourself.
Of course, in our case tailoring the bounding shape has the unfortunate side effect of letting a few cats scurry away that would have been squashed with our simple box method! ;]
And That’s A Wrap!
Here’s a sample project with all of the code we’ve developed in the above tutorial.
Note that this is only one way of doing collision detection with Box2D. Lam from the Box2D forums has pointed out another way where you can use functions such as b2CollidePolygons instead of using this method. If you are looking for just a quick collision check rather than going through all of the above setup, you might want to check out Lam’s method.
I’m really interested in hearing how other developers have used Box2D and Cocos2D in their projects – or are planning to use it – do you use Box2D for physics, or just for collision detection, or not at all? And if you use Box2D for collision detection, which method do you use?
Category: iPhone
















great tuts,
thank you!
Hi Ray,
Thank you so much for all your fantastic tutorials! I’ve switched from Chipmunk to Box2D. Chipmunk felt less realistic, slower and buggier (random freezes). I’m much happier with Box2D.
Also thank you for mentioning VertexHelper :)
- Johannes
And thank you for writing VertexHelper, it’s a great tool! :]
Hi Rey,
You said:
“according to the Cocos2D best practices page, it’s a lot more efficient to use sprite sheets instead”
and you example after that is:
CCSpriteSheet *sheet = [CCSpriteSheet
spriteSheetWithFile:@"grossini_dance_atlas.jpg" capacity:1];
CCSprite *sprite1 = [CCSprite spriteWithTexture:sheet.texture rect:
CGRectMake(85*0, 121*1, 85, 121)];
According to the cocos2d best practices “Make CCSprite children of CCSpriteSheet”.
To my best understanding just using spread sheets can increase the efficiency of video memory usage. To optimize the number of GL calls we will also need to make CCSprite children of CCSpriteSheet.
Please, correct me if I’m wrong.
Hey Victor, you are right!! Thanks so much for pointing this out, I had missed that!
I’ve updated the tutorial to correctly add the sprites as children of the CCSpriteSheet instead. Let me know if I’ve got it right this time!
Hi Ray,
If you are addind _spriteSheet to children_, do you need to explicitly call “retain”? Doesn’t adding to the children_ NSArray implicitly call a retain? Or does this not matter?
i
@indy – You are right that adding _spriteSheet to the children implicitly calls retain, so the retain count on the object would be two (once for the retain we called, and once for being in the NSArray).
However, it’s good practice to call retain on objects that you are keeping track of in your member variables so you know they are always accessible to your object until you decide you are definitely done with it and call release.
This way, if we later remove the _spriteSheet from the scene for example, the retain count will decrement down to 1, and since it is positive it won’t be deallocated yet, so we can still re-use it again if we want.
Hi,
Would you need to call [_spriteSheet release] in dealloc?
Could I ask – why you chose dynamic bodies and not kinematic bodies for example. I dont know the difference really, but thought you would use kinematic bodies if you need to move the shapes position manually (I have likely misunderstood).
If these weren’t sensors and were colliding, as you are moving the position manually how is velocity calculated – and force reactions calculated when a dynamic body is moved manually?
i
@indy: Yes the dealloc method should have [_spriteSheet release] – looks like I forgot to put that in the tutorial. I’ve fixed the tutorial to include this – thanks for catching this!
Re: dynamic bodies vs. kinematic bodies – the best discussion I found on that was in the Box2D forums here: http://www.cocos2d-iphone.org/forum/topic/3208
From what it sounds like to me, you’d want to pick kinematic bodies if you wanted to move an object manually by setting velocity directly and have it affect other things but not have it be affected.
In this case, I could be wrong but I don’t think it really matters which we choose since we have set our objects to be sensor objects (hence they won’t be affected at all).
Regarding your last question about how velocity is calculated when an object is moved manually: good question, I have not played around with this myself yet. Would be interesting to find out the answer to that though, if for example you wanted some bodies to be simulated with physics but some with Cocos2D…
Hey Ray, thanks for this great tut! Only 1 question: Is there a way to check the collision only 1 time?
@nitro – Thanks! What do you mean only check the collision one time? Box2D will tell the contact listener which objects collide each frame, which we store into an array, and then march through the array to process the objects that are colliding.
We have to keep checking each frame, since the objects are moving they could collide (or not) each frame.
Nevermind, I forgot the toDestroy.push_back code. Now it works :)
Hello Ray,
First off, thanks for the tutorials, they have been very helpful.
I thought I would mention a couple of things with respect to using VertexHelper with Box2D that are not readily apparent, but can cause problems if you aren’t aware of them.
When going through this tutorial, I decided that I would use the “initialization” method rather than the “assignment” method to add the verts to my collision shape (using spriteShape.Set()). When I ran the simulation, I kept hitting an assertion in Box2D, which I realized was due to the fact that the shape defined by the verts was not convex! I think that the reason this was not an issue for the tutorial is because you assign the verts directly, and then assign the position of the shape’s centroid directly (also, the shape’s normals aren’t set at all in this tutorial). If you were use this same technique for a non-sensor body, the simulation would, at best, behave strangely, and at worst, would probably crash. The b2PolygonShape.Set() function handles computing the centroid based on the verts, as well as sets up the normals, and runs some checks (in debug mode) to ensure that the verts form a convex polygon, so I believe using that method is much safer than assigning the verts directly.
Anyway, I just wanted to point out those things, because they are not obvious, particularly to newcomers ;)
Hi @toadkick,
you are right about the shape having to be convex. I’ve put VertexHelper together in a couple of hours to make the task of defining vertices a lot easier for a project I was working on. It is still lacking a lot. A warning when the shape is not convex would be nice as well as a warning when Box2D vertices are not defined counter clockwise.
The tool kind of assumes some knowledge of box2d/chipmunk. But if I (or someone else) find some time, I’ll try to put in those improvements. I hope it makes your life a little easier nonetheless.
- Johannes
It has, it’s quite a nice little tool :) Thanks for sharing it with us!
@toadkick – Thanks so much for pointing this out! I am such a n00b to Box2D that I didn’t realize this myself. I didn’t want to confuse anyone else with this, so I’ve updated the tutorial to make the shape convex and use the Set() method rather than direct assignment.
Thanks again for your help! :]
Hello Ray. I’ve been using cocos2d and box2d just being impressed from your nice tutorials. I’m using box2d in my new game. One problem that I’m having is about collision detection.
In your tutorials, you use to destroyBody when it collides. But I need to stop moving body (in fact a ball of size 24×24) when it collides with any of other bodies (which are also balls of the same size).
I’ve done it in two ways… One, by setting linear velocity of moving body to b2vec2(0,0) as it collides. Second, by simply changing the type of moving body to b2_staticbody.
In both cases, body stops at a distance from the body with which it is colliding. I just want to stop it at the point where it collides, having absolutely no distance.
Am I using wrong method to stop the body or I must handle the gaps(distances) manually?
Thanks for your help and once again obviously for your very interesting and helpful tutorials thank you very much.
@Atif – Good question! I played around with a bit in the breakout project and saw the same issue you were talking about at first. But I was able to get good results by setting the restitution for the ball and the blocks to 0, the friction for both to be high (10), and then using the SetLinearVelocity to b2Vec2(0,0) as you mentioned.
Let me know if that does it for you!
I did it as per your suggestion but it doesn’t work as desired. Still having gaps…
Body size is critical for collision detection. Larger bodies are easier to detect collision. My ball size (24×24) is rather smaller.
Using physics was a great excitement but unfortunately it hasn’t worked for me atleast for collision detection. May be its my own misunderstanding!!
I’m trying a very similar thing to Atif,
I define a Body and Fixture for my GameEntity, the collisions are actually detected perfectly, but I need to stop moving body (80×80 px) when it collides with another body (80×80 px).
But the problem is, my moving body A doesn’t even Stop and he just goes trough my (not moving) body B.
It reports a collision though, but they pass through each other ( as in this picture : http://bit.ly/cKXpIa )
Any ideas ?
@Johannes Fahrenkrug hey I made some modifications to your code, so it can’t go clockwise and only allow you to draw convex polygons I made a comment on your blog with the link to the source code, I hope it help :)
PS: Thank you for the app, im learning cocos2D + box2D and I found it very usefull.
- Ivan Bastidas
@Nicolas – Not off the top of my head, but if you figure it out pls. post here so others can benefit! :]
@Ivan: Very cool stuff! :]
@Ray BTW man I love your tutorials, im trying to make a game with a MarioBros like movement, but I don’t know yet how to start. I hope you make a tutorial about animations and AtlasSprite because I dont understand that very well :S.. ty very much.
(Sorry for my english)
- Ivan Bastidas
at the tick method ->
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[self removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}
the line -> “[self removeChild:sprite cleanup:YES];”
shall be “[_spriteSheet removeChild:sprite cleanup:YES]; ” ?
and thank you for these great tutorials.
I’m learning it through you blog, it’s very useful.
@xhan: Whoops! I had that in the sample project, but for some reason it was incorrect in the post above. I’ve fixed that up now. Thanks for pointing it out!
Thanks for the great tutorial.
One minor correction…
When you wrote:
“Then add the following member variable to HelloWorldScene.mm:
b2World *_world;”
It should read:
“Then add the following member variable to HelloWorldScene.h:
b2World *_world;”
The member var needs to be declared in the header, not the implementation file.
@Laurent: Nice catch, I’ve updated the post, thanks!
@Atif adjust your Bodyshape perhaps this is the issue. as you are saying that body did stop but at distance check both ball shape and the obstacle shape with which you want to collide
hey ray thanks for this wonderful tutorial specially for contact class . i have one issue how can i apply momentum on a body let clarify the issue like in pinball game i have stick if ball is touched with stick then it gets speed as i did but this is not i want what i want when i move the stick if it touch the ball the ball should get the real momentum like in baseball Or in Cricket .. any ways thank you your tutorial and work is very beneficial to me.
@umer: Sorry not sure off the top of my head, would have to play around with it… maybe try the Box2D forums!
I really love your tutorial.
Which is very helpful for the beginner like me
Hello Ray,
Thanx for such awesome tutorial.
I am facing a problem using box2d.I have created a game thats uses all collision concepts described in this tutorial.It runs fine on simulator but when i run it on my ipod frame rates just drop down to 30.My game has only 15-25 objects so i am confused on such a massive frame rate drop.
Any insight regarding this will be really helpful.
Plus keep making wonderful tutorials for us :P.
Azeem
Hi Ray,
Firstly I thank you for these brillliant tutorials. I owe almost everything I know about Cocos 2D to them.
I managed to implement the collision detection but there’s a slight problem when I try to customize the bounding box with Vertex Helper.
My main character sprite is the first frame from a spritesheet that Im using to call an animation on the character. So even if I draw the box around the first frame in that sheet, he box “moves up” as the animation on the character gets called. However, if I done meddle with the bounding box, the rectangle remains intact even while playing the animation.
Is there a way to fix this or do I have to define a dummy sprite for each animated character to handle collisions?
Thanks a lot :-)
@Abdul: Hm the only way to tell would be to profile your code to figure it out. Read up on the “Time Profiler” tool in Instruments, or you could go low-tech and use NSLog debugging printing out some timing statements. But yeah the simulator will usually give you higher FPS, because you’re running your code on your PC which is lot more powerful, so device testing is where it’s really at!
@Chirag: Thanks for the kind words! Unfortunately I haven’t tried this myself so I’m not sure what the issue is off the top of my head. However it sounds interesting, so if you figure it out please post here so others may benefit!
Hi Ray
I appreaciate your work and include your code totally in my project. i got it working as i expected so firstly I want to thank you so much.
I am currently destroying the bodies with bullets, and 1 bullet is enough to destroy. when I wanted to create more stronger bodies which can be killed by 2 or more bullets, i call the “todestroy.push_back” method when the desired amount of bullets collided to my body.
The problem appears here, when we do not destroy the body as soon as the first collision happens, that collision happens lots of times when the bullets totally leaves the body. It enters the bodies frame and makes 4 or 5 collisions until it leaves the body..
As a solution , I erase the contact from the contact listeners vector when i first processed , so i can count now the first contacts of the bullets only..
I am questioning , am i doing it in a right way ? or there is a better solution you can suggest.
thank you in advance.
@Selim: I had the same situation in one of my games. What I ended up doing was have the monster object keep track of the bullets that had already collided with it, and ignore the collision if the bullet had already hit it. I’m sure there are several other solutions as well! The only drawback I can see with your method is could it prevent one bullet from hitting more than one monster at a time?
I have been using all your tutorials to learn cocos2d and Box2D and have been getting a very good grasp thanks to your careful explanations. Thank you so much for making your tutorials. I am trying to wrap my mind around setting this up instead of the cocos2d rectIntersect method from your turret game.
In my game I don’t have projectiles just the player moving around touching other sprites just as your car(my player controlled sprite) and cat(my targetSprites) do. I also have obstacles that the player needs to not be able to pass through. There also obstacles that neither player nor targets can pass through.
My trouble is with implementing the collision detection for the obstacles. I cant get it to work with the old method from the turret game(rectIntersect) nor with the Box2d method here. Once my player collides with an obstacle he stops moving and his rect is always intersecting the obstacle’s rect. So my check for intersect basically never allows the player to break free from next to the obstacle.
Any ideas on how best to implement this? I don’t need code just psuedo-code or explanations(unless you wanted to add an obstacle to the car/cat game). I’m gonna re-check your break-out game for ideas too.
Also, could you put some more comments in the sample code – specifically explaining code groups within the spriteDone and tick methods? I’m trying to understand exactly how these methods work so I can implement mine correctly for my game.
Thanks again Ray, if you have time to help that would be greatly appreciated. I know can be busy though, so if not I understand. I can provide my email if that’s easier. B)
I got the detection to work correctly!! I had to stop the actions and then ‘kick’ the player back away from the barrier. I did this by saving the prev position and then using the relation between current and prev position to determine which way to bump the sprite. I defined a small default bump distance(far enough to make the rects no longer intersect) and then used the CCPlace to make the sprite pop away from the barrier from whence he came! Yay!
Also, I had to shrink the barrier rect by 10% so that can easily navigate around the corners. I used the original rect intersect method you used. I still cant get the box2d version used here. I think the polygone one would be perfect for my game so that I could cut the corners off my sprites but I just don’t have a grasp on the box2d. I understand most of what you did here but when I apply it to my game I am not doing something correctly. I’ll try pick it apart again. Just wanted to post the ‘push back’ method I used in case anyone else had the same issue. Thanks again Ray, please keep making tutorials. How about one using the polygon method? hehe B)
Hey,
I kinda have another problem. My game is a sidescrolling shooter where both my hero and enemies can shoot each other. I started off by running everything from a single class. Now referring to your turret shooting game I tried to move my sprites into another class called ActorManager. The problem is my bullets are being cleaned up from my GameSceneClass. So I cannot move that to the Actor Manager right (since it makes a call to world objects). Now how do I call the attacking action on the sprite? Here’s how I do it now:
-(void)enemyGenerator:(id)Sender
{
ActorManager *wannabeBoss = [ActorManager spriteWithTexture:wannabeBossSheet.texture rect:CGRectMake(0, 0, 112, 85)];
[wannabeBoss setCanTrack:YES];
[self addBoxBodyForSprite:wannabeBoss];
[wannabeBossSheet addChild:wannabeBoss z:1 tag:kTagWannabeBoss];
wannabeBoss.scale = 0.85;
[ActorManager track:wannabeBoss];
int yloc = [[spawnLocY objectAtIndex:spawnIndex++]intValue];
NSLog(@”%d”,yloc);
wannabeBoss.position = ccp(winSize.width + (wannabeBoss.contentSize.width/2),yloc);
[wannabeBoss runAction:[CCSequence actions: mAttackingStateMoving, callback, mAttackingStateShooting, mAttackingStateMoveOut, enemyDone, nil]];
}
where the callback generates the bullet at the sprite location and the enemyDone cleans up the Sprite.
What I want to do is something like what you did on the monster:
[wannabeBoss attack];
that automatically generates the whole sequence.
Pls. help
@Mark: Cool glad you got it working! And sure thing – I just updated the sample project with some detailed comments in the spriteDone and tick method, hope that helps!
@Chirag: One way to accomplish that is to pass your ActorManager a pointer to your Layer and have it call a method on the layer such as “createBulletAtPoint:(CGPoint)point”. The layer can then be responsible for creating the Box2D body for the bullet, adding it to the world, and creating a Cocos2D sprite for the bullet and adding it to the layer. You could also factor this logic into a helper class if you’d like as well.
Ray,
Thanks a lot for the prompt answer, but Im an absolute beginner. Could you please elaborate with an example code?
Hi Ray,
Thank you so much for your tutorial! I have encountered some problems which I can’t solve.
I have this method, addTarget which creates a sprite using the spritesheet. I also have a timer which runs every second to create another new sprite using the addTarget method. In my addTarget method, I tried adding your code
“[self addBoxBodyForSprite:target];”
The project compile without any problems but crashes right after. Also it is weird, when I use
“[_spriteSheet addChild:target z:0 tag:1];”
the sprite does not appear on the screen. Instead I have to use
“[self addChild:target];”
in order to get the sprite there, am I doing it wrongly? I’m new to the iPhone platform.
Hi Ray,
Thank u Thank u for your tutorials! I want to ask, if [_spriteSheet removeChild:sprite cleanup:YES]; replaces the spriteDone: method ?
Thanks
Hi Ray,
Thanks for putting the comments in – it helped me understand! Now I am having a different problem(sorry it is so long, just trying to make sure you get the whole picture): in my tick method I have determined the bodies to destroy and then in the section where I loop through the bodies to destroy I am trying to update my score and determine if I need to do gameOver or nextLevel methods. But to do this determination I need to know the target.tag value(type of target) of the CCSprite that matches the b2Body(because I have subClassed CCSprite with values containing how that type of sprite effects the score[like your StrongTarget/WeakTarget] and need to reference those values as well). Problem is I do not pass the sprite to the tick method so I have no reference to the sprite target. Is there a reference value within the b2Body object for its sprite? Or could I create a subClass of b2Body with a pointer to its corresponding CCSprite object? Or would the other way around work better and subclass CCSprite with a pointer to its corresponding B2Body object?
Also related to the subClassing options, I tried to setup a global b2Body object for my player sprite(which is also global) but got a message saying that I could not use the ‘retain’ because b2Body is not an object type. Is b2Body not an object class? Doesn’t it ultimately come from NSObject? If not, would this effect how I did the subClassing? I was trying to set it up like this:
in–GameScene.h–
@property (nonatomic) b2Body *b2Dplayer;
…
@property (nonatomic,retain) b2Body *b2Dplayer;
and then in –GameScene.mm–
@synthesize b2Dplayer = _b2Dplayer;
Any ideas or alternate solutions would be appreciated.
Thank you! B)
@Chirag: Unfortunately, I don’t have time to write sample code for this. The best thing to do is keep working at it yourself – you’ll learn more that way anyway. If you can’t get it working, maybe try something simpler!
@Hexa: Regarding the crash issue, I’d recommend stepping through with the debugger to learn specifically where the crash is occuring and check to make sure that all of the variables are set as you would expect. Regaring the sprite not appearing, it’s better if you add your sprite a child of the spritesheet as you will get better performance that way. If it’s not working, try comparing your project to the sample project to see what might be different.
@kosskevin: We call that method from the spriteDone method, but I’m not sure what you’re asking beyond that?
@Mark: Glad the comments helped!
Notice that in the b2BodyDef we set the sprite as the userData parameter – so if you have a b2Body, you can get the sprite pointer by calling b->GetUserData(), like we do in the spriteDone method.
Going the other way, yes it’s common to store a pointer from the CCSprite back to its b2Body as an instance variable if you subclass CCSprite as well.
b2Body is a C++ class, not an Objective-C class, which is why you can’t retain it. You can just set up a global variable such as “b2Body * g_myGlobalBody” at the top of your .m file, no need for properties. If you’re unfamiliar with C++ you might want to read up on it a bit, as you’ll need to know a good bit of C++ if you want to use Box2D effectively.
Thanks Ray I’ll get cracking on the new info right now! Thanks again for the help.
Hey,
Thanks for the reply..I got it working on the same layer by defining a function to spawn a bullet and calling it using a CCCallFuncn whenever I need to generate a bullet..
1. However, the new problem lies with the character shooting the bullet. Since Im calling the enemyGenerator from a scheduler, the character sometimes dosent respond to collisions when there are abt 3 enemies on the screen. The collision is definitely deected though, since the bullet hitting the enemy disappears as it should. The problem is that the enemy dosent burst after the health is 0. Is there something under the hood that is causing this selective response to collision?
2. For the earlier problem regarding the size of the bounding box im using tags to identify the calling sprite on addBoxBodyForsprite(). This works pretty well except for the fact I cant control the centre of the bounding box. Is there a way to probably offset the centre of the bounding box?
Thanks for these great tutorials once again..
@Chirag: Regarding the collisions, there isn’t anything under the hood that would cause selective collision detection that I know of, so I’d double check your logic to make sure all is OK.
Regarding setting the center of the bounding box… you might want to take a look at this tutorial again, which explains the difference between body definitions/objects, shapes, and fixture definitions/objects:
http://www.raywenderlich.com/457/intro-to-box2d-with-cocos2d-tutorial-bouncing-balls
Basically, in the addBoxBodyForSprite we set the position of the b2Body to be the same as the sprite’s position (which is by default the center of the sprite), and then the SetAsBox method is set up to create a bounding box for the object centered around the position of the b2Body.
If you have a different center point, you can set the position in the b2BodyDef to your center point, but don’t forget to update the tick() method and any other areas that reference the position the same way as well.
Ray,Thanks a lot :-)
Got the centre point fixed. I shall look into the logic of the collisions, although its essentially the same as your tick method with a block of if conditions to handle collisions between specific sprites based on tags.
Hi Ray,
1. Alright. So I might be getting this wrong, but for the centre of the box I went back and did this:
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_dynamicBody;
if(sprite.tag == kTagHero){
spriteBodyDef.position.Set(sprite.position.x/PTM_RATIO – 50,
sprite.position.y/PTM_RATIO);
}
else{
spriteBodyDef.position.Set(sprite.position.x/PTM_RATIO,
sprite.position.y/PTM_RATIO);
}
spriteBodyDef.userData = sprite;
b2Body *spriteBody = world->CreateBody(&spriteBodyDef);
.
.
.
Thats for moving the box for my hero backwards by 50 pixels. But it dosent work.
If I do something similar for spriteBody rather than spriteBodyDef it gives me an error at that line.
2.
I rechecked my tick method,and I cant fathom why it works sometimes and sometimes it dosent. Would it be possible for you to have a quick look at my tick method?
@Chirag: I think you want (sprite.position.x – 50) / PTM_RATIO, not the other way around – assuming 50 is 50 pixels. Remember that dividing by PTM_RATIO is what converts your position from Cocos2D (pixel) position to Box2D position.
Have you enabled Box2D debugging so you can see where the Box2D bodies are located with respect to your sprites, btw?
Regarding your tick method, unfortunately I don’t have time to review people’s projects.
Hi Ray, your tutorials have been really useful, thank you for them.
I have been trying to follow this tutorial as I would like to use the spritesheet part for my new game. I have followed it to the letter up to the part where you spawn the car to just get cocos2D to draw the car (but with my own two images) and I created a standard cocos2d project with 0.99.4 (not the box2d template). I understand the code and see no problems but whenever I run the app in the simulator it crashes at the line “CCSprite *car = [CCSprite spriteWithSpriteFrameName:@"car"];” with the signal SIGBRT. If I look at the information the debugger is telling me it crashes specifically at the line “NSAssert(texture!=nil, @”Invalid texture for sprite”);” in CCSprite.
Do you know what is happening?
I think i have found the source of the problem. I was comparing the .plist files created by Zwoptex online and Zwoptex app. The Zwoptex app exports plist differently and the actual data in the plist file is presented differently. For some reason (even with export to cocos2d set) it makes cocos2d crash. Looks like its best to stick with the flash one.
@JetWilson: Cool, thanks for sharing your solution!
haha I ran the project while the volume was at max, my wife woke up scared and I jumped off the chair. The “hahaha” sound is scary :)
Again, amazing tutorial, thanks!
Followed all your tutorials with great interest and all is well explain. Both thumps up ;)
But here I’m facing a compilation error that I can’t solve by myself:
Command /Developer/Library/Xcode/Plug-ins/CoreBuildTasks.xcplugin/Contents/Resources/copyplist failed with exit code 127
This happen right after including the “sprites.plist” to my ressources folder.
This doesn’t seem to be a code issue. So, I tried to re-install XCode just to be sure I’m not missing any plugin but still. Also, I do get the same error when building your downloaded final project.
Does anyone have an idea ?
Regs
@Alfred: Lol that’s hilarious about the sounds, didn’t know my voice was that scary! :]
@Sam: Hm… maybe re-add the sprites.plist to your resources folder, but make sure that when you do you choose “Copy items into destination group’s folder (if needed)”?
Don’t know why but as soon as I add any file.plist to my ressources folder, I got that error. Even with an empty project with a new plist, that error comes in!
It’s not linked to your tutorial but most probably something wrong with my XCode software!
Hello Ray,
I can’t tell you how much your tutorials are helping me out…
I’m making a turn based box2d game. When a player makes his move, the next player’s turn starts after all activity has ceased in the box2d world.
My question is: Is there a way to know if there are any currently awake objects in the box2d world or do I have to iterate through all the bodies checking the isAwake state for each one individually?
Another question: Is there a listener/event that is fired when the world “sleeps” or “awakens”?
Thanks a lot!
@Spencer: Thanks! Regarding your question, it’s been a while since I’ve played with Box2D, so don’t know off the top of my head. If you figure it out please post the solution here though :]
hey rey, in this tutorial u delete the cat object if it is collide……please tell how to move reverse the direction of cat if it collides…..
@indian: Here’s the general process for one way of solving this: a) add a flag to the cat object to indicate whether it is advancing or retreating, b) when checking for collisions, only check for cats that are advancing, c) if a collision is detected, switch the cat to retreating, cancel any actions currently running on it, and add a new action to make it go the other way.
Hi,
Enjoying the tutorials. I am seeing one error. I haven’t been able to get it to work. In addBoxBodyForSprite.
‘PTM_RATIO’ was not declared in this scope.
Any idea why I am getting this error.
spriteBodyDef.position.Set(sprite.position.x/PTM_RATIO,
@Joseph, I had the same problem. I can only assume it was covered in a previous lesson.
define PTM_RATIO yourself and set it to 32:
#define PTM_RATIO 32
Congrats man, incredible post!
Hi, thanks for your post. You did fantastic job!
I am working with cocos2d with box2d and i need some tips. I am working with some “vision test” to detect when an enemy can shoot to the character. I am thinking of using a body from the enemy to the caracter to shoot and detect only the collision but ignore the physics. Any idea?
Thanks!!
@Joseph, @Martin: Thanks guys, I’ve added this to the tutorial to avoid confusing any future readers :]
@FabioTNT: Thanks glad you liked it! :]
@Nicolas: There’s actually a really nice feature built into Box2D that can help you with exactly that called raycasting. The idea is you can draw a line (called a ray) from a point to a certain direction, and Box2D will tell you what it intersects! It’s great for vision tests like that.
Hi Ray,
I am a student from USC. I am building a mario like game using box2D and tilemap both combined.Your tutorials are very helpful to us in understanding each and every function.I tried the contact between two bodies using sprites using the tag and it works well.
How can I determine which 2 bodies are in contact when I use the tilemap objects.In this case,I cannot use the tag to determine the objects.How can I link the tilemap objects to the colliding bodies
@Raghav: You can put whatever you want as the userdata for a Box2D body. You obviously need some kind of data structure to identify the objects that you want to check if they’re colliding – whether they are subclasses of CCSprite, custom game objects, etc. Whatever it is, you can pass that in as the user data, and then pull it out to see what collided.
hi everybody:
when i use b2MouseJoint,i have a problem. I got no response in simulator when i move my mouse.
Here is my code:
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGl:location];
b2Vec2 MousePosition = b2Vec2(location.x,location.y);
b2MouseJointDef md;
b2MouseJoint *mj;
md.bodyA=groundBody;
md.bodyB=_body;
//_body is the body which i want to move;
md.target=MousePosition;
md.maxForce=1000*_body->GetMass();
mj=(b2MouseJoint *)_world->CreateJoint(&md);
_body->SetAwake(true);
mj->SetTarget(MousePosition);
}
There is no document in box2d manual. So could someone give me a sample code? Thank you in advance.
ok,i find this tutorial: http://www.cocos2d-iphone.org/forum/topic/7219#post-42388
and figure it out. bodyB is the object that i want to move, but i do not quite understand what bodyA means.
@keithlee980: For setting up a mouse joint, you usually put a static (fixed) body as bodyA. Usually in games (like this one) you create a border around an area to act as the “ground” that doesn’t move, so that is a good thing to use for bodyA. Does that make sense?
If you’re not getting a response form your mouse joint, first check and make sure that your ccTouchMoved method is getting called.