In this section, we’ll create every bullet body on the beginning of the game and we’ll use them one by one. So we need a place to store them all. Go to the .h file and add these variables to our class:
Go back to the implementation file and, before we forget, add this to the dealloc method:
Add a method to create all the bullets above the init method:
currentBullet = 0;
CGFloat pos = 62.0f;
if (count > 0)
// delta is the spacing between corns
// 62 is the position o the screen where we want the corns to start appearing
// 165 is the position on the screen where we want the corns to stop appearing
// 30 is the size of the corn
CGFloat delta = (count > 1)?((165.0f - 62.0f - 30.0f) / (count - 1)):0.0f;
bullets = [[NSMutableArray alloc] initWithCapacity:count];
for (int i=0; i<count; i++, pos+=delta)
// Create the bullet
CCSprite *sprite = [CCSprite spriteWithFile:@"acorn.png"];
[self addChild:sprite z:1];
bulletBodyDef.type = b2_dynamicBody;
bulletBodyDef.bullet = true;
bulletBodyDef.userData = sprite;
b2Body *bullet = world->CreateBody(&bulletBodyDef);
circle.m_radius = 15.0/PTM_RATIO;
ballShapeDef.shape = &circle;
ballShapeDef.density = 0.8f;
ballShapeDef.restitution = 0.2f;
ballShapeDef.friction = 0.99f;
[bullets addObject:[NSValue valueWithPointer:bullet]];
Most of this method should be familiar to you by now. Our method will create a variable number of bullets, evenly spaced between the first squirrel and the catapult’s body.
One detail you might not have seen before are the “bullet” parameter of the bulletBodyDef. This tells box2d that this will be a fast moving body so box2d will be extra careful with it during the simulation.
The box2d manual explains it well why we need to mark these bodies as bullets:
Game simulation usually generates a sequence of images that are played at some frame rate. This is called discrete simulation. In discrete simulation, rigid bodies can move by a large amount in one time step. If a physics engine doesn't account for the large
motion, you may see some objects incorrectly pass through each other. This effect is
By default, Box2D uses continuous collision detection (CCD) to prevent dynamic bodies from tunneling through static bodies. This is done by sweeping shapes from their old position to their new positions. The engine looks for new collisions during the sweep and computes the time of impact (TOI) for these collisions. Bodies are moved to their first TOI and then halted for the remainder of the time step.
Normally CCD is not used between dynamic bodies. This is done to keep performance reasonable. In some game scenarios you need dynamic bodies to use CCD. For example, you may want to shoot a high speed bullet at a stack of dynamic bricks. Without CCD, the bullet might tunnel through the bricks.
We’ll now create a method to attach the bullet to the catapult. We’ll need two more class variables for this so let’s add them to the .h file:
The bulletBody will keep track of the currently attached bullet body so we can track it’s movement later. The bulletJoint will keep a reference to the joint we’ll create between the bullet and the catapult’s arm.
Now go back to the implementation file and add the following right after createBullets:
if (currentBullet < [bullets count])
bulletBody = (b2Body*)[[bullets objectAtIndex:currentBullet++] pointerValue];
bulletBody->SetTransform(b2Vec2(230.0f/PTM_RATIO, (155.0f+FLOOR_HEIGHT)/PTM_RATIO), 0.0f);
weldJointDef.Initialize(bulletBody, armBody, b2Vec2(230.0f/PTM_RATIO,(155.0f+FLOOR_HEIGHT)/PTM_RATIO));
weldJointDef.collideConnected = false;
bulletJoint = (b2WeldJoint*)world->CreateJoint(&weldJointDef);
We first get the pointer to the current bullet (we’ll have a way to cycle through them later). The SetTransform method changes the position of the center of the body. The position in the code is the position of the tip of the catapult. We then set the bullet body to active so Box2d starts simulating it’s physics.
We then create a weld joint. A weld joint attaches two bodies on the position we specify in the Initialize method and don’t allow any movement between them from that point forward.
We set collideConnected to false because we don’t want to have collisions between the bullet and the catapult’s arm.
Notice that we return YES if there were still bullets available and NO otherwise. This will be useful later to check if the level is over because we ran out of bullets.
Let’s create another method to call all these initialization methods right after attachBullet:
Now add a call to this method at the end of the init method:
Run the project and you’ll see something weird:
The corn is a little off the mark. That’s because the position I set for the corn to attach is the position for when the catapult’s arm is at 9 degrees, at the resting position. But at the end of the init method the catapult is still at the zero degree angle so the bullet actually gets attached to the wrong position.
To fix this we only have to give the simulation some time to put the catapult’s arm to rest. So let’s change the call to resetGame on the init method to this:
[self performSelector:@selector(resetGame) withObject:nil afterDelay:0.5f];
This will make the call half a second later. We’ll have a better solution for this later on but for now it’ll do. If you run the project now you’ll see the correct result:
If we now pull the catapult’s arm and release the corn will not be released because it’s welded to the catapult. We need a way to release the bullet. To do this we just have to destroy the joint. But where and when to destroy the joint?
The best way is to check for some conditions on the tick method that gets called on every simulation step.
First we need a way to know if the catapult’s arm is being released. Let’s add a variable to our class for this first:
Now go back to the ccTouchesEnded method and add this condition right before we destroy the mouse joint:
if (armJoint->GetJointAngle() >= CC_DEGREES_TO_RADIANS(20))
releasingArm = YES;
This will set our release variable to YES only if the arm gets released then the arm is at least at a 20 degree angle. If we just pull the arm a little bit we won’t release the bullet.
Now add this to the end of the tick method:
// Arm is being released.
if (releasingArm && bulletJoint)
// Check if the arm reached the end so we can return the limits
if (armJoint->GetJointAngle() <= CC_DEGREES_TO_RADIANS(10))
releasingArm = NO;
// Destroy joint so the bullet will be free
bulletJoint = nil;
Pretty simple, right? We wait until the arm is almost at it’s resting position and we release the bullet.
Run the project and you should see the bullet flying off very fast:
In my opinion a little too fast. Let’s try to slow it down a bit by decreasing the max torque of the revolute joint.
Go back to the init method and decrease the max torque value from 4800 to 700. You can try out some other values to see what the effect is.
armJointDef.maxMotorTorque = 700; //4800;
Try again and, ah much better.