Java cooperation home
Tutorial
type and press Enter

Leveleditor

In this chapter we want to tell you how to program a leveleditor one can use in any arraybased game. An arraybased game might be a Breakout clon, Boulderdash, PacMan or a Nibbles clon like our game Snakin where we used this leveleditor too. Before you start reading this article you should know something about arrays in Java (just basics), maybe a little bit about applet parameters and of course you should download the sourcecode at the end of the chapter!

Why do we need a leveleditor?

Even though the answer to this question might be clear I want to write a few lines about it. To program a leveleditor and to make your game work with any level build with this editor is much harder and costs you much more time than to write a game that has one or two static levels. But this is worth the price if you want to program a game with many different levels. If you programmed the game that way, that it works with a leveleditor, then it costs you just a few minutes to add a new level. But if you programmed your game just for static levels it will be hard and maybe even impossible to add a new level. So the hard work at the start of your gamedesign was really worth the price! Well, let's start with the real problem!

The basic idea

Before we'll find a solution to the problem of where and how to define our levels we have to think about something else. We have to find a way, how we want to represent a level in our game.
Ok, imagine the applet area with a grid over it. Now we place the elements included in our level (walls, stones, enemies...) in the fields of this grid (just one element per field). This means that the position of every level element is defined by the row and the column number of the grid field in which the level element is placed. The easiest way to represent such a two dimensional matrix in Java is to use a two dimensional array. So every level consits of a 2D array. This array holds the different level elements and these elements can differ from level to level. So everytime we read in our levels we will place different level objects at different positions in our 2D array. Everytime we want to draw our level to the screen we go through the array and paint every object in it at the position in our grid which is represented by the column and row number of the array. Using this pretty simple idea we will now try to solve the problem, where and how we want to define our levels so that it is possible to fill the level array later.

Where and how will we define the levels?

We are programming applets so we have basicly three possibilities where we can define the levels:

  1. In an extern file
  2. In the source code of the game
  3. In the HTML - page of the applet (with help of applet parameters)

The first and the second solution are for different reasons worse than the third one. To read in an extern file into an applet is possible but not that easy so we won't do it. To define the different levels in the sourcecode is a good solution if the player should not be able to take a look at a level before he has reached it, for example if the goal of the game is to find a way out of a labyrint. But you can't use this solution if the player should be able to write a level by himself. So there is just the third alternative left and we will use this idea now in our editor.

So we will use applet parameters to define our levels. Every applet parameter has a value and a name (for details see below) and we can get the value of a parameter by calling the getParameter(parametername) - method of the applet class. Then the value of the parameter with the specific name is returned as a string. Applet parameter can be defined between the opening and the closing applet tag and look always the same just like:

    <param name= "name of the parameter" value="value of the parameter">

Well, now we know, where we will write our levels, but we still don't know what a level will look like. Ok, here comes my solution. Every level we'll write will consist of 11 parameters: 3 information parameters that hold information about the author of the level, the level description and the level name. The other 8 parameters will represent the 8 rows of the level array that represents our level in the example. The parameter names will always look like this: "Level" + "Levelnumber" + "_" + "Id". Id can have the values "Author", "Name", "Comment" or "Line" + "Rownumber". The value strings of the information parameters can have any lenght, the values of the level defining parameters have to consist of a string with length 10. Every character of the string represents one level element, in our case, stones with different colors. These colors/character pairs will be r = red, g = green, b = blue, y = yellow and another character ":" that represents grid fields in the level where no level element shall be placed. So the names of parameters of different levels will look all the same execpt for the levelnumber. Because of this structure of the parameter names it is really easy to read in the different levels using a while or a for loop, counting from 1 to the in the "Levels_in_total" defined integer (for details see readLevel - method further down this chapter). Now you can take a look at a level, which can be read by the leveleditor:

    // Start of the applettag including the normal applet information
    <applet code = Main width=300 height=400>

    // This line tells the editor how many levels are defined
    <param name="Levels_in_total" value="1">

    // These lines include the level information parameters
    <param name="Level1_Author" value="FBI">
    <param name="Level1_Name" value="Test Level 1">
    <param name="Level1_Comment" value="My first try">

    // This is the "real" level
    <param name="Level1_Line0" value="rrrrrrrrrr">
    <param name="Level1_Line1" value="bbbbbggggg">
    <param name="Level1_Line2" value="r::rrrr::r">
    <param name="Level1_Line3" value="yyyyybbbbb">
    <param name="Level1_Line4" value="rrr::::rrr">
    <param name="Level1_Line5" value="gggggyyyyy">
    <param name="Level1_Line6" value="r::rrrr::r">
    <param name="Level1_Line7" value="bgybgrybgy">

    // End of the applet tag
    </applet>

Class design of our editor

Now we'll start with the class design of our leveleditor, that will make it possible to read in a level.

  1. LevelReader :

    This class will read in a certain number of levels defined in the "Levels_in_total" - parameter using the getParameter(parametername) - method. For every level in the HTML - file it will create a instance of the class Level (see below), and stores this created level in an array of instances of the class Level. Then the method readLevels(), which is doing the job of reading in all the levels, returns this array of Level instances to the calling class.

  2. Level :

    This class saves the values of the information strings Author, Levelname and description and holds the 2D array (stone_map) with the level elements. As I already said, this array saves instances of the class Stone and, according to the definition of the level, in different colors. Each stone "knows" its color and its position in the grid. The class level has also a method to paint the whole level to the screen.

  3. Stone :

    This class holds the color and the position of the stone in the applet area. The position (in pixel) is calculated in the constructor of the class using the information in which column and row the stone is placed in the stone_map array of the level instance. A stone instance has also a own paint method to paint the stone in the right color and at the right position.

  4. Main :

    This class holds an array of instances of the class level. A level can be chosen out of this level array using the cursor keys and then the chosen level is painted to the screen. This is just a test class and has absolutly no meaning for the leveleditor.

  5. C_LevelEditor :

    To make the leveleditor more flexible without needing to change the sourcecode in general, all constant values (number of lines in one level, number of columns in one level, grid size...) are stored in the class C_LevelEditor. If you want to use more lines in your level... you just have to change the values of the corosponding constant. So this class holds some static constants and nothing else!

The sourcecode of the most important methods.

As always I won't discuss every single line of code in detail, but I'll talk about the most important methods and classes. These are the class LevelReader and its method readLevels() and the class Level itself. The classes Main, Stone and C_LevelEditor are pretty simple and you should be able to understand them by yourself.

LevelReader

First of all we have to read all defined levels into the applet. This happens in the class LevelReader using the method readLevels(). The method is pretty simple and selfexplaining, the only special thing is, that we have to set a refference to the applet class in the class LevelReader. This refference to the Component = Applet is needed to use the getParameter() - method. Well, here comes the code:


    import java.util.*;
    import java.applet.*;
    import java.awt.*;


    public class LevelReader
    {
      // Variables
      private int levels_in_total;

      // Array, stores all generated instances of the class Level
      private Level [] level_array;

      // Appletrefference
      private Component parent;

      public LevelReader (Component parent)
      {
        // Initialize appletrefference
        this.parent = parent;

        // Get number of levels in total
        levels_in_total = Integer.parseInt (((Applet)parent).getParameter (C_LevelEditor.total_levels));

        // Initialize level_array
        level_array = new Level [levels_in_total];
      }

      /* This method reads every level in the HTML - Page, generates a
      instance of the class Level (for every level) and stores it in the
      level_array*/
      public Level [] readLevels ()
      {
        for (int i = 1; i <= levels_in_total; i++)
        {
          // generate new level
          Level level = new Level ();

          // get and set information parameters
          level.setAuthor (((Applet)parent).getParameter ("Level" + i + "_" + C_LevelEditor.author));
          level.setName (((Applet)parent).getParameter ("Level" + i + "_" + C_LevelEditor.name));
          level.setComment (((Applet)parent).getParameter ("Level" + i + "_" + C_LevelEditor.comment));

          // read in all the lines and store them in the level
          for (int j = 0; j < C_LevelEditor.number_of_lines; j++)
          {
            level.setLine (((Applet)parent).getParameter ("Level" + i + "_Line" + j), j);
          }

          // store level
          level_array [i-1] = level;
        }
        return level_array;
      }
    }

The class Level

Now we are able to read in the levels and to store them in Level instances but we still don't know anything about the Level class in detail. Now we have to generate a real level mainly the stone_map which holds the different level elements out of the string information we get out of the level definition in the HTML - page. The stone_map 2D array stores the level elements at the same position where they appear in the level definition (for example a "r" occurs at line 3 as the third character of the string, then a red stone object is generated in the array in row 3 and column 3). First of all this class has some set and get methods to get and set the values of the level information parameters. Much more interesting is the method setLine. This method gets one line of the level definition (a string) and translates this string to stone objects and stores these stone instances in the stone_map. Last but not least the class has its own paint method.

    import java.util.*;
    import java.awt.*;

    public class Level
    {
      // Variables
      private String author;
      private String name;
      private String comment;

      // Levelmatrix, stores the stone objects
      private Stone [] [] stone_map;
      public Level ()
      {
        // Initialize the stone_map, all fields are initialized with null
        stone_map = new Stone [C_LevelEditor.number_of_lines]
        [C_LevelEditor.number_of_cols];
      }

      // Method translates information of one line of the level definition to
      stone objects
      public void setLine (String line, int line_index)
      {
        char [] entrys = line.toCharArray();

        // go thourgh all chars and translate them to stone objects
        for (int i = 0; i < C_LevelEditor.number_of_cols; i++)
        {
          Stone stone = null;

          // generate red stone if char equals "r"
          if (entrys[i] == 'r')
          {
            stone = new Stone (line_index, i, Color.red);
          }

          // generate different coloured stones the same way
          ...

          // If char is unknown, generate no stone, which means that this
          // array field stays null
          else
          {
            // do nothing
          }

          // store stone in array if it is not null
          if (stone == null)
          {
            // do nothing
          }
          else
          {
            stone_map [line_index] [i] = stone;
          }
        }
      }

      // set and get methods for the information strings
      ...

      // Method paints level
      public void paintLevel (Graphics g)
      {
        // go through the whole stone map and paint stones
        for (int i = 0; i < stone_map.length; i++)
        {
          for (int j = 0; j < stone_map[i].length; j++)
          {
            Stone stone = stone_map [i][j];

            // paint stone or do nothing if stone is null
            if (stone == null)
            {
              // draw nothing
            }
            else
            {
              stone.drawStone(g);
            }
          }
        }

        // paint level information
        g.setColor (Color.yellow);
        g.drawString (comment, 50, 250);
        g.drawString (name, 50, 270);
        g.drawString (author, 50, 290);
      }
    }

Conclusion

In this chapter I showed you one way to define a level in a HTML - page, to read in this level using a LevelReader and one method to represent this level in our applet (2D array). As always there are many ways to do this maybe much better ones than mine and even though we might have helped you, because you can use the methods to read in and store the level in the applet in almost every arraybased game the much harder work is still in front of you. You have to make your game work with every level someone defined (which is really hard) and of course you have to generate your own level elements, change the number of level lines... . Ok, I hope I could help you a little bit, if you wrote a game using this editor, I would be glad if you would send it to me. Well, we are finished, here comes the link to download the sourcecode and the link to the working level editor applet (take a look a the sourcecode of the HTML - page to see what the levels look like).

SourceCode download
Take a look at the applet

Next chapter

Scrolling
Fabian Birzele, 2001-2004.
web-design: Vadim Murzagalin, 2004.