Kilobolt
  • Home
  • Tutorials
    • Game Development Tutorial >
      • Unit 1: Beginning Java >
        • Before you begin...
        • Day 1: Setting Up
        • Day 2: Java Basics
        • Day 3: More Basics
        • Day 4: Java Math
        • Day 5: More Math
        • Day 6: If... else...
        • Day 7: More Control Flow
        • Day 8: Looping
        • Day 9: More on Looping
        • Day 10: Inheritance, Interface
        • Day 11: Threads and Graphics
      • Unit 2: Creating a Game I >
        • Day 1: Foundations
        • Day 2: Basic Framework
        • Day 3: Taking User Input
        • Day 4: Enter the Robot
        • Day 5: Background and Sprites
        • Day 6: Adding Enemies
        • Day 7: Shooting Bullets
        • Day 8: Animations
        • Day 9: 2D-Arrays
        • Day 10: Painting the Tilemap
      • Unit 3: Creating a Game II >
        • Day 1: Level Creation - Part 1
        • Day 2: Level Creation - Part 2
        • Day 3: Level Creation - Part 3
        • Collision Detection Basics
        • Day 4: Collision Detection Part 1
        • Day 5: Collision Detection Part 2
        • Day 6: Collision Detection Part 3
        • Day 7: Health System & Death
        • Day 8: Basic AI & Final Touches
      • Unit 4: Android Game Development >
        • Day 1: Introduction to Android
        • Day 2: Setting up for Development
        • Day 3: Creating our First Android Application
        • Day 4: Parts of an Android Application
        • Day 5: The Android Game Framework: Part I
        • Day 6: The Android Game Framework: Part II
        • Create an Android Game From Scratch (or port your existing game)
        • Day 7: Creating an Android Game (From Start to Finish)
      • Reference Sheet
    • Zombie Bird Tutorial (Flappy Bird Remake) >
      • Unit 1: Building the Game >
        • Introduction
        • Day 1: Flappy Bird - An In-depth Analysis
        • Day 2: Setting up libGDX
        • Day 3: Understanding the libGDX Framework
        • Day 4: GameWorld and GameRenderer and the Orthographic Camera
        • Day 5: The Flight of the Dead - Adding the Bird
        • Day 6: Adding Graphics - Welcome to the Necropolis
        • Day 7: The Grass, the Bird and the Skull Pipe
        • Day 8: Collision Detection and Sound Effects
        • Day 9: Finishing Gameplay and Basic UI
        • Day 10: GameStates and High Score
        • Day 11: Supporting iOS/Android + SplashScreen, Menus and Tweening
        • Day 12: Completed UI & Source Code
    • Android Application Development Tutorial >
      • Unit 1: Writing Basic Android Apps >
        • Before you begin...
        • Day 1: Android 101
        • Day 2: Getting to Know the Android Project
        • Day 3: The Development Machine
        • Day 4: Building a Music App - Part 1: Building Blocks
        • Day 5: Building a Music App - Part 2: Intents
        • Day 6: Building a Music App - Part 3: Activity Lifecycles
  • New Forum
  • About Us
    • Contact Us
  • Our Games
    • TUMBL: FallDown
  • Facebook
  • Twitter

GAME DEVELOPMENT TUTORIAL: DAY 3-2: LEVEL CREATION PART II

11/30/2012

69 Comments

 
Picture
Continuing with the series, we have Part 2 of the Level Creation lessons. In this lesson, we will be adding a method that will read a .txt file's information and draw a map to the screen accordingly.

A typical map file may look like this:
Picture
A map file
In such a file, each space would represent a tile (or an empty tile), and we would be able to create corresponding structures on the screen in this way.
Picture
Corresponding map on screen
Using this method, we will be able to create multiple levels for our character to traverse through.
Let's begin.

Making Changes to the StartingClass

I. We will begin at the beginning by adding a few more types of tiles to the game.

1. Find the line (yay syntax highlighting):

    public static Image tiledirt, tileocean;
 
And change it to this one:
    public static Image tilegrassTop, tilegrassBot, tilegrassLeft, tilegrassRight, tiledirt;
 
2. Find this segment in your init() method:
        tiledirt = getImage(base, "data/tiledirt.png");
        tileocean = getImage(base, "data/tileocean.png");
 
Update like so:

        tiledirt = getImage(base, "data/tiledirt.png");
        tilegrassTop = getImage(base, "data/tilegrasstop.png");
        tilegrassBot = getImage(base, "data/tilegrassbot.png");
        tilegrassLeft = getImage(base, "data/tilegrassleft.png");
        tilegrassRight = getImage(base, "data/tilegrassright.png");
 
II. You will want to download these (overwriting tiledirt.png and deleting tileocean.png).
They go inside your data folder.
Picture
tiledirt.png
File Size: 3 kb
File Type: png
Download File

Picture
tilegrasstop.png
File Size: 3 kb
File Type: png
Download File

Picture
tilegrassleft.png
File Size: 3 kb
File Type: png
Download File

Picture
tilegrassright.png
File Size: 3 kb
File Type: png
Download File

Picture
tilegrassbot.png
File Size: 3 kb
File Type: png
Download File



While you're at it, download the map file also (place it also in the data folder):
Picture
map1.txt
File Size: 1 kb
File Type: txt
Download File

III. Now that we got our assets ready, navigate to your start() method.
In here, we have a double for loop that creates our Tile map. We will now be encapsulating this inside a method. Find the following section:

        // Initialize Tiles

        for (int i = 0; i < 200; i++) {
            for (int j = 0; j < 12; j++) {

                if (j == 11) {
                    Tile t = new Tile(i, j, 2);
                    tilearray.add(t);

                } if (j == 10) {
                    Tile t = new Tile(i, j, 1);
                    tilearray.add(t);

                }

            }
        }
Change it to the following. Ignore errors.
// Initialize Tiles
        try {
            loadMap("data/map1.txt");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
This is a pretty straightforward segment of code. We call the loadMap() method (which we will now create) and pass in the parameter map1.txt which you should have downloaded above.

Since we are dealing with files, Java requires us to create a try/catch statement in case an exception occurs.

IV. Now we will create the loadMap() method.
The easiest way to do so is to put your mouse over the error shown in the try/catch statement like so:
Picture
Click "Create method 'loadMap(String)'"

Eclipse will automatically create the loadMap for you. You have to make a few changes.

1. You want to change the name of the parameter (string) to filename.
2. In addition, as we are dealing with files in this method, we want to handle the exceptions. Rather than use try/catch statements, we have an alternative. Declare "throws IOException" after the parameters are stated in the method declaration.

If you do so, the method will look like this.

    private void loadMap(String filename) throws IOException{
        // TODO Auto-generated method stub
       
    }
 
Let's now talk in detail about what this loadMap() method will do.

LoadMap() Method

The functions of this method are as follows:

1. To create an ArrayList and fill it with the lines parsed from the .txt file.
2. To utilize a double for loop to create tiles (we are basically rewriting the tile initializing for loops in the init() method in this method).
3. Assigning a type (recall in the last unit, we used integers to represent whether the tile was an ocean tile or a grass tile) by reading the text file at the (x , y) index of the tile.

The completed method looks like this:

    private void loadMap(String filename) throws IOException {
        ArrayList lines = new ArrayList();
        int width = 0;
        int height = 0;

        BufferedReader reader = new BufferedReader(new FileReader(filename));
        while (true) {
            String line = reader.readLine();
            // no more lines to read
            if (line == null) {
                reader.close();
                break;
            }

            if (!line.startsWith("!")) {
                lines.add(line);
                width = Math.max(width, line.length());

            }
        }
        height = lines.size();

        for (int j = 0; j < 12; j++) {
            String line = (String) lines.get(j);
            for (int i = 0; i < width; i++) {
                System.out.println(i + "is i ");

                if (i < line.length()) {
                    char ch = line.charAt(i);
                    Tile t = new Tile(i, j, Character.getNumericValue(ch));
                    tilearray.add(t);
                }

            }
        }

    }
 
Discussion:
We initialize some variables in the first section (including the ArrayList) and the width and height (which represent the number of lines and characters per line in the text we read).

We use a while loop to fill the ArrayList lines with the lines read from the text file. If the line begins with an "!", we ignore it (I used ! to begin comments in the map file).

We give width and height the appropriate values to serve their function. 


The familiar double for loop creates tiles at the (i, j) index, i representing the x coordinate and j representing the y coordinate. 



Now, we check what the character at the index i of the current line is (which has index j). We then create a new Tile with the parameters x position, y position, and type. Since the characters we read from the text file are characters, not integers (there's a distinction between '1' and 1 much like there's a distinction between "a" and a), we use a built-in method: Character.getNumericValue(ch) to convert it to a number.

The purpose of the if(i < line.length()){... statement is there to ensure that our i index never searches for a character that does not exist. If this is not there, we can have all kinds of problems with our map.txt so this is very important!

Finishing Touches

Now all we have to do is handle the tile types inside the Tile class. Make these changes to the if statements within the constructor, and we will be good to go!

    public Tile(int x, int y, int typeInt) {
        tileX = x * 40;
        tileY = y * 40;

        type = typeInt;

        if (type == 5) {
            tileImage = StartingClass.tiledirt;
        } else if (type == 8) {
            tileImage = StartingClass.tilegrassTop;
        } else if (type == 4) {
            tileImage = StartingClass.tilegrassLeft;

        } else if (type == 6) {
            tileImage = StartingClass.tilegrassRight;

        } else if (type == 2) {
            tileImage = StartingClass.tilegrassBot;
        }

    }
 
Finally, let me explain my reasoning for choosing the seemingly arbitrary typeInt values.

If you look at a typical numpad:
Picture
There are arrows below the numbers 8, 4, 6, and 2. I used these numbers to represent dirt tiles with grass on the side that the arrows point towards. 5, of course, has no arrows and represents a dirt tile with no grass on any side.

Examine the map1.txt file to see how I applied this technique to create a simple level.

At this time, you can run the code and it will work!

In the next lesson, we will implement some basic collision detection to these tiles, and we will move on from there! (Please don't send me emails saying "I can't jump on the platforms!" because we haven't coded anything to make them platforms yet. They are as of now just images).

Like Kilobolt Studios on Facebook and I will keep you posted.
Picture
unit3day2.zip
File Size: 302 kb
File Type: zip
Download File

Go to Unit 3: Day 1
Go to Unit 3: Day 3
69 Comments

    Author

    James Cho is the lead developer at Kilobolt Studios. He is a college student at Duke University and loves soccer, music, and sharing knowledge.


© 2014 Kilobolt, LLC. All rights reserved.