[Solved] Tiling in Java

I have looked for days and days, but could not find a solution to my problem.
How do you tile a map in java?

When i worked with XNA the array.length gave me more or less an x and y, but java does not. If someone can post either source code or instructions on how to do this it would be extremely helpful.

Thanks
~Scyth
EDIT: Solved, for others this is the code:
Class Variables Needed:

public static final int BLACK = 0;
   public static final int GRASS = 1;
   public static final int Magenta = 2;
   
   int[][] tiles;

   InputStream level1 = this.getClass().getResourceAsStream("level.txt");

Constructor must have this code:

public Game()
{
// Reads the text file
try {
		tiles = getIntsFromTextFile(level1);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

Your Render Methods:

public void render(Graphics2D g) {
	   renderTiles(g);
	}

	public void renderTiles(Graphics2D g) {
	   // Iterate through all tiles:
		for (int y = 0; y < tiles.length; y++) {
		      for (int x = 0; x < tiles[y].length; x++) {
	         renderTile(x, y, tiles[y][x], g);
	      }
	   }
	}
	public void renderTile(int x, int y, int tileID, Graphics2D g) {
		   switch (tileID) {
		   // don't forget the breaks! :)
		   case BLACK: g.setColor(Color.black); g.drawRect(x * 32, y * 32, 32, 32); break; 
		   case GRASS: g.setColor(Color.green); g.drawRect(x * 32, y * 32, 32, 32); break;
		   case MAGENTA: g.setColor(Color.MAGENTA); g.drawRect(x * 32, y * 32, 32, 32); break;
		   }
		}

Your methods to read the text file:

public int[][] getIntsFromTextFile(InputStream input) throws IOException {
	   // Create a list of rows. It needs to be resizable, because
	   // we don't know the number of rows the text file has in the
	   // beginning.
	   ArrayList<int[]> yList = new ArrayList<int[]>();
	   // A bit try-finally stuff, so we can close the reader properly.
	   BufferedReader reader = null;
	   try {
	      // We create a BufferedReader by putting InputStreamReader-
	      // glue between the Reader and the FileInputStream.
	      reader = new BufferedReader(new InputStreamReader(input));
	      
	      // This variable counts the y rows.
	      int y = 0;
	      // This variable will hold every newly read line
	      String line = null;
	      // This reads a line and puts it into "line".
	      // line is then tested against null, because when
	      // the line is null, then we reached the end of the
	      // file.
	      while ((line = reader.readLine()) != null) {
	         // We parse the string into a int[] and put it
	         // into the list of rows.
	         yList.add(parseString(line));
	      }
	   } finally {
	      // "finally" we close the reader ;)
	      if (reader != null) {
	         reader.close();
	      }
	   }
	   // This now creates an int[][] out of the ArrayList<int[]>.
	   // This can be done, since we know the number of rows
	   // now. You can see ArrayList's as a resizable array.
	   int[][] ints = new int[yList.size()][];
	   for (int i = 0; i < ints.length; i++) {
	      ints[i] = yList.get(i);
	   }
	   return ints;
	}

	public int[] parseString(String str) {
	   // We can create a int[], since we know
	   // the length of the incoming string,
	   // which is for example: "00192773716749".
	   int[] array = new int[str.length()];
	   
	   // We loop through each character in the string
	   // and create an integer from the returned char
	   // with Integer.parseInt(...);
	   for (int i = 0; i < str.length(); i++) {
	      char c = str.charAt(i);
	      // I say "" + c to create a string
	      // out of the char. I'm not sure if
	      // it's needed. You could try it without.
	      array[i] = Integer.parseInt("" + c);
	   }
	   return array;
	}

Voila! You’re done, all you have to do is create and edit a level.txt file and it will display the corresponding tiles in the correct position, if our tile are not 32x32 then you should change *32 in each of the cases:

public void renderTile(int x, int y, int tileID, Graphics2D g) {
		   switch (tileID) {
		   // don't forget the breaks! :)
		   case BLACK: g.setColor(Color.black); g.drawRect(x * 32, y * 32, 32, 32); break; 
		   case GRASS: g.setColor(Color.green); g.drawRect(x * 32, y * 32, 32, 32); break;
		   case MAGENTA: g.setColor(Color.MAGENTA); g.drawRect(x * 32, y * 32, 32, 32); break;
		   }
		}

Tiling is implemented in LibGDX, isn’t it? What do you mean by tiling a map?

like if i had a text file which was like:

111111111122222221111011123
115464531111112223333333111
222222221111111111100000000

etc and then in java i can read each number, and load paint an image in accordance to the number, eg: 1 would be dirt, 2 would be grass etc
then i could use this to paint my map to the screen.
however i do not know how to read each number and interpret there position to correspond to where i want them for example the first 1 in the first line would be painted at (0,0) and the first one on the second line would be painted at (0,32) etc

any ideas?


public int[][] getIntsFromTextFile(File file) throws IOException {
	// Create a list of rows. It needs to be resizable, because
	// we don't know the number of rows the text file has in the
	// beginning.
	ArrayList<int[]> yList = new ArrayList<int[]>();
	// A bit try-finally stuff, so we can close the reader properly.
	BufferedReader reader = null;
	try {
		// We create a BufferedReader by putting InputStreamReader-
		// glue between the Reader and the FileInputStream.
		reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
		
		// This variable counts the y rows.
		int y = 0;
		// This variable will hold every newly read line
		String line = null;
		// This reads a line and puts it into "line".
		// line is then tested against null, because when
		// the line is null, then we reached the end of the
		// file.
		while ((line = reader.readLine()) != null) {
			// We parse the string into a int[] and put it
			// into the list of rows.
			yList.add(parseString(line));
		}
	} finally {
		// "finally" we close the reader ;)
		if (reader != null) {
			reader.close();
		}
	}
	// This now creates an int[][] out of the ArrayList<int[]>.
	// This can be done, since we know the number of rows
	// now. You can see ArrayList's as a resizable array.
	int[][] ints = new int[yList.size()][];
	for (int i = 0; i < ints.length; i++) {
		ints[i] = yList.get(i);
	}
	return ints;
}

public int[] parseString(String str) {
	// We can create a int[], since we know
	// the length of the incoming string,
	// which is for example: "00192773716749".
	int[] array = new int[str.length()];
	
	// We loop through each character in the string
	// and create an integer from the returned char
	// with Integer.parseInt(...);
	for (int i = 0; i < str.length(); i++) {
		char c = str.charAt(i);
		// I say "" + c to create a string
		// out of the char. I'm not sure if
		// it's needed. You could try it without.
		array[i] = Integer.parseInt("" + c);
	}
}

Mind the edit.

thanks for commenting! didnt understand the last few parts of it! Thanks so much!

Contents of file “level.txt”


111111111122222221111011123
115464531111112223333333111
222222221111111111100000000

Load level method


private void loadLevel(){
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("level.txt")));
        String line = null;

        while ((line = reader.readLine())!=null){
            for (int i=0; i<line.length; i++){
                int c = Integer.parseInt("" + line.charAt(i));
                switch (c){
                    case 0: Map.add(new Object0()); break;
                    ....
                }
            }
        }
    } catch (Exception e){
        // Error. exit. Should not happen
        e.printStackTrace();
        System.exit(-1);
    }
}

@matheus: so now, considering that we know how many rows there are file and we read each character one at a time, is there a way to interpret the row and “column” of each char? you probably already have it coded but I’m an amateur at java.
@ SHC will that give both row and “column”?

My code is not really the best, because it’s actually like DOM and SAX for xml, but that’s another story. Short: It reads all data and then the data is parsed, instead of parsing all the data right away.

Anyways.

You’d need to create tiles from the int[][] array you got. You basically got an array of numbers reaching from 0 to 9, describing what tiles to put.
So you’d just iterate through the array and create the tiles as you want them (you could also just use the array as an array of tile ID’s and don’t use classes for tiles at all, but yeah. Thats another story as well…)


public void createTiles(int[][] dataArray) {
	for (int y = 0; y < dataArray.length; y++) {
		for (int x = 0; x < dataArray[y].length; x++) {
			int tileID = dataArray[x][y];
			createTile(x, y, tileID); // <- You have to create this method. I have no Idea about your codebase :)
		}
	}
}

Mind that I first iterate throught y ([icode]for (int y = …)[/icode]) and then through x ([icode]for (int x = 0; x …)[/icode]). This is needed, or else your map in the file will be rotated by 90° :wink:
(Or probably not… if so, try swapping those variables :smiley: )

Thanks Matheus, i literally spent ages trying to work out how to use it so now i’ll try this and if it works - then thank you so much, this has been my problem since i started game programming in java!

Yes. It reads each line i.e line and each character is a cell in the row.

EDIT : Too much code :stuck_out_tongue: removed it

Are u using a “tile-editor”?
If one line in the txt is one complete line of tiles, you could use .readline()
->after that u split it at the " " and parse it in the int[x (increasing)][y]
-> next line int[x (increasing)][y+1]

If you one line isn’t one complete line of tiles, you could just read the whole file
->after that u split it at the " " and parse it in the int[x][y] until the width is reached.
-> next line int[x (increasing)][y+1]

Am I wrong or are you not able to create tiles with the id 10?

In addition you could write a tile-mapeditor on your own, it’s not that hard.
To save the map afterwards, I would serialize it, especially if you have entities on it, it makes it much easier.
best regards

Oh yes. I see :slight_smile:

Better use our pastebin for such big codes. Just remember that for the next time :slight_smile:
(If you write a reply just like me right now, there is a link “our pastebin” at the bottom)

You don’t manage your tiles yourself right now. Okey.

You need to change you [icode]Game[/icode] class to take another field:
[icode]
int[][] tiles;
[/icode]

Then, when initializing (in the constructor) you load the text file into your tiles array:
[icode]
// catch IOException here
tiles = getIntsFromTextFile(level1);
[/icode]

And now you have a wonderful [icode]int[][] tiles[/icode]-array you can use to draw your tiles.

You can do this in the [icode]render(Graphics)[/icode] method:
[icode]
public void render(Graphics g) {
renderTiles(g);
}

public void renderTiles(Graphics g) {
// Iterate through all tiles:
for (int x = 0; x < tiles.length; x++) {
for (int y = 0; y < tiles[x].length; x++) {
renderTile(x, y, tiles[x][y], g);
}
}
}

// These belong to the top of your class file :slight_smile:
public static final int BLACK = 0;
public static final int GRASS = 1;
public static final int EARTH = 2;

public void renderTile(int x, int y, int tileID, Graphics g) {
switch (tileID) {
// don’t forget the breaks! :slight_smile:
case BLACK: g.setColor(Color.black); g.drawRect(x * 32, y * 32, 32, 32); break;
case GRASS: g.setColor(Color.green); g.drawRect(x * 32, y * 32, 32, 32); break;
case EARTH: g.setColor(Color.brown); g.drawRect(x * 32, y * 32, 32, 32); break;
}
}
[/icode]

And then it should work.

It’s important that you only load the tiles at the beginning! (only call [icode]getIntsFromTextFile[/icode] once…) If you dlo that every time you want to render, you will waste lots of memory and it will be slow. Very slow.

@Riven: Wuahaha :smiley: I like to abuse your wonderful icodes :slight_smile: I don’t have line numbers anymore, but it’s just so beautiful now :stuck_out_tongue:

Ok here’s my code:
http://pastebin.java-gaming.org/fa43e1f6f31

technically it should work, but all I’m getting is a blank screen? Any suggestions. And thanks matheus for continually helping me :slight_smile: I appreciate it

I’m getting these errors and I don’t know why:

java.io.IOException: Stream closed
	at java.io.BufferedInputStream.getBufIfOpen(Unknown Source)
	at java.io.BufferedInputStream.read(Unknown Source)
	at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
	at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
	at sun.nio.cs.StreamDecoder.read(Unknown Source)
	at java.io.InputStreamReader.read(Unknown Source)
	at java.io.BufferedReader.fill(Unknown Source)
	at java.io.BufferedReader.readLine(Unknown Source)
	at java.io.BufferedReader.readLine(Unknown Source)
	at com.epacgames.games.redxog.Game.getIntsFromTextFile(Game.java:185)
	at com.epacgames.games.redxog.Game.<init>(Game.java:70)
	at com.epacgames.games.redxog.Game.main(Game.java:161)
Exception in thread "Thread-2" java.lang.NullPointerException
	at com.epacgames.games.redxog.Game.renderTiles(Game.java:145)
	at com.epacgames.games.redxog.Game.render(Game.java:140)
	at com.epacgames.games.redxog.Game.render(Game.java:123)
	at com.epacgames.games.redxog.Game.run(Game.java:99)
	at java.lang.Thread.run(Unknown Source)

this is extremely frustrating :stuck_out_tongue:
@Phibedy, if all else fails then i’ll try that :frowning:

This:


public Game(){
      // removed Java2D setup stuff
      try {
          createTiles(getIntsFromTextFile(level1));
       } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
       }
      
      try {
      tiles = getIntsFromTextFile(level1);
   } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
   }
      
   }

is your problem.

In your [icode]getIntsFromtextFile()[/icode] method you close the stream at the end (like you should), which means that when you try to use that stream the second time, it is closed and throws the [icode]IOException[/icode]. Then the [icode]tiles[][][/icode] array is never created and causes the [icode]render()[/icode] method to throw a [icode]NullPointerException[/icode].

To fix:

Remove this part:


try {
          createTiles(getIntsFromTextFile(level1));
       } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
       }

That should fix things.

EDIT: it seems matheus23 beat me to it.

Yup. Do what HeroesGraveDev showed. You have to replace the code, it wasn’t meant to be added :slight_smile:

Huh?

This:

EDIT: It appears you said that for performance reasons, not the stream closing and throwing the exception. :point:

Yep. You’re right :smiley: I didn’t see it like that :slight_smile:
InputStreams generally shouldn’t be stored as fields, but requested everytime you need one.
Otherwise you “abuse” them just like that.

Ok, that has definitely worked! it has removed the IOException and the NullPointerException, however now it gives me a:

Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException: 2
	at com.epacgames.games.redxog.Game.renderTiles(Game.java:139)
	at com.epacgames.games.redxog.Game.render(Game.java:133)
	at com.epacgames.games.redxog.Game.render(Game.java:116)
	at com.epacgames.games.redxog.Game.run(Game.java:92)
	at java.lang.Thread.run(Unknown Source)

and i’ve tried to find what a ArrayIndexOutOfBoundsException is to try and tackle the problem, but i cant find it. Again, Google hs failed me :frowning:

:stuck_out_tongue:

as you can probably see I’m new to game programming in java but I must say i appreciate your help :slight_smile: