How To Make a Catapult Shooting Game with Cocos2D and Box2D Part 1

This is a blog post by iOS Tutorial Team member Gustavo Ambrozio, a software engineer with over 20 years experience, including over three years of iOS experience. He is the founder of CodeCrop Software. You can also find him on Google+. In this tutorial series we’ll build a cool catapult type game from scratch using […] By Gustavo Ambrozio.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Adding the Catapult Arm

It’s time to start adding some physics to this world. Right after the code you added above is the template’s code to create the world borders. Let’s change this a bit to describe our world.

The default world is exactly the size of the iPhone’s screen. Since our scene’s width is twice this size we’ll have to change our world’s width. To do this just find every instance of screenSize.width you can find when defining the world boundaries and multiply it by 2.0f

Also, since our world’s floor is not on the bottom of the screen change the bottom’s edge Y coordinates to FLOOR_HEIGTH/PTM_RATIO. The final code should be this:

// bottom
groundBox.SetAsEdge(b2Vec2(0,FLOOR_HEIGHT/PTM_RATIO), b2Vec2(screenSize.width*2.0f/PTM_RATIO,FLOOR_HEIGHT/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);

// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width*2.0f/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);

// left
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox,0);

// right
groundBox.SetAsEdge(b2Vec2(screenSize.width*2.0f/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width*2.0f/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);

Now let’s add the catapult arm. First let’s add some references to the Box2d body and fixture we’ll create as we’ll need this later on. Go to HelloWorldLayer.h and add this to the class’ instance variables:

b2Fixture *armFixture;
b2Body *armBody;

Now go back to the class implementation file and add this at the bottom of init:

// Create the catapult's arm
//
CCSprite *arm = [CCSprite spriteWithFile:@"catapult_arm.png"];
[self addChild:arm z:1];

b2BodyDef armBodyDef;
armBodyDef.type = b2_dynamicBody;
armBodyDef.linearDamping = 1;
armBodyDef.angularDamping = 1;
armBodyDef.position.Set(230.0f/PTM_RATIO,(FLOOR_HEIGHT+91.0f)/PTM_RATIO);
armBodyDef.userData = arm;
armBody = world->CreateBody(&armBodyDef);

b2PolygonShape armBox;
b2FixtureDef armBoxDef;
armBoxDef.shape = &armBox;
armBoxDef.density = 0.3F;
armBox.SetAsBox(11.0f/PTM_RATIO, 91.0f/PTM_RATIO);
armFixture = armBody->CreateFixture(&armBoxDef);

If you already went through the previous Box2d tutorials on the site (and if you haven’t I recommend going through those before continuing) most of this should be to you.

We first load the catapult’s arm sprite and add it to the scene. Notice the z index. We used z indexes when we added the static sprites to the scene, so this z index puts the arm between the two sides of the catapult to look nice.

Notice also that we didn’t specify the sprite’s position. That’s because the tick method will set the sprite’s position to the Box2D body’s position.

Next we create the Box2d body as a dynamic body. The userData here is important because of what I mentioned on the last paragraph, so that the sprite will follow the body. Also notice that the position is set to be above the FLOOR_HEIGHT we defined. That’s because here we have to use the position of the center of the body, we can’t use the left-bottom corner using Box2d.

Next comes the creation of the fixture that describes the body’s physical features. It will be a simple rectangle.

We set the fixture’s rectangle to be a bit smaller than the size of the sprite. This is because if you take a look at the sprite, you’ll see that the sprite is larger than the arm itself:

Making a fixture rectangle smaller than the sprite

In this picture, the black rectangle is the size of the sprite, and the red rectangle is the size we want for the fixture.

You can get these dimensions using any image editing software. Just remember to use the standard resolution image for this as the -hd version gets scaled to the standard size.

Run the app again and you’ll see the arm in a vertical position:

Catapult Arm added to level

So far so good – now let’s add some movement to this catapult!

Rotating with Joints

We need to somehow restrict the movement of the catapult so that it rotates around a point. The way you restrict motion of Box2D bodies relative to each other is via joints.

No Jay and Silent Bob, not that kind of joint!

No Jay and Silent Bob, not that kind of joint!

There’s one particular joint that is perfect for this – a revolute joint. Think of this as pinning two bodies together at a particular point, but still allowing them to rotate.

So we can “pin” the catapult arm to the ground at the base of the catapult to get the effect we want!

Let’s try this out. Go to HelloWorldLayer.h and add this to the class’ instance variables:

b2RevoluteJoint *armJoint;

Now go back to the class implementation file and add this right after the catapult’s arm creation:

// Create a joint to fix the catapult to the floor.
//
b2RevoluteJointDef armJointDef;
armJointDef.Initialize(groundBody, armBody, b2Vec2(233.0f/PTM_RATIO, FLOOR_HEIGHT/PTM_RATIO));
armJointDef.enableMotor = true;
armJointDef.enableLimit = true;
armJointDef.motorSpeed  = -1260;
armJointDef.lowerAngle  = CC_DEGREES_TO_RADIANS(9);
armJointDef.upperAngle  = CC_DEGREES_TO_RADIANS(75);
armJointDef.maxMotorTorque = 4800;

armJoint = (b2RevoluteJoint*)world->CreateJoint(&armJointDef);

When creating the joint you have to specify 2 bodies and the hinge point. You might be thinking: “shouldn’t the catapult’s arm attach to the base?”. Well, in the real world, yes. But in Box2d not necessarily. You could do this but then you’d have to create another body for the base and add more complexity to the simulation.

Since the base would be static anyway and the in Box2d the hinge body doesn’t have to be in any of the to bodies, we can just use the groundBody that we already have.

The angle limits, combined with a motor make the catapult behave much like a catapult in the real world.

You’ll also notice we set a motor up on the joint, by setting “enableMotor”, “motorSpeed”, and “maxMotorTorque”. By setting the motor speed to negative, this makes the catapult arm want to continually rotate clockwise (kind of like a spring on a catapult).

However, we also enabled limits on the joint by setting “enableLimit”, “lowerAngle”, and “upperAngle”. This makes the arm stop rotating at the 9 degree angle (slightly bent to the left) and a 75 degree angle (pulled back a good bit to the left). This simulates the movement of the catapult arm we want.

In a while we’ll add another joint that will let you pull back the catapult. When we release this force the motor will make the arm fling forward, much like a real catapult would!

The motor speed value is in radians per second. Yes, not very intuitive, I know. What I did was to just try out different values until I had the desired effect. You can start out small and increase it until you get the desired speed for the arm. The maxMotorTorque parameter is the max torque the motor can apply. This value is also something that you can vary to see how things react. Again it will become clear soon.

Run the app and you’ll see the arm bent to the left now:

Catapult arm connected to ground via a revolute joint

The repository tag for this point in the tutorial is CatapultJoint.

Gustavo Ambrozio

Contributors

Gustavo Ambrozio

Author

Over 300 content creators. Join our team.