Getting Started

Next, you’re going to add what I like to think of as the “Hello, Box2D!” code. You’ll create a Box2D world and add some code to set up debug drawing and add some test shapes, just to make sure you have everything working.

This adds the Box2D header and the debug draw header file. It also declares the pixel-to-meter ratio (PTM_RATIO) to 32.0. As a refresher, this is what you need to use to convert between Box2D units (meters) and Cocos2D units (points).

Then add the following new methods to HelloWorldLayer.mm, above the onEnter method:

The code you added to onEnter just calls the new method you wrote earlier to set up the Box2D world. You also modified the line to initialize the Terrain class to pass in the Box2D world. This way it can use the world to create a Box2D body for the hill eventually. You’ll write some placeholder code for this next.

The code you added to update gives Box2D time to run its physics simulation each update by calling _world->Step. Note that this is a fixed-timestep implementation, which is better than variable rate timesteps when running physics simulations. For more information on how this works, check out our Cocos2D book‘s Box2D chapters.

The code you added to ccTouchesBegan:withEvent: method runs the helper method to create a Box2D body wherever you tapped. Again, this is just for debugging to verify Box2D is working OK so far.

Notice that this gets the coordinate of the touch within the terrain coordinates. This is because the terrain will be scrolling, and you want to know the position within the terrain, not the position on the screen.

Next, you’re going to make some changes to Terrain.h/m. Make the following changes to Terrain.h:

// Add to top of file#import "Box2D.h"// Add after setOffsetX: method-(id)initWithWorld:(b2World *)world;

This just includes Box2D, and predeclares a new initializer that will take the Box2D world as a parameter.

This is just a helper method that creates a Box2D body along the bottom of the hill, to represent the “ground.” This is just a temporary method so we can add bodies and have them hit something rather than falling endlessly, you’ll replace this later to model the hill itself.

For now, all it does is figure out the x-coordinate for the first and last keypoints, and draw an edge connecting the two.

Next, add a few more modifications to Terrain.m in order to call this code, and set up debug drawing:

Every time the hill vertices are reset, you call resetBox2DBody to create the Box2D representation of the visible hills. Right now the body never changes (it’s just adding a line for the ground) but later on you’ll fix up the Box2D body to model the visible hills.

setupDebugDraw and the draw code is what you need to set up debug drawing for the Box2D objects. If you’re familiar with Box2D, this should be review.

However, you may be wondering why the debug draw code is in Terrain.m, instead of in HelloWorldLayer.mm. This is because scrolling is implemented in this game by moving Terrain.m. So to match up the coordinate system of Box2D with what’s visible on the screen, we need to add the debug drawing code into Terrain.m.

One last step. If you try to compile right now, you’ll get a ton of errors. This is because Terrain.m imports Terrain.h, which imports Box2D.h. And whenever you have a .m file import C++ code (like Box2D) you get a ton of errors.

Luckily the solution is simple – renamed Terrain.m to Terrain.mm.

Compile and run, and now as you tap you can see a lot of circle objects fall into your scene!

Shaping the Hills in Box2D

Right now you have a Box2D shape representing the ground along the bottom of the screen, but what you really want is a shape representing the hills.

Luckily, this is quite easy since you already have all of the pieces in place!

You have an array of vertices of the top of the hill (borderVertices). You created this in the last tutorial when you generated the triangle strip in resetHillVertices.

You have a method that is called whenever the vertices change (resetBox2DBody).

So you just need to replace resetBox2DBody to create edges for each entry in borderVertices! Replace it with the following code:

This new implementation first looks to see if there’s already a Box2D body, and destroys the old one first before continuing.

Then it creates a new body, and starts looping through the array of border vertices, which contain the vertices for the top of the hill. For each piece, it creates an edge connecting the two.

Pretty easy, eh? Compile and run, and now you’ll have a nice Box2D body following the slope of your hills!

Adding the Seal

All this time you’ve been working with a project named Tiny Seal, but there’s no seal!

That is just a tragedy, so let’s address that right away.

First, download and unzip the resources file for this project, and drag the “Sprite Sheets” and “Sounds” directories into your Xcode project. For each directory, verify “Copy items into destination group’s folder” is selected, and click Finish.

The createBody method creates a circle shape representing the seal. This is almost exactly the same as the createTestBodyAtPosition method you wrote earlier, except it bases the radius on the seal size (actually smaller than the seal, for the collisions to work right).

Also, the friction is set to 0.2 (so the seal is extremely slippery) and the restitution is set to 0 (so the seal doesn’t bounce when he hits the ground).

It also sets some linear damping on the body so it tends to slow down over time, and sets the body to fixed rotation since it doesn’t really need to rotate for this game.

The initWithWorld: method initializes the sprite to a particular sprite frame (seal1.png), squirrels away a copy of the world, and calls the above createBody method.

The update method updates the position of the seal sprite to match the position of the Box2D body, and the rotation of the sprite based on the seal’s velocity.

Next, you need to make a few modifications to Terrain.h and Terrain.mm, because you’re going to add a sprite batch node inside Terrain.m.

The wake method applies an impulse to the upper right to get the seal initially moving.

The dive method applies a strong impulse down, and an impulse slightly to the right. The down impulse will cause the seal to collide off of the hill, and the stronger the slope of the hill the more the seal will want to “fly up” once the next hill arrives.

limitVelocity just makes sure the seal is moving at least 5m/s² along the x-axis, and no less than -40m/s² along the y-axis.

Almost done – just a few modifications to HelloWorldLayer. Start with HelloWorldLayer.mm by adding the following instance variable inside the @interface:

This creates an animation for when the seal is moving normally, and a method to run that animation. The “animation” for diving is actually just a single frame, so we add a helper method to set that too.

Finally make a few changes to HelloWorldLayer.mm:

// At top of file#import "SimpleAudioEngine.h"// At end of onEnter[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"TinySeal.caf" loop:YES];
// At start of update, add an else case for the if (_tapDown):else{[_hero nodive];
}// Inside ccTouchesBegan[_hero runForceAnimation];
// Inside ccTouchesEnded AND ccTouchesCancelled[_hero runNormalAnimation];

Finally go to Terrain.mm and comment out the call to _world->DrawDebugData in the draw method.

Compile and run the code, and now your seal can ride the hills in style!

Where To Go From Here?

At this point, you have a basic game working. Why not try to tweak the behavior of the seal so he moves in a more natural way up and down the hills? Or start adding in items to pick up, decorations on the hills, points to collect – your imagination is the only limit!

If you extend the game to add any cool new features or have any comments or questions, please join the forum discussion below!

Ali is an independent iOS and Android developer currently focussing on building immersive experiences on mobile devices. He is an avid programmer and loves learning better and faster ways of solving problems. You can follow him on Twitter or github.

To join the discussion, enable your javascript or head over to the forum over here

User Comments

13 Comments

Only skimmed through (I'm at work) but am I missing something? The code here seems to be in Objective-C - which surely isn't supported on non-Apple platforms? Doesn't this all need to be translated into C++ to work on a non Apple device?

Sorry. No offense intended, but did you miss the part about this being a (mostly) iOS site? Other platforms are covered in some amount of detail, but not to the extent that iOS is covered.

Answering your question, you would have to "translate" for other platforms; both language and API. Still, this site is a great resource for all platforms if you have enough experience to use what you learn here in other places, with other languages and tool sets.

Maxxx wrote:I know the site is mainly IOS - but I thought the idea of Cocos2d-X was to be cross platform - otherwise why use it over Cocos2d-iPhone at all?

I'm not being critical - just wondering if I am missing something?

It's not a tutorial for Cocos2D-X. Maybe you misread the title, it says:

How To Create A Game Like Tiny Wings with Cocos2D 2.X Part 2

And not Cocos2D-X so this tutorial is aimed for Cocos2D with versions 2.X. I understand your confusion because they recently updated 2 Cocos2D-X tutorials. A tutorial like this for Cocos2D-X would be nice though.

Well , I tried this stuff in android , it worked for me but few things are still bothering me. With your Figures provided for seal ,When I'm running down the slopes the character speeds up way to fast and the user did nt get much time to release the button . Like in tiny wings , bird move around hills in good speed and user gets enough time to release the button and get perfect fly.
Any Suggestion why it's happening .
THanks

It is in center of screen, but the origin of sprite(seal) is (0.0,0.0), but (HEIGHT_DEVICE/2, 0) is deplacement of origin, this is visible because when it goes up/down on the hils it is rotated, and the seal is going out of screen(up/down like a semi-rotation).

hey there,
so i downloaded this version of tiny wings (https://github.com/haqu/tiny-wings), but when the player reaches end of level 1 it keeps falling because theres no more terrain. how do i load a new world??

i managed to add a new player to run agains me. but he doenst fly like my player. is there a way to simulate a real player behavior?
also can someone help me with the terrain? i need to generate the level 2 hills once the hills from level 1 disappear.
Can someone give me a hand here?