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

Bogdan Vladu

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

Create a game like Jetpack Joyride with LevelHelper and SpriteHelper!

Create a game like Jetpack Joyride with latest LevelHelper and SpriteHelper!

Hello again, and welcome to the fourth and final instalment of the tutorial series on LevelHelper and SpriteHelper. I said that by the end of this series you would have an exciting game to play, and you’re almost there!

Last time, in Part Three, you came close to completing your game. You fully implemented collisions between the player and the other game elements, enabling the player to die and score points. You also made the player fly and added some nice sounds and animations.

But there are still a few improvements you can make. For one thing, you need a way to keep track of the score, and while you’re at it, you might as well add some cute moving bunnies that will be worth bonus points.

And wouldn’t it be interesting if you made some of your lasers rotate? Keep reading to do all that and complete the game!

Getting Started: Rotating Lasers

You pick up the project where you left off at the end of Part Three. You can download that version of the project here.

You can’t have your game be too easy, can you? So, the first thing you’ll do is make your lasers rotate.

Open your project in LevelHelper, and make sure you have the latest version of your level open. Then go to Define Tags and define a new tag named ROTATING_LASERS.

Don’t forget to regenerate the supporting code since you added a new tag.

You will use this new tag to reference the lasers you select to rotate (not all of them… you’re not that evil, are you?) when you implement the rotation via code.

Now select a couple of the lasers in the level – make sure the lasers you select have room to rotate. Assign the ROTATING_LASERS tag to the selected lasers.

Once you’re done tagging the lasers, save the level and open your Xcode project. Navigate to HelloWorldLayer.h and add the following instance variable:

NSArray* rotatingLasers;

Then add the following code at the end of retrieveRequiredObjects in HelloWorldLayer.mm:

rotatingLasers = [loader spritesWithTag:ROTATING_LASERS];
[rotatingLasers retain];

Here you get references to all the sprites that have the ROTATING_LASERS tag. Because the array returned by this method is autoreleased, you retain it. So that you won’t forget to release this retained array later, go to dealloc and add the following at the top of the method:

[rotatingLasers release];

Now that you’ve grabbed the lasers that you want, time to make them rotate! Add the following to the end of update::

for(LHSprite* laser in rotatingLasers) {
    [laser transformRotation:[laser rotation]+1];
}

If you run the game now, the chosen lasers will rotate, but the player won’t die when he makes contact with them. This is because the rotating lasers are no longer tagged as LASER. You need to register a collision event for the ROTATING_LASERS tag. To do that, add the following to the end of setupCollisionHandling:

    [loader registerPreCollisionCallbackBetweenTagA:PLAYER
                                            andTagB:ROTATING_LASERS
                                         idListener:self
                                        selListener:@selector(mouseLaserCollision:)];

Running the game now, the lasers will rotate and kill the player just as the non-rotating lasers do.

Congrats, you have just added another layer of difficulty to your game! That said, you may want to play-test the game some more at this point to make sure the player can still get through the level!

Moving Bunnies

Running and flying over sleeping cats and dogs and dodging rotating lasers is all fun, but let’s add another reward component to the game to give it more complexity.

I have just the ticket: bunnies. Cute, helpless bunnies that our player can kill for points.

Open the Animations.pshs SpriteHelper document. Then open Finder and navigate to the ArtPack folder (download it from Part One if you don’t have it), select the bunnies images and drag them on to the SpriteHelper window.

Now do the following:

  1. Select the “bunny_1″ sprite in the list and switch over to the Physics tab.
  2. Create 2 fixtures on the bunny using “Create New Circle Shape”. Rename the two fixtures as “bunnyFixBody” and “bunnyFixHead”. Use the “Edit shape” button and “Radius” property” to resize the two shapes and place them correctly on the sprite.
  3. Check “Is Sensor” for both the shapes.
  4. Set the body type to “Static”.

Note: While I also have “Fix Rotation” checked (in the image below), this is not mandatory since the body is static. This property only has effect when the body is dynamic.

Next, create the following two animations using the bunny frames:

  • Animation Name: BunnyRun
  • Speed: 0.400 (default)
  • StartAtLaunch: YES (default)
  • LoopForever: YES
  • Frames: bunny_1, bunny2

  • Animation Name: BunnyDie
  • Speed: 0.400 (default)
  • StartAtLaunch: YES (default)
  • LoopForever: NO (default)
  • Frames: bunny_die_1, bunny_die_2

When you’re done creating the animations, save the scene.

Going back to LevelHelper, you should see the bunny animations inside the Animations section. (If you don’t, use the reload button or failing that, close and relaunch LevelHelper.)

Select the “bunny_1″ sprite and drag it on to your level. Then assign the “BunnyRun” animation to the new sprite.

You now have a bunny in the level, but it will not be visible because you need to move it with the parallax.

To add the bunny to the parallax, go to the Parallax tab, select your parallax node and add the bunny sprite to it. Set the x component of the movement ratio to 1 so that the bunny moves at the same speed as your level.

Running the level in Scene Tester, you should see the bunny moving with the parallax, but the bunny appears to be moving in place. You want the bunny to move from right to left and from left to right. To do this, you have to make it move on a path.

Open the Bezier tool and click “Start Bezier Creation” to create a new bezier path that will describe the path taken by the bunny. Click in the level twice to create a path from the first click point to the second click point.

When you’re done, click on the “Finish Bezier” button.

With the bezier selected (use the Level Navigator if the path is not selected), navigate to “Bezier Properties” and select “Is Line” and “Is Path”.

You now have a path, but the bunny sprite doesn’t know that it should move on that path. Let’s make the bunny aware of this.

Select the bunny, then in the “Path Movement” section, select the bezier name from the “Path Name:” property.

In the Path Movement section, make the motion cyclic by selecting “Cyclic Motion.”

Set the amount of time you want the bunny to take to move from one end of the bezier to the other (or, leave it at the default value). Then select “Flip Sprite X At Ends” so that the bunny is always facing in the direction it is moving. Otherwise, it will appear to be moving backwards half the time.

If you now test the level in Scene Tester, the bunny should move along the path and also move with the parallax. This is cool, eh? LevelHelper is the only editor that lets you move sprites on a path and with a continuos scrolling parallax at the same time.

The last step in LevelHelper is to define a new tag called “BUNNY” and assign it to the bunny sprite. Once you’ve done this, save the level. (Don’t forget to regenerate the code after you have added the BUNNY tag.)

Time to move back to your Xcode project and write some code that will make the bunny die when the player collides with it.

Add the following to the end of setupCollisionHandling:

    [loader registerBeginOrEndCollisionCallbackBetweenTagA:PLAYER
                                                   andTagB:BUNNY
                                                idListener:self
                                               selListener:@selector(mouseBunnyCollision:)];

This registers the collision callback, but you also need to define the method for the callback. Add it as follows:

-(void)mouseBunnyCollision:(LHContactInfo*)contact {
    if(playerIsDead)
        return;
    LHSprite* bunny = [contact spriteB];
 
    if (nil != bunny) {
        if ([[bunny animationName] isEqualToString:@"BunnyRun"]) {
            [self scoreHitAtPosition:[bunny position] withPoints:500];
            [[SimpleAudioEngine sharedEngine] playEffect:@"bunnyHit.wav"];
        }
        [bunny prepareAnimationNamed:@"BunnyDie" fromSHScene:@"Animations"];
        [bunny playAnimation];
        [bunny pausePathMovement];
    }
}

The above code tests for a dead player and does nothing if that’s the case. It then takes the bunny sprite from the contact and tests if it’s valid and whether it’s running the “bunnyRun” animation.

If the bunny sprite meets both conditions, the player is given 500 points and the bunny hit sound is played (part of the sound pack downloaded in Part Three of this series). You then start the bunnyDie animation on the bunny sprite and stop the sprite’s movement along the path.

If you run the game now, you’ll see that the bunny dies when he collides with the mouse. But if you loop through the entire level, you’ll see that the bunny you killed doesn’t reappear in the level – it stays dead! So let’s do the same thing you did with the coins in Part Three and reset the bunny.

Find spriteInParallaxHasReset: in HelloWorldLayer.mm and modify it to look like this:

-(void)spriteInParallaxHasReset:(LHSprite*)sprite {
    if(COIN == [sprite tag]){
        [sprite setVisible:YES];
    }
    else if(BUNNY == [sprite tag]){
        [sprite prepareAnimationNamed:@"BunnyRun" fromSHScene:@"Animations"];
        [sprite playAnimation];
        [sprite startPathMovement];
    }
}

Above, you simply check for the BUNNY tag, and when you find a sprite that is a bunny, you set its animation to bunnyRun and start its path movement.

Because sometimes the player (mouse) Box2D body goes to sleep – the collision between the player (mouse) and the bunny may not get triggered. To fix this, open the Animations.pshs SpriteHelper document and uncheck “Can Sleep” on “rocketmouse_1_run” and on “bunny_1″

The complete project up to this point is available here.

Displaying the Score

Your game’s looking pretty good now! All that’s left is to display the score.

For this game, you’re going to implement a dynamic way of showing the score. Instead of having a fixed display at the top or bottom of the screen, you’re going to show the score in a temporary pop-up window each time the player scores.

Begin by adding the following instance variable to HelloWorldLayer.h:

CCLabelTTF *scoreText;

Now add this new method to HelloWorldLayer.mm:

-(void)setupScore {
    score = 0;
    CGSize winSize = [[CCDirector sharedDirector] winSize];
    scoreText = [CCLabelTTF labelWithString:@"Score: 0"
                                   fontName:@"Arial"
                                   fontSize:22
                                 dimensions:CGSizeMake(200, 50)
                                 hAlignment:kCCTextAlignmentLeft
                                 vAlignment:kCCVerticalTextAlignmentCenter];
 
    scoreText.color = ccWHITE;
    scoreText.position = ccp(100, winSize.height-25);
    [self addChild:scoreText z:20];
}

The above method creates a new label and assigns it to the scoreText variable. It then adds this label to the game layer and sets the label position to be the top left corner.

Now call this new method from init right after the call to setupAudio:

[self setupScore];

This is good, but right now you’re not showing the actual score to the user. You’re just showing some text. Let’s make the text change when the user scores.

Replace the current implementation for scoreHitAtPosition:withPoints: with this:

-(void)scoreHitAtPosition:(CGPoint)position withPoints:(int)points {
    score += points;
    [scoreText setString:[NSString stringWithFormat:@"Score: %d", score]];
 
    NSString* curScoreTxt = [NSString stringWithFormat:@"+ %d", points];
    CCLabelTTF *curScore = [CCLabelTTF labelWithString:curScoreTxt fontName:@"Marker Felt" fontSize:24];
    curScore.color = ccWHITE;
    curScore.position = position;
    [self addChild:curScore z:20];
 
    id opacityAct1 = [CCActionTween actionWithDuration:1 key:@"opacity" from:255 to:0];
    id actionCallFunc = [CCCallFuncN actionWithTarget:self selector:@selector(removeScoreText:)];
    id seq = [CCSequence actionOne:opacityAct1 two:actionCallFunc];
    [curScore runAction:seq];
}

This adds the new points to the score variable. Then it sets the text of your score label to be the newly updated score.

You then create a new label that will be displayed at the position you received via the position parameter. As shown in the code, this is the position of the coin or bunny triggering the score update. The coin or bunny vanishes upon collision with the player, and a score display temporarily appears in its place.

You add the new label to the layer and then create two actions. The first action makes the new score label disappear after one second by changing the opacity property of the sprite from 255 (full visibility) to 0 (not visible). The second action calls a new method, removeScoreText:.

Finally, you create a sequence using the two defined actions and run that sequence on the new label sprite.

You still need to add the removeScoreText: method called by the second action. Add it as follows:

-(void)removeScoreText:(CCLabelTTF*)scoreLabel {
    [scoreLabel removeFromParentAndCleanup:YES];
}

This new method just removes the score display from the layer.

Run the game now and see how nicely this works. Every time the player makes contact with a coin or a bunny, a label pops up showing the updated score, displays for one second, then disappears.

Score displayed in Rocket Mouse

There’s nothing left to do but to run the game and enjoy!

Where to Go From Here?

Congratulations, you’ve finished the game! It’s been a long journey. I hope you enjoyed creating this wonderful game with me, and hardly noticed you were learning in the process. Thanks for reading!

A project with all of the code as at the end of the tutorial series can be found here.

As always, I am available to answer questions about this tutorial series in the forums, both here and on the LevelHelper forum.


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

User Comments

24 Comments

[ 1 , 2 ]
  • Once again, amazing feedback Bogdan!

    Using CCMoveTo was my initial thought, however I couldn't get it to work... now I know why, I wasn't making the player static! I'll try this tomorrow, I think it will definitely give me the effect I'm after.
    tongesy
  • hi
    i am new on the level helper i developed a simple code in which on mouse is running with background moving which is simple till now my view of game is at the time now is 1600*320 .But now i want to make it 1600*640 size so i can make the sky in my game also so when i did this screen is showing just the sky and all the thing is down .so now my question is that how can i move the screen point at run time in the game and when the mouse fly i will show the sky too here is the screen shot of both level helper screen


    http://postimage.org/image/eej0ephrl/

    http://postimage.org/image/qhoc29stt/


    thanks in advance
    faizanaliemblem
  • I replied on the other thread you made with this

    http://www.levelhelper.org/phpBB3/viewt ... 248#p15248
    vladubogdan
  • Hi everyone when i tried to compile this and run it on my iPad the screen just turns purple, and when trying on my iPhone I can only make it run in portrait mode even though on both the iPhone and iPad simulator in xcode both run fine and in landscape >.< anyone got any ideas?
    fuuuuuu
  • looks like something is not copied properly to the device. Try to look at the console log to figure out the cause.
    vladubogdan
  • How is data stored in a game built with levelhelper? There are many objects and not know how to catch them.
    zeroexu
  • What do you mean catch them?

    You can do

    LHSprite* spr = [loader spriteWithName:@"NameOfSprite"]

    or

    NSArray* sprites = [loader spritesWithTag:TAG];
    vladubogdan
  • These sounds are great! What is the licensing for using them in my own projects?
    CKal
  • Hey guys,

    We developed an open source, multiplayer game like fun run in Cocos2d-x which comes together with a detailed tutorial. Here's a link to it:
    www.nextpeer.com/ufo-run-tutorial

    Hope you like it.
    Dror
    dror
[ 1 , 2 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Gaming!

    Loading ... Loading ...

Last week's winner: Apple TestFlight Tutorial.

Suggest a Tutorial - Past Results

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in January: WatchKit.

Sign Up - January

Our Books

Our Team

Tutorial Team

  • Sam Davies

... 60 total!

Update Team

  • Ray Fix

... 12 total!

Editorial Team

  • John Clem

... 17 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

  • Richard Casey

... 4 total!