How to Make a Game Like Jetpack Joyride using LevelHelper, SpriteHelper [Cocos2D 2.X edition] – Part 2

This is a post by special contributor Bogdan Vladu, an iOS application developer and aspiring game developer living in Bucharest, Romania. Welcome back to our Jetpack Joyride tutorial series! In this tutorial series, we are making a game similar to Jetpack Joyride using Cocos2D and Box2D, and the LevelHelper and SpriteHelper tools. So far, we’ve […] By .

Leave a rating/review
Save for later
Share

This is a post by special contributor Bogdan Vladu, an iOS application developer and aspiring game developer living in Bucharest, Romania.

Learn how to make a game like Jetpack Joyride with latest SpriteHelper and LevelHelper!

Learn how to make a game like Jetpack Joyride with latest SpriteHelper and LevelHelper!

Welcome back to our Jetpack Joyride tutorial series! In this tutorial series, we are making a game similar to Jetpack Joyride using Cocos2D and Box2D, and the LevelHelper and SpriteHelper tools.

So far, we’ve got a working level with background art that scrolls continuously. Check out how we did it in Part One.

By now, you should be pretty comfortable working with LevelHelper and SpriteHelper, at least when it comes to the basics like adding objects to the levels.

In this second part of the tutorial, we’ll focus on adding some movement and activity to the game. Lasers, coins, a flying player-character, the beginnings of collisions… it’s all coming up!

To follow along with this tutorial, you’ll need to have the RocketMouse project where we left off in part one. If you don’t have this already, you can grab a copy of it from here.

So let’s get that mouse flying!

Getting Started

The next step in creating our Jetpack Joyride game is to add some elements that the player will directly interact with: lasers and coins. Adding these elements is also a good way to begin learning how to implement animations and sensors using LevelHelper.

Since we want the lasers to go on and off, they need to be animations. As for the coins, we want to know when the player collides with them, but we don’t want the player to bounce off them. So they need to be sensors.

Having grappled with the basics of animation and collision response, we’ll expand the concepts to other elements of our game, including the player. We’ll learn how to create tags to track collisions and then put it all together with some actual code!

Before you get started, you might want to open your current LevelHelper project and save the level as level03 (this way you’ll have the old level to refer to). If you do this, be sure to add the new Level to your Xcode project, and update the line that selects the level to choose the new level like so:

lh = [[LevelHelperLoader alloc] initWithContentOfFile:@"level03"];

Working With Animations: Adding Lasers

How you’ll use the animations in the game is fairly simple. When the player makes contact with a laser, you just test what frame the animation is on. If it’s the frame with the laser-off sprite, you leave the player alone. If it’s the frame with the laser-on sprite, then you kill the player.

To create an animation for your lasers, open SpriteHelper and start a new scene. Drag the laser animation frames on to the scene from the “animations/laser” directory from the assets for this tutorial.

Rename the sheet to “animations”. You will put all the animations in this sheet. You can also put each animation in its own sheet but loading a texture is expensive, so its better to have as few textures to load as possible.

Next, switch to the “Animation” tab and create a new animation by clicking on the “Create New Animation” button. Double click on the new animation and rename it to “laserAnim”. Select “Loop” and set a “Units of Time” to 1.2.

Now select the 2 laser sprites and tap the “Add frame” button.

With the laser_on frame selected, change the “Frame unit” to 0.4. What this will do is make the frame where the laser is on display for a shorter time period. With frame unit you control how long a frame will display in an animation. The time of a frame is calculated by multiplying “Units Of Time” with “Frame Unit”. In our case “laser_off” will show for 1 x 1.2 = 1.2 seconds and laser_on will show for 0.4 x 1.2 = 0.48 seconds.

Save the scene by going to File/Save As. Name it “Animations”.

Click the Save As button, navigate to your project’s Images folder, and save the new document.

Switch over to LevelHelper and you should see that the view has been updated to contain this new SpriteHelper document.

Note: If LevelHelper does not show the new document even after reloading, it might mean you saved the document to a location outside of the project folder. Check and if that’s the case, move it to the project folder.

Drag one of the animation frames on to the scene. With the laser sprite selected go to “Generic Animation Properties” and set the Animation to “laserAnim”. Now you have a sprite that will run the laser animation when the game runs. If, for example, you want to start the animation on this sprite manually via code, you can uncheck “Start At Launch”.

With the laser sprite selected, clone it as you learnt in part 1 to add more lasers to the scene till a satisfying level of danger is achieved. You can rotate and scale the lasers by using the handles that appear when you select a sprite.

By changing “Timer per frame” property inside LevelHelper (Generic Animation Properties section) you can make each laser animate differently.

My level now looks like this:

If you look at the laser sprite, it has no shape. So open the SpriteHelper document to create a shape for it.

The shape of an animations is the shape of the sprite that you dragged on to the level. The shape of an animation does not change. Changing a shape for every frame will make Box2D cancel collisions and will also result in poor performance.

Lets create the shape for the animation based on the “laser_on” sprite. Since you need a rectangle shape, you will not use the auto-trace feature. Instead, with laser_on selected, go to the “Physics” tab and tap “Create New Rectangle Shape”. Set the body type to Static and rename the fixture to “laserFixture”.

Note: You can have multiple shapes per body. During a collision event you can see which shape in the body has collided. This will make it easier to create certain game events like knowing when an enemy was shot in the head or torso. More about collisions soon.

Since the shape doesn’t need to cover the entire sprite, you will edit the shape and make it smaller. With the shape selected tap on the “Edit Shape” button. Now drag the corners of the quad shape to make it smaller. When you are satisfied press on the “Edit shape” button again to finish the editing.

Back in LevelHelper, you should see that all lasers have been updated with the correct shape. If you have the laser_off sprite displaying, you can always switch to the laser_on sprite by selecting the correct sprite frame under General Properties for the SH Sprite property.

Now select all the lasers and add them to the parallax node. Enter 1 for the x ratio.

Save the level. If you build and run your game it will now have animating lasers! (But don’t forget to add the new animation document that you added to the Images folder to your Xcode project before you build it …)

Level with animating lasers

Working With Sensors: Adding Coins

Now for the coins. Remember, we want to make the coins sensors so that the mouse can pass straight through them without bouncing off, but still allow us to detect when there is a collision.

To create the coins sensors, open the SpriteHelper document containing the “objects” sheet and select the coin sprite. Go to Physics tab and click on the “Create New Circle Shape”. Make the body type “Static” then rename the shape and check “Is Sensor”.

Save the scene when you are done.

  • Is Sensor: makes the body trigger a collision, but not a collision response.
  • Is Circle: makes the shape of the body circle-based.
  • Can Sleep: speeds up simulation of the physics.

Now switch to LevelHelper and add some coin sprites to the scene, placing them as you see fit. Use the Clone Tool to easily clone the coin with the direction offset you prefer.

Now my level looks like this:

Add the coins to the parallax, set the x ratio to 1 and y ratio to 0.

Save the level when ready. Compile and run your project, and now you have coins!

Adding coins to the level

The project with all of the work up to this point can be downloaded from here.

Adding the Player

Now that we have a good-looking level with all the basic components, let’s create the player and accompanying animations.

Open the “Animations” SpriteHelper document. Then from Finder drag all the mouse sprites and the rocket flames from the “animations/player” folder.

Now switch to the Animation tab. You’ll create all the animations you need there, exactly as you did for the laser.

Using the same technique you learned earlier, create five animations with the properties listed below.

Hint: If you forgot how to create animations, for each animation, click the “Create New Animation” button, and double click to rename it. Then select the required frames for that particular animation, set the “Units Of Time” and make sure to check the “Loop” option for those that need to loop.

1) Animation Name: mouseRun
Start At Launch: YES
Loop Forever: YES
Speed: 0.400 (default)
Repetition: 1.000 (default)
Frames: rocketmouse_1_run, rocketmouse_2_run, rocketmouse_3_run, rocketmouse_4_run

2) Animation Name: flame
Start At Launch: YES
Loop Forever: YES
Speed: 0.400 (default)
Repetition: 1.000 (default)
Frames: rocket_flame1, rocket_flame2

3) Animation Name: mouseFly
Start At Launch: YES
Loop Forever: YES
Speed: 0.400 (default)
Repetition: 1.000 (default)
Frames: rocketmouse_5_fly

4) Animation Name: mouseDie
Start At Launch: YES
Loop Forever: NO
Speed: 0.400 (default)
Repetition: 1.000 (default)
Frames: rocketmouse_7_die, rocketmouse_8_die

5) Animation Name: mouseFall
Start At Launch: YES
Loop Forever: NO
Speed: 0.400 (default)
Repetition: 1.000 (default)
Frames: rocketmouse_6_fall

Once you’re done, hit Command-S to save the scene.

Switch back to LevelHelper, you should find all of your new animations in the Animation section.

Drag the “rocketmouse_1_run” sprite onto the main screen (the one with a red border). Place it at the left edge of the screen. Then drag the “rocket_flame1” sprite and place it right under the red tank on the back of the mouse.

If you look closely, you’ll see that the flame sprite is on top of the red tank. Let’s put it behind the tank, so that it looks more like the flame is coming out of the tank. Select the flame sprite and under General Properties, set Z Order to -1.

The result should look something like this:

With the mouse sprite selected, sets its animation to “mouseRun”.

With the flame sprite selected set the animation to “flame”

If you run the level at this point using Scene Tester you will see the mouse running and the flame animating. But we have no collisions yet.

Time to make a shape for the mouse and the flame sprites! Open Animations.pshs in SpriteHelper.

With the “rocketmouse_1_run” sprite selected, go to the Physics tab and do the following:

  1. Click on “Create New Circle Shape”.
  2. Then select “Dynamic” as the body type.
  3. Check “Fix Rotation”.
  4. Double click on the fixture name and set it to “mouseFixture”.
  5. Click on edit shape, and drag around the circle shape until you are satisfied with how its placed over the mouse sprite. You can also change its size using the Radius property.

Note: You use a circle shape because this is the fastest shape to simulate. You could always use multiple circle shapes to create a complex shape and this will be faster then having a complex concave shape.

“Fixed Rotation” tells Box2d to not perform any rotation simulations on this shape. Because the shape is a circle you don’t want the mouse to rotate.

With the “rocket_flame1” sprite selected go to Physics tab and do the following:

  1. Click on “Create New Circle Shape”.
  2. Then select “Dynamic” as the body type.
  3. Double click on the fixture name and set it to “flameFixture”.
  4. Make sure that “Is Sensor” is checked for the fixture.
  5. After that, click on edit shape, and drag around the circle shape until you are satisfied with how its placed over the flame sprite. You can also change its size using the Radius property.

Save the scene when you’re done!

Back in LevelHelper, when you run Scene Tester again, you will see the mouse falling out of the screen.

Note: If the mouse does not fall out of the scene when you run Scene Tester, one of the things to check is that the SH Sprite is set to rocketmouse_1_run for the mouse on LevelHelper. Since the physics shape is set to the rocketmouse_1_run sprite, you need that to be the initially displayed sprite. If the right sprite is selected, you should see the shape circle in LevelHelper whith “Show Polygon Shapes” on.

The mouse falls out of the screen because it is responding to gravity. Let’s make him stay on the screen. Click the Physic Boundaries button. From the Physic Boundaries section, click Create.

You’ve now created the boundary, but you must edit it so that it’s right under the mouse in the middle of the floor, where the mouse will walk. Inside the Physic Boundaries window, click Edit.

You will now see four red handles on the corners of the physic boundary. Drag any of the bottom handles to move the boundary so that it’s under the mouse’s feet.

You should have something similar to this:

When you’re satisfied with your physic boundary, press the Editing button to stop the editing process.

Run the level in Scene Tester again and you will see the mouse now doesn’t fall out of the scene and that it collides with the dogs and cats. But alas, there is yet another problem!

This time, the flame falls off the screen. That’s because it’s a sensor and so it does not collide with any object and isn’t attached to the mouse’s body. But we can connect the flame to the mouse by creating a weld joint.

Go to the Joints section in LevelHelper, select Weld Joint from the list and click the + button.

Now let’s select the sprites we want the joint to connect.

Select the joint from the list (if not already selected), then on the Body A property, click and hold the circle icon and drag over to the mouse sprite. When the text says “rocketmouse_run_1”, release. (This is exactly the same process as connecting an outlet inside Interface Builder in Xcode.)

Now repeat the process for Body B but select the flame as the body.

Now let’s put the joint anchor point where the bodies if the mouse and flame overlap.

Select the joint in the view and drag it into position as shown below:

If you now run the level in Scene Tester, you’ll see that the flame stays attached to the mouse. w00t!

Since you added a physic boundary, you need to tell the code to load that boundary in your game. Add the following line to init in HelloWorldLayer.mm right before the [self scheduleUpdate]; line:

    [loader createPhysicBoundaries:world];

You now have almost everything you need in order to start coding the game!

Creating Tags to Perform Collisions

In order to identify collisions between your sprites, you need a way to register collisions between sprites of one type with sprites of another type.

You will achieve this by separating your sprites into types using tags – all dog sprites will have the tag “DOG” and all cat sprites will have the tag “CAT” and so on.

To create the tags, in LevelHelper select the “Custom Properties” tab.

In the Tag Management section, enter the name for the new tag and click the Add button to create it. Create the following tags: DOG, CAT, LASER, COIN, PLAYER.

Now that you have the tags defined, you need to assign the tags to sprites.

Switch back to the Level Editor tab and select all the dog sprites from the list of sprites on the left. Then under General Properties, assign the DOG tag to all of those sprites.

Repeat the process for the other sprites. Assign PLAYER to the mouse sprite, CAT to all the cat sprites, LASER to all the laser sprites, and COIN to all the coin sprites.

When you’re done, save the level with Command-S.

Since you added tags, you need to regenerate the supporting code in order for the tags to exist in the code. So go to File/Generate Code/Cocos2d With Box2d and re-generate the code, replacing the old version.

If you look in LevelHelperLoader.h now, you will see that the tags are there.

Note: Every time you change, delete, or add a custom property (tags or classes) you need to regenerate the code.

A project with the code up to this point can be downloaded from here.

Coding the Game Logic

You are now (finally) ready to start coding your game. So switch back to Xcode, open your project and get ready for some real fun :]

In order to control the movement of the parallax and allow the player to jump, you need to have a few variables that will point to the objects created by LevelHelper. Add these lines to HelloWorldLayer.h inside the class definition:

LHParallaxNode* paralaxNode;
LHSprite*   player;
LHSprite*   rocketFlame;

Then add the following method prototype right before the @end:

-(void) retrieveRequiredObjects;

Inside HelloWorldLayer.mm add the new method implementation:

-(void) retrieveRequiredObjects {
    //Retrieve pointers to parallax node and player sprite.
    paralaxNode = [loader parallaxNodeWithUniqueName:@"Parallax_1"];
    NSAssert(paralaxNode!=nil, @"Couldn't find the parallax!");
    
    player = [loader spriteWithUniqueName:@"player"];
    NSAssert(player!=nil, @"Couldn't find the player!");
        
    rocketFlame = [loader spriteWithUniqueName:@"flame"];
    NSAssert(rocketFlame!=nil, @"Couldn't find flame sprite!");
    
    [rocketFlame setVisible:NO];
}

The first line retrieves a pointer to the parallax node using the unique name assigned in LevelHelper.

Then a pointer to the player sprite is retrieved, also by giving the unique name. You might need to modify the unique name in LevelHelper to read “player” as shown in the screenshot below if it isn’t set to that already.

Similarly, you should change the unique name of the flame to “flame” so it matches the code.

When you use spriteWithUniqueName: to look up a sprite, you get a LHSprite* instance, which is a class derived from CCSprite.

Finally, the pointer to the flame sprite is used to make it invisible. You only need to see the flame when the player is flying. (You could also have done this inside LevelHelper by unchecking the Visible option in the General Properties section but then you wouldn’t be able to see the flame in the Level Editor and so it would be harder to set up the scene properly.)

Now that you have the method defined, let’s use it (after you’ve loaded the level) via init – add the following right before the [self scheduleUpdate]; line:

    [self retrieveRequiredObjects]; // Retrieve all objects after we’ve loaded the level.

Compile and run, and if the flame is invisible when the game starts, you know it’s working so far! :]

The flame is now invisible.

Making the Player Fly

Time to let the mouse fly!

Add the following instance variables to HelloWorldLayer.h:

float  playerVelocity;
bool   playerWasFlying;
bool   playerShouldFly;

Now inside HelloWorldLayer.mm, replace the existing touch functions with the following:

-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    playerVelocity = 0.5f;
    playerShouldFly = true;
    [rocketFlame setVisible:YES];
    [player prepareAnimationNamed:@"mouseFly" fromSHScene:@"Animations"];
    [player playAnimation];
}

////////////////////////////////////////////////////////////////////////////////
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    
}
////////////////////////////////////////////////////////////////////////////////

-(void)cancelPlayerFly {
    playerShouldFly = false;
    [rocketFlame setVisible:NO];
    playerWasFlying = true;
    playerVelocity = 0.0f;
}

-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self cancelPlayerFly];
}

-(void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [self cancelPlayerFly];
}

In ccTouchesBegan:, when the user touches the screen, playerShouldFly variable is set to YES, the rocket flame becomes visible and the flying animation starts on the player sprite. The code also starts the animation named “mouseFly” on the player instance.

To start any animation on a sprite, you just prepare the animation on that sprite similar to this:

[player prepareAnimationNamed:@"mouseFly" fromSHScene:@"Animations"];

And then play that animation as follows:

[player playAnimation];

To prepare an animation you just need the animation name and the SpriteHelper document containing the animation.

When the player stops touching the screen, or the touch is canceled, you call cancelPlayerFly to stop the mouse flying. You also hide the flame because the player is no longer flying.

But this isn’t enough. You set up the flag to indicate that the the player is flying, but you haven’t implemented the actual flight. That requires adding the following code to the end of update::

    if (playerShouldFly) {
        [player body]->ApplyLinearImpulse(b2Vec2(0, playerVelocity),
                                          [player body]->GetWorldCenter());
        playerVelocity += 0.01f;
        if(playerVelocity > 1.5f)
            playerVelocity = 1.5f;
    }

Here, you check if the player should fly and if true you apply a linear impulse with a horizontal direction (on Y) on the mouse Box2D body. The mouse Box2D body is retrieved using the “body” method of the LHSprite class.

You then make the velocity bigger and bigger, so the player will look like they are taking off from the ground, gaining speed with time. If the player’s velocity reaches a certain speed (1.5), you stop the speed from increasing further.

Compile and run. The mouse can now fly when you touch the screen!

A flying rocket mouse!

Where to Go From Here?

The full project up to this point is here.

Thanks for staying with me this far! We’ve made a huge amount of progress for one tutorial. And we’re only at the end of Part Two!

You won’t want to miss Part Three, where we’ll fully implement collisions, animate the player’s flight and landings, and add sounds. We’ll also make the player die when it touches the dogs, cats or lasers and add a restart screen.

Then in the fourth and final part, we’ll add bunnies that will run through the level to give the player more scoring opportunities and add a score display.

Clearly our fun is just getting started. Keep your questions, comments and suggestions coming!


This is a post by special contributor Bogdan Vladu, an iOS application developer and aspiring game developer living in Bucharest, Romania.