Java cooperation home
Tutorial
type and press Enter

Platform game basics

Since I started to implement J-Rio one year ago, I thought about writing a tutorial chapter about the basics of platform games, but until today I never had the time to do it. But as many mails I got in the last year included questions about techniques of J-rio and platform games in general, I think that such a chapter could be very usefull to some of you. Also as I won't give the sourcecode of J-rio away, people that asked me for the sourcecode have with this chapter the chance to take a look behind the scenes of my J-rio implementation because the example applet we will implement in this chapter is some kind of a lite version of J-rio. Well, ok, I hope that I can help you a little bit with this chapter and before we start you should take one more look at the chapters about the level editor and about scrolling because I'll use the techniques I introduced in those chapters in our platform game but I won't talk about them in detail anymore. You should also download the sourcecode of the example applet now and work the code through while you read this tutorial. This chapter won't give you the details to every line of code but I'll focus on three major problems. First, we'll take a closer look at the class design I have chosen to implement the game, second, we'll talk about the methods and attributes of the player class where I'll focus on things that are related to the movement of the player and last but not least we'll talk about the structure and function of the class level .

The class design of our platform game

The more complex a game (or any other program) gets, the more important becomes a good class design to allow the programmer to add new functions to the game easily. In our case (or in my case when I implemented J-rio) we want to have the possiblity to add new enemies and level elements to our already released game without any major changes of the code and it should also be easy to write new levels for the game. I think that I managed those problems pretty well with the J-rio class design and that's why I'll use it in our case too, even though there might be simpler but maybe not so powerful solutions (this does not mean that I found the only correct solution and there might not be better ones than mine!). For example it is really easy to add new levels to J-rio, just take a look at the J-rio level editor .
If you downloaded the sourcecode of this chapter and extracted the *.zip - file you'll find the following classes (Note: what follows now is just an overview, many details will follow later):

The class Main

First of all the Main class implements the applet itself, which means the init() , start() , stop() , destroy() and paint(Graphics g) - methods and includes the main game thread within the method run() of the interface Runnable . It also holds methods to handle player events (keyDown...) and two attributes: an instance of the class player and one instance of a child class of the class level (in our case only the class LevelOne). In our run() - loop we manage the complete game which means to call methods to draw player and level, to scroll the level and the player if necessary, to test for collisions between player and level elements and so on. Please note that the class main does not implement one of those methods on its own but calls methods in the classes Player and Level.

The class Player

This class implements behaviour and attributes of the player object. This means it holds information about the position of the player in the game, stores the player images used to animate the player and is also responsible to control the movement of the player (which is explained in detail later).

The class LevelElement

A game like J-rio consists of many different kinds of level elements that have different attributes, behaviour and cause different interactions with the player. There are for example those "questionmark stones" that have at least two different states, which are "hit" and "not yet hit", moving platforms and the most simple level element, the ground element, that has no special behaviour at all. And for this chapter we will concentrate on this very simple element implemented in the class Ground . But despite those differences between the different kinds of level elements they have quite a bit of common behaviour, which is implemented in the class LevelElement , which is the basic class for every level element in our Platform game. This basic bahaviour contains the position of the level element, a unique integer identifier, a boolean variable "inSight" that is used for scrolling and painting the level element (see chapter about scrolling for details), as well as an Image - object to store the level element GIF. Every level element in J-rio extends the class LevelElement and the internal data structure of the class Level contains only instances of the child classes of the class LevelElement that are identified by their unique integer id.

The class Ground

This class represents exactly such a child class of the class LevelElement. But this kind of LevelElement has no special behaviour so that it only calls the super constructor in its own constructor.

The class Level

The abstract class Level is maybe the most important and complex class of the game. The class holds a method to translate the string representation of the level (given in the class LevelOne) to the internal data representation of the level which is a two dimensional array of LevelElement - instances. It also holds methods used for collision control between player and level elements. What the internal representation looks like, how it is created and how the collision control works will be explained later in this chapter.

The class LevelOne

This class contains level specific definitions especially the definition of the level itself consisting of 25 strings representing 25 rows of the level. The color of the background can also be manipulated using the initializeColorArray() method of the class LevelOne.

Die Klasse C_Jump

To make changes of the game size, levelelement size... easier all constants used in the game are written down in a special class, in our case in the class C_jump.

How the classes work together

As I already said the class Main holds an instance of the Player - object, as well as an instance of a child class of the class Level . Main is also responsible for managing the complete game. The class Player is used to store player specific attributes and to move, draw, animate and scroll the player correctly, whereas the class Level is used to hold the internal representation of the level, to draw and to scroll the level as well as to test for collisions between the player and level elements. A level is implemented by a child class of the class Level , in our case by the class LevelOne . When we construct a instance of the class LevelOne, the string representation of the level is translated to the two dimensional array of LevelElement - objects, stored in the parent class of LevelOne Level . Every level element is a child class of LevelElement , in our platform game there is only the class Ground .
I hope that I could make clear now, why I designed the classes like I did, if there are any problems left you maybe should take a look at the level editor chapter , where I used quite the same design but maybe a little bit simpler. Now, we'll take a closer look at the classes Player and Level .

Details of the class Player

The animation and movement control of the player is very important for our game. But to control the movement of the player in a platform game is not that simple as it might seem, because we have to take care that our player has to stop walking or jumping if he hits a wall, he has to fall down of any platform if the end is reached, has to stop falling if he lands on a platform and so on. That's why I'm going to explain my solotion to those problems within the next part of this chapter.
First of all you have to recognize that in this platform game the movement of the player is controlled in a indirect way using boolean flags. In our player class / object the keyboard input of the human player and results of the collisions control in the class Level only set boolean values of four movement flags and do not cause any movement of the player on their own (which means they don't change the x of y positions). The run() - method of the class Main calls a method named playerMove() of our Player class which now moves the player according to the values of the movement flags. Let's take a closer look on those flags:

The four movement flags and their control

Our player object as four, partly independent movement flags:

  • walking_left : shows if the player should move to the left and is mainly controlled by the keyboard input of the human player, so its value is set to true if the left arrow key is pressed and is set to false if the key is released. But the flag is also influenced by the collision control of the level, becaues our player has to stop movement if it hits a wall on its left side.

  • walking_right : same as the walking_left flag, only in the other direction (no surprise I think ;-)

  • jumping : this flag indicates that our player has to jump. But things are a little bit more complicated here because on the one hand, this movement is controlled by two controllers: the human player pressing or releasing the 'a' - key and the collision control of the level class (for example if the player bangs his head against a wall). On the other hand the player may not be able to jump one more time if he's already jumping or he may not jump if he's falling and so on, details on those problems and their solution follow later.

  • falling : This flag is only controlled by the collision control of the level class (set to true if the player leaves a platform...). Here we need some tricks too to stop the player right on the surface of a new platform (or at least to correct mistakes made here) and so on, details again later.

The walking_left and walking_right as well as the falling and jumping flags may not be true at the same time. But falling / jumping and walking_left / walking_right are completely independent from each other so they can be true together because our player must have the chance to jump to the left or to fall to the right side. The movement flags are controlled from two different sides: the keyboard input of the human player and the collision control of the level class, we'll discuss later this chapter. But now we'll take a look at the set - methods for our boolean flags and the playerMove() - method that does the movement work for us.

The set - methods for the walking_left and walking_right values are really simple, here is the code.

    // Method sets the value of the walking_left flag
    public void playerWalkLeft(boolean value)
    {
      walking_left = value;
    }

    // Method sets the value of the walking_right flag
    public void playerWalkRight(boolean value)
    {
      walking_right = value;
    }

In case of the flag jumping it's getting a little bit more complicated because we have to take care of some more things, when we set the value. First of all the player can only jump, if he's not already falling which means that the value of falling is false and if the player is not already jumping so the value of another flag jump_lock has to be false too. One more thing is, that we will use a counter jump_counter to contoll the length of the jump. This counter must be reset to 0 when the player starts a new jump which means that the values of the flags are: jumping = false, jump_lock = false and value is true. You'll find some details on the jump_counter later this chapter. Here comes the code:

    // Method to set the value of the jumping flag
    public void playerJump(boolean value)
    {
      // reset jump_counter if player starts a new jump
      if(!jumping && !jump_lock && value)
      {
        jump_counter = 0;
      }

      // the player can only jump, if he's not already falling
      if(falling)
      {
        jumping = false;
      }
      else
      {
        jumping = value;
      }
    }

The last one of the four flags is the falling flag. With this flag, it is important that if falling is set to false which means the player lands on a platform, we have to end a jump (jump_lock and jumping must be set to false). One more thing is, that in some cases the player does not stop moving right on the surface of the platform but a little bit more down so that it seem the player would be standing right in the platform. So we have to correct errors that happen here. We also have to take care that the player can't jump and fall at the same time but this is guarantied somewhere else (in the method playerMove()).

    // Method to set the value of the falling flag
    public void playerFall(boolean value)
    {
      // we want to stop falling
      if(!value)
      {
        // reset the jump flags to make new jump possible
        if(jump_lock)
        {
          jump_lock = false;
          jumping = false;
        }

        // we have to correct the player position so that the player always,
        // stands on the surface of a platform. In this case, the lower
        // position of the player modulo the Height of a level element
        // is equal 0. If this is not the case, the player is moved up
        // until this is the case and the player stands on the surface!
        while(y_pos_down%C_Jump.level_element_height != 0)
        {
          y_pos_down --;
          y_pos_up--;
        }
      }

      // set value of falling
      falling = value;
    }

Movement and animation of the player

Now as we know about the four movement flags and how they are set we will take a look at the method that actually moves the player according to the values of the flags. This happens in the playerMove() - method but before we take a look at the sourcecode of the method I have to say something about two variables I'll use in the method:

  • step_counter and picture_counter : Those variables are only necessary to animate the player. When the player made 15 "steps" we want to change the player image and this means that we have to change the value of picture_counter that tells the paint() - method which image of the player should be painted to the screen.

  • jump_counter : This counter is used to determine how far the player can jump and when a jump of the player stops. If the jump_counter value reaches a certain maximal value, the value of the jumping flag is set to false.

    // This method moves the player according to the values of the movement flags
    public void playerMove()
    {
      // walking_left flag is true
      if(walking_left)
      {
        // Change the x - position of the player
        x_pos_left -= walk_x_speed;
        x_pos_right -= walk_x_speed;

        // change the game position (important for scrolling)
        game_x_position -= walk_x_speed;

        // change the player image after 15 steps
        if (step_counter%15 == 0)
        {
          // change value of the picture_counter
          picture_counter ++;

          // reset counter if value is 2 (there are only 2 pictures)
          if(picture_counter == 2)
          {
            picture_counter = 0;
          }

          // reset step counter
          step_counter = 1;
        }
        // else increase value of the step counter
        else
        {
          step_counter ++;
        }

        // tell if player looks left (only important for animation)
        look_left = true;
      }
      // walking_right flag is true
      else if(walking_right)
      {
        ... All the same as for walking_left only to the other side...
      }

      // Value of jumping is true
      if(jumping)
      {
        // This variable exists to avoid that the player can jump one more time
        // if he's already jumping
        jump_lock = true;

        // Make sure that the value of falling is false becaues the collision
        // control sets the falling value to true even if the player is jumping
        falling = false;

        // jump_counter still smaller than 30
        if(jump_counter < 30)
        {
          // player jumps by the speed of 2
          y_pos_up -= jump_y_speed;
          y_pos_down -= jump_y_speed;
          jump_counter ++;
        }
        // if the value of the jump_counter is smaller than 30 but greater than
        // 40 jump by the speed of 1
        else if (jump_counter < 40)
        {
          y_pos_up -= jump_y_speed2;
          y_pos_down -= jump_y_speed2;
          jump_counter++;
        }
        // if the value of the jump_counter is greater than 40, player can't
        // jump further so set the value of jumping to false
        else
        {
          jumping = false;
        }
      }

      // if player is falling move playe down
      if(falling)
      {
        y_pos_up += fall_y_speed;
        y_pos_down += fall_y_speed;
      }
    }

I hope that I could show you now the most important and new parts of the class player, scrolling and painting of the player should be no problem anymore. But I think you should really take a look at the sourcecode of the class to understand everything because this class is not so simple. Well, ok, lets start with a even more complex class, the class Level

Structure and function of the class Level

In this last part of the tutorial we'll talk about the class Level and the related classes LevelOne and LevelElement . First of all we'll take a look at the "definition language" of a platform game level and we'll see how this definition language is translated to the internal data representation of the level. Those are mainly ideas I already introduced in the chapter about the level editor, if you have any problems please read this chapter first. Afterwards I'll show you some details of the collision control between level elements and the player. I won't talk about things like scrolling and painting because those things should be clear now (at least I hope so).

Definition language and internal data structure of a level

One major goal of our class design was to allow ourselfs to write new levels for our game easily. That's the reason why we define our levels with a certain "definition language" which can easily be used by humans and then we translate this level definition into a new data structure which can be used by the computer. In our design language a level consists of 25 rows represented as 25 strings. The length of those strings is variable but they all need to be of the same length. Every level element we want to use in our level is implemented as a child class of LevelElement, has a unique character identifier and at every position where this character appears in our level definition strings this level element will be generated in the internal data structure of the level. The parser in the class Level (method initializeLevel()) has to be capable to translate those string definitions into the internal data structure of our level. This internal data structure consists of a two dimensional array with null pointers at places where no level element exists in our level and instances of child classes of the class LevelElement where a level element exists in the level. This idea is explained in more detail in the chapter aboue the level editor . Ok, before we get to the collision control here comes the sourcecode:

The level definition can be found in the class LevelOne . This class is used to define all the level specific things like the background color, the level itself and so on where the real functionality of the level is implemented in the inherited methods from the parent class Level. Here comes a level in the level definition language:

/**
Legend:
":": represents a position in the level where no element should be generated
"g": represents a position in the level where a ground element should be generated
*/

// String definition of the level
// rows 1 - 10 are missing, because they don't contain any important data
public static final String row11 = "::::::::::::::::::::::::::::::::::::::::g::";
public static final String row12 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row13 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row14 = "::::::::::::::::::::::::::::::::::::g::::::";
public static final String row15 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row16 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row17 = "::::::::::::gggg::::::::::::::::g::::::::::";
public static final String row18 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row19 = ":::::::::::::::::::::::gggg::::::::::::::::";
public static final String row20 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row21 = "::::::gggg:::::::::::::::::::::::::::::::::";
public static final String row22 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row23 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row24 = ":::::::::::::::::::::::::::::::::::::::::::";
public static final String row25 = "ggggggggggggggggggggggggggggggggggggggggggg";

As you can see here it is really simple to define new levels (see the level editor version of J-rio too). But how is this simple definition translated to the internal computer readable data representation? This is implemented in the method initializeLevel(String [] definitions) of the class Level. This method parses the level definition strings and generates a two dimensional array of level elements out of them. This array is mainly used for the collision control. Afterwards all pointers to levelelements are also stored in a one dimensional array to make scrolling and painting of the level more efficient.

// Method to parse the level definition strings and generate 2D and 1D arrays.
public void initializeLevel(String [] definitions)
{
    // Initialize collision array
    collision_array = new LevelElement [C_Jump.number_of_level_lines] [definitions[0].length()];

    // Initialize some level information: level length and left and right border
    level_length = definitions[0].length() * C_Jump.level_element_width;
    left_level_border = 0;
    right_level_border = C_Jump.applet_width;

    // Counter to count the number of level elements in the level, important
    // for the initialisation of the 1D array
    int elements_counter = 0;

    // For all level definition strings do:
    for(int i=0; i<definitions.length; i++)
    {
      // generate a char array of the current level definition string
      char [] definition_line = definitions[i].toCharArray();

      // for all elements in the char array do:
      for(int j=0; j<definition_line.length; j++)
      {
        // Translate the chars to level elements
        if(definition_line[j] == ':')
        {
          collision_array[i][j] = null;
        }
        // Generate a Ground element
        else if(definition_line[j] == 'g')
        {
          // Important: position in the string definition (i, j)
          // is translated to concrete pixel position
          Ground element = new Ground(j*C_Jump.level_element_width,
            i*C_Jump.level_element_height, ground, parent, C_Jump.ground_id);

          // Store element in collision array
          collision_array[i][j] = element;

          // increase element counter
          elements_counter ++;
        }
      }
    }

    // Copy levelelement pointers to 1D array
    // Code's missing because nothing special happens
}

Test for collisions between player and level elements

Let's get to the last and maybe most comprehensive topic of this chapter. All the things we did and talked about before are important for the collision control of the game because the collision control has quite a lot to to with the movement control and the internal data representation of the game.
Well, the idea is the following: The only thing we have to do, when we want to test if the player collides with any level element is to determine the position of the player in the 2D level array, which means the players row and column and test if there is a level element (which means not a null pointer) or if there ist no element (= null pointer). If we find a levelelement at the players position, we have to do something with the movement flags of the player, in our case we have to set them to false. As you might remember, we have four movement flags so we have to test for four possible collisions: collison up (important for jumping), collision down (important for falling), collision left and collision right. Therefore I implemented some methods. The method testForPlayerCollisions() is used to manage the collision control and to decide what to do when a certain collision between a level element and the player occurs. There are also four specialized methods ( testCollisionUp , -Down , ...) that are used to make a lookup within the 2D array if a level element exists at a given player position or not. Ok, here comes the sourcecode of the method testForPlayerCollisions(Player player) as well as for the method testCollisionDown (as a example for one of those methods that test for the existence of a level element at a given player position and return this element to the calling method).

// method tests for collisions between player and level elements
public void testForPlayerCollisions(Player player)
{
    // get some player specific position values
    int player_game_pos = player.getGameXPosition();
    int player_down_pos = player.getYPosDown();
    int player_up_pos = player.getYPosUp();

    int player_left = player_game_pos - (C_Jump.player_image_width/2);
    int player_right = player_game_pos + (C_Jump.player_image_width/2);

    // Test for collisions down
    LevelElement down_element = testCollisionDown(player_game_pos, player_down_pos);

    // If there is a element below the player, the player does not fall
    if(down_element != null)
    {
      player.playerFall(false);
    }
    // if null is returned the player is falling down
    else
    {
      player.playerFall(true);
    }

    // Test for collisions up
    LevelElement upper_element = testCollisionUp(player_game_pos, player_up_pos);

    // stop jumping if there is a element
    if(upper_element != null)
    {
      player.playerJump(false);
    }

    // Test for collisions at the left side of the player
    LevelElement left_element = testCollisionLeft(player_left, player_down_pos);

    // stop movement of the player to the left
    if(left_element != null)
    {
      player.playerWalkLeft(false);
    }

    // Test for collisions at the right side of the player
    LevelElement right_element = testCollisionRight(player_right, player_down_pos);

    // stop movement of the player to the right
    if(right_element != null)
    {
      player.playerWalkRight(false);
    }
}

// Method tests for a given player position if there is a level element, this element is returned to the calling class
public LevelElement testCollisionDown(int game_pos, int player_y_down)
{
    // Translate player position into array position (row and col)
    int col = game_pos / C_Jump.level_element_width;
    int row = player_y_down / C_Jump.level_element_height;

    try
    {
      // Return element at this position or null
      if(collision_array[row][col] != null)
      {
        return collision_array[row][col];
      }
      else
      {
        return null;
      }
    }
    catch (ArrayIndexOutOfBoundsException ex)
    {
      return null;
    }
}

That's it!

Well, here I'd like to finish this chapter and I hope that I could help you a little bit. I know that this chapter is not that simple as some other chapters of this tutorial (it was neither easy for me to write, nor easy for you to understand, I think), but as the topic is not so simple this is not such a big surprise. Also I'd really like to hear what was useful and what was not with this chapter because maybe I should explain some things in more detail or so. Please write me a mail if you used this chapter and tell me what was useful and what was too fuzzy (a word my professor is always using so I wanted to use it too once in my life ;-)! Well then, as always you can download the sourcecode of this chapter and you can take a look at the example applet we've just implemented but you can play J-Rio as well, because it is "almost" the same. Good luck with the implementation of your own platform games!

Sourcecode download
Take a look at the applet
Fabian Birzele, 2001-2004.
web-design: Vadim Murzagalin, 2004.