Flash Tutorial Links:
Play Games: HTML5 Tutorials:

How To Make A Platform Game (Part 1)

Jump or die!

Platform Games

Jumping games are subsets of popular platform games like Mario. The action usually take place in a more subdued form, and the main priority is for the player to jump to safety, or to get more points. A more traditional platform game like Mario (which we'll learn to do in Tutorial 8) is one which involves more complex game level design, and there're usually monsters or some fun platform mechanics (moving platforms, platforms that vanish after you land on them) to interest the player.

One of the popular jumping games I've seen recently is Icy Tower. It's basically quite a simple game where you just keep jumping and jumping. Another popular one (in a small survey done some years back, this was actually the most popular casual game) was Winter Bells, where you use your mouse to keep landing on platforms which propels you further up.

Here, we'll learn how to create the proper mechanics for it, and it will lead up to our next tutorial.

Game Scenario

Tooney the little green monster has a penchant for shiny coins, and he is on his way to jump all the way up into the sky where he heard there're lots of them. Over at the place they call the Waterfall, falling platforms or bricks constantly fall from the sky. If Tooney were to get up, this would be the place.

Help Tooney scale the falling bricks, and go as high as you can!

Game Details

  1. The player controls the movement of Tooney with the LEFT, RIGHT keys.
  2. Tooney can be made to jump with the SPACEBAR key.
  3. Each brick that falls off the screen earns Tooney 10 points.
  4. Do not let Tooney fall off the platforms.

Download the Game Files

The files you need to create this game can be downloaded from here.

Step 1 - Managing your FLA file

In the library, you will see that there are four movieclips. A more unusual one that you'll spot is this HitAreaBox movieclip. It is actually only a box that we'll use to denote a specific area that we'll be checking for hitTests.

library

To see how it is used, examine the Player movieclip.

player

A layer called HitArea has been added, and it stores only the additional hit area which we'll use later on to detect if Tooney has touched the floor. Hence, you can see that it is placed at the bottommost portion of the movieclip.

This hit area is called "hitAreaFloor", and it is an instance of the HitAreaBox movieclip which we saw earlier on. The reason why we don't see it appearing here is because we have set the alpha value of it to 0.

alphaProperty

We'll see how this is made use of in the code later on.

Step 2 - Starting up the game in your GameController.as

Let us see what goes on in the startGame function.

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public function startGame()
{
    playerScore = C.PLAYER_START_SCORE;
			
    bricks = new Array();
    
    scrollSoFar = 0;
    moveX = 0;
    jump = false;
                
    //Add first 3 bricks for player to step on
    var newBrick = new Brick(C.BRICK_1_X, C.BRICK_1_Y);
    bricks.push(newBrick);
    mcGameStage.addChild(newBrick);
    
    newBrick = new Brick(C.BRICK_2_X, C.BRICK_2_Y);
    bricks.push(newBrick);
    mcGameStage.addChild(newBrick);
    
    newBrick = new Brick(C.BRICK_3_X, C.BRICK_3_Y);
    bricks.push(newBrick);
    mcGameStage.addChild(newBrick);
    
    //Add player on to stage
    player = new Player(C.PLAYER_START_X, C.PLAYER_START_Y);
    mcGameStage.addChild(player);
    
    mcGameStage.addEventListener(Event.ENTER_FRAME,update);
    
    //Handle event when this game is being preloaded
    addEventListener(Event.ADDED_TO_STAGE, gameAddedToStage ); 
    
    //Handle situations when this game is being run directly
    if (stage != null)
    {
        stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDownHandler);
        stage.addEventListener(KeyboardEvent.KEY_UP,keyUpHandler);
    }
}

A lot of things, ya ... ok, take a deep breath!

scrollSoFar in line 54 keeps track of the amount of pixels in the y axis that had already been scrolled. This variable will be used when we need to generate new bricks later on.

moveX indicates the direction in the x axis which the player is intending to move, with 1 being the positive x direction, and -1 indicating the negative x direction. jump is set to true if the player hits the SPACEBAR.

In lines 58 to 69, we generate 3 starting bricks for the player to play with right at the start. In lines 72 and 73, we then create and add the player, spawning him above the second brick that we created.

Step 3 - Key Handlers

The keyboard input to the game will be the LEFT, RIGHT and SPACEBAR keys. Feel free to add in the support for the A and D keys as well if you want.

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
private function keyDownHandler(evt:KeyboardEvent):void
{
    if (evt.keyCode == 37) //LEFT Key 
    {
        moveX = -1;
    }
    else if (evt.keyCode == 39) //RIGHT Key
    {
        moveX = 1;
    }
    
    if (evt.keyCode == 32) //Spacebar
    {
        jump = true;
    }
}

jump is just a boolean variable. We set it to be true when the player hits the spacebar.

Step 4 - Game Loop - User Input

Now, let us consider the user input portion in the game loop. You can see that we actually rely on the Player class to handle instead of doing it in the main GameController.as. This is probably the first tutorial where you will see a lot more code in the external class files for the movieclips. We'll take a look at the Player class shortly, but suffice to know that depending on what was set in moveX, we issue the corresponding command to the player object.

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public function update(evt:Event)
{
    //******************			
    //Handle User Input
    //******************
    if (moveX > 0)
        player.moveRight();
    else if (moveX < 0)
        player.moveLeft();
    else if (moveX == 0)
        player.stopMoving();
    
    if (jump && !player.isInAir())
    {
        player.jump();
        jump = false;
    }

If you look at line 136, you will see that there is an additional check there to see if the player is in the air. The reason we do this is to prevent the player from double-jumping. In other words, the player can only jump if he is standing still on a platform (and hence player.isInAir() will return false).

Now, let's see what goes on in the Player class. There are four functions that are used to set the motion of the player, moveRight, moveLeft, jump and stopMoving.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
public function moveRight()
{
    if (this.x <= C.PLAYER_RIGHT_BOUND)
        this.speedX = C.PLAYER_SPEED_X;
    else
        stopMoving();
}
 
public function moveLeft()
{
    if (this.x >= C.PLAYER_LEFT_BOUND)
        this.speedX = -C.PLAYER_SPEED_X;
    else
        stopMoving();
}
 
public function jump()
{
    this.speedY = C.PLAYER_SPEED_Y;
    this.inAir = true;
}
 
public function stopMoving()
{
    this.speedX = 0;
}

Looking at moveRight, we do the usual check for out of bounds in line 81. This ensures that if the player is already at the edge and will essentially move out of screen, we stop it from moving. But notice that we do not set the .x property of the player like we used to. Instead, we set this speedX property.

Likewise, if you look at the jump function, we do not set the .y position of the player each time he hits the spacebar (and that triggers the jump function). We use the speedY variable to keep track of the intended speed in which the player will move in the y direction.

Since this is going to be a jumping game, one mechanic you have to keep in mind is the concept of speed and gravity. No longer can you just move the player immediately whenever the player pushes a button. Rather, you just need to give it a speed and let the game logic update where it should be in the next moment.

If you've paid attention to your Physics lessons, you know that gravity acts upon the player and pulls it downwards. The variable speedY ( I know that technically we should call it velocityY ... but hey, this is not really a physics lesson! =) ) you see in line 97 is the velocity in the y-axis, which gravity will act on. In each tick of the game loop, gravity will slowly modify this speedY. We'll discuss this extensively in the game logic portion of the game loop.

Step 5 - Game Loop - Game Logic

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//******************
//Handle Game Logic
//******************
//Check to spawn bricks
scrollSoFar += C.BRICK_SPEED;
if (scrollSoFar % C.SPAWN_BRICK_FACTOR == 0)
{
    var randomXPos = Math.floor(Math.random() * C.GAME_WIDTH/2) + 
                        C.GAME_WIDTH/4;
    
    var newBrick = new Brick(randomXPos, C.BRICK_SPAWN_Y);
    bricks.push(newBrick);
    mcGameStage.addChildAt(newBrick,0);
}
 
//Update Bricks
for (var i=bricks.length - 1; i >= 0; i--)
{
    bricks[i].update();
    
    if (bricks[i].notInScreen())
    {
        mcGameStage.removeChild(bricks[i]);
        bricks.splice(i,1);
        
        //Add score
        playerScore += C.SCORE_PER_BRICK;
    }
}

The algorithm to spawn a brick is a little different than usual. We could do what we usually do, that is to rely on a random chance to spawn the brick at a random position. But what you will end up with is usually ai messy clutter of bricks or platforms. Eventually, there may be a scenario whereby the player is unable to win at all. (meaning a platform appear so far up that what Tooney can reach).

Instead, we use the variable scrollSoFar to keep track of how much game time has passed, in a way. Each tick of the loop, we add the brick's speed to it. Therefore, it knows much the game has progressed, and how much lower Tooney is now, because the brick he stood on went down by the speed of the brick.

scrollSoFar % C.SPAWN_BRICK_FACTOR == 0 indicates that when scrollSoFar reached a certain amount, it will return true. The % operator gives the remainder when scrollSoFar is divided by C.SPAWN_BRICK_FACTOR. So, we can see that per tick, scrollSoFar will go from 0 ... 2 ... 4 ... 6 ...68 ... 70 ... 72, and so on, since the speed of the brick is 2. SPAWN_BRICK_FACTOR was set as 70, so we will spawn the bricks when scrollSoFar is 0, 70, 140, and so on.

These constants can be changed to vary the gameplay as required.

Lines 157 to 170 should be straightforward, as we are merely updating the y position of the bricks to get them to move downwards. When they vanish off screen, we give the player his due points for surviving.

Now, let's see what goes on next in the game loop.

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//Update Player
player.update();
    
//Check for collisions
if (player.isInAir() && player.isFalling())
{	
    for (var i=bricks.length - 1; i >= 0; i--)
    {
        if (player.hitAreaFloor.hitTestObject(bricks[i]))
            {
                //Player landed on the brick
                player.hitFloor(bricks[i]);
            }
    }
}

We call the usual update function in the Player class to handle all its motion. We'll take a look at it again later, but this is where we finally update the player's real x and y positions based on its relevant speeds.

The next part where we check for collisions, is done only if the player is in the air and is falling. You may be puzzled why we need to check for collisions only when the player is falling. That is because we do not want the player to land immediately when he is still moving skywards, where isInAir is true. Only when the player has reached his maximum high point, and is falling down do we want him to land.

Line 180 is where the whole point of using the additional movieclip HitAreaBox comes in. Remember we mentioned earlier on that we placed this additional movieclip into the Player movieclip.

player

You can see that in line 180, we do not just use the player to check for the hitTest. Rather, we only check for the hitTest with this small box called hitAreaFloor. This will make the game play out more naturally. So, depending on how your character is designed, whether it has a flat base or not, you may need to change the size of hitAreaFloor accordingly.

If the hit is detected, we call the hitFloor method in the Player class. This will bring the player to the correct y position, as well as remember the fact that the player is now standing on this particular brick it landed on.

Now is perhaps a good time to examine the Player class in detail. We'll look at the hitFloor method first.

122
123
124
125
126
127
public function hitFloor(brick:Brick)
{
    this.inAir = false;
    this.standingOn = brick;
    this.y = brick.y;
}

This method is invoked whenever a collision is detected between the player and a brick. When that happens, we set the inAir property to be false, because when the player landed, he should not be in air anymore. The standingOn property is a reference to the brick that the player is standing on. This is important because when this particular brick moves, we need to update the position of the player as well.

Finally, we set the y position of the player to be the y position of the brick. The brick is positioned such that the crosshairs (denoting the origin) is at the top of the brick. So when we align the player's y position with the brick's, the player will be seen standing on the brick.

brickY

Now, let's see what happens to the player every tick of the game loop. Remember that the player is also updated as well. Let's examine its update function.

Step 6 - Player's update Function

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public function update()
{
    //Move Horizontally
    this.x += this.speedX;
    
    //Move Vertically
    if (this.speedY < C.MAX_SPEED)
        this.speedY += C.GRAVITY;
    
    if (this.inAir)
        this.y += this.speedY;
    else
    {
        //Move the player as its floor moves
        if (this.standingOn != null)
            this.y = standingOn.y;
    }
    
    if (standingOn != null)
    {
        //Check if player has moved off the floor it stands on
        if (!this.hitAreaFloor.hitTestObject(standingOn))
        {
            removeFloor();
        }
    }

We update the movement of the player in the x direction first. Whether the player is moved by the player pressing the LEFT and RIGHT keys or whatever reasons, the speedX would have been updated accordingly beforehand. During the update function, we just move the player according to what the speedX is.

In lines 35 and 36, we modify the speedY by the gravity of the game. You find another check in line 35 which compares the speedY to the maximum speed. What's this, you may ask. Let me bring you back in time again to the day where you attended a Physics class that talks about terminal velocity.

Well, in a nutshell, the theory goes that a free falling object eventually achieves a maximum velocity. In practical sense, you should not see Tooney moving ever-increasingly-faster as it falls. At his highest point, he should slowly drop faster and faster until a point of time where he just drops down with a constant speed, in this case, C.MAX_SPEED.

Lines 38 to 45 is where we finally move the player in the vertical direction. If the player is in air, we update its y position by what the speedY says. If the player is not in the air, we just need to update Tooney to the location of the brick he was standing on.

There will be situations where the player is standing on a brick, and eventually moves away from it. Lines 47 to 54 checks and accounts for that. If hitAreaFloor not longer collides with the brick, it means that the player has moved off.

Step 7 - Handle the Animation of the Player

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//Animate
if (this.inAir)
{
    if (this.currentLabel != C.PLAYER_JUMP)
        this.gotoAndPlay(C.PLAYER_JUMP);
}
else if (this.speedX > 0)
{
    if (this.currentLabel != C.PLAYER_RIGHT)
        this.gotoAndPlay(C.PLAYER_RIGHT);
}
else if (this.speedX < 0)
{
    if (this.currentLabel != C.PLAYER_LEFT)
        this.gotoAndPlay(C.PLAYER_LEFT);
}
else
{
    if (this.currentLabel != C.PLAYER_IDLE)
        this.gotoAndPlay(C.PLAYER_IDLE);
}

The last part of the player's update function is where we handle the animation of the player. There are four animations that I drew for Tooney. If there is a positive value in speedX, we can be sure that Tooney is moving to the right, hence we can play the animation labelled "right". We do a check prior to it because we do not want it to keep jumping to the start of the animation.

But if there is a value in speedY, we play its jumping animation as a priority.

A much tougher tutorial this one is ... but hey, we got through it!

The Game

And here you have it ... the working game! If you like it ... share it!

Download the Game Files

The files you need to create this game can be downloaded from here.

How To Play

Objective: Keep Tooney alive by jumping onto the platforms as they fall.

Controls: Press LEFT and RIGHT keys to move Tooney. Press SPACEBAR to jump.


Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player


Flash Resources
Preloader FPS Display Sounds & Music
Keycodes Name Generator
Game Development Resources
Sprite Sheets


Posted by gelto

on 2014-07-17 22:41:28

hey there .. for creating the movieclip avatar, does the parts needed to be used needs to be movieclip as well ..??
for example, the antenna. Should I make it into a movie clip first before putting in into the avatar movieclip?


Posted by Joseph

on 2014-03-31 21:58:01

@Ezekiel, don't worry about the warning. It's because we are reusing variable name.

The score is "not functioning" because you republished it, I supposed with Flash CS5 or above. Select the TextField, and find this option "Use Device Fonts".

Otherwise, embedding the fonts works as well.


Posted by Ezekiel

on 2014-03-30 02:45:36

After downloading the game file and hitting ctrl+enter to publish it,(without doing any editing to your file),I receive a warning: 3596: Duplicate variable definition.

However, I can still play the game just fine, but the score is not functioning.

Is there something I need to edit to have it functioning the same as your website published game example?

Thank you,
Ezekiel


Posted by Joseph

on 2014-01-02 22:37:00

The HitArea layer is inside the Movieclip for Player.
There's a screenshot in the tutorial too.


Posted by Toqa

on 2013-12-20 04:12:53

Hey!
the image file is empty !
and i can't really find the HitArea layer !
help please


Posted by Jeff Ibacache

on 2013-08-29 21:47:22

Hey!, awesome tutorial! Nice to start ! :)
Cheers!
Jeff.


Posted by Joseph

on 2013-05-01 11:46:34

Hi Jaime!
Did you check out the gameframework tutorial? That's the tutorial you should head out to first to get a grasp of how to structure your entire game.

I think that should help you in understanding where to put the codes. :)


Posted by Jaime

on 2013-04-21 16:24:56

Hi :)

I love your website and tutorials, however, I'm getting really lost of where to actually out all the coding. I'm trying to make my own platform game like this one but I don't know which parts you should put these codes. Please help me. I would greatly appreciate it.

Thank you for your time.


Posted by Ryan

on 2013-03-09 00:01:19

Hi, I love your tutorials! They help a lot.Can you please post the one to make a Contra game, please?

Thanks and keep up the great work :)

Ryan.


Posted by Shiela

on 2012-12-29 18:13:51

Nice. Thanks!


Flash Tutorial Links:
Play Games: HTML5 Tutorials:
gaming tools download on app store now!
Home | About Us | Contact Us
Powered by Make Flash Games. All Rights Reserved.
By Joseph Tan