Generating large tile maps from 2d array?

Hey guys!

I’m trying to generate large tile maps from a 2D array in for loops:

package com.elements.world;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;

import com.elements.gameobject.entity.Entity;
import com.elements.gameobject.tile.Tile;

public class Level {

	public Tile[][] Tiles;
	
	public Level(String name) {
		Tiles = new Tile[50][50];
		
		for (int x = 0; x < Tiles.length; x++) {
			for (int y = 0; y < Tiles[0].length; y++) {
				Tiles[x][y] = new Tile(new Rectangle(x * 32, y * 32, 32, 32));
			}
		}
	}
	
	public void Generate() {
		for (int y = 0; y < Tiles.length; y++) {
			for (int x = 0; x < Tiles[0].length; x++) {
				Tiles[x][y].setTileID("elements:air");
				
				if (y > 50 / 4) {
					try {
						if (Tiles[x-1][y-1].getTileID() == "elements:dirt") {
							Tiles[x][y].setTileID("elements:dirt");
						}
					} catch (Exception e) {}
					
					try {
						if (Tiles[x+1][y-1].getTileID() == "elements:dirt") {
							Tiles[x][y].setTileID("elements:dirt");
						}
					} catch (Exception e) {}
					
					try {
						if (Tiles[x][y-1].getTileID() == "elements:dirt") {
							Tiles[x][y].setTileID("elements:dirt");
						}
					} catch (Exception e) {}
					
					if (new Random().nextInt(120) < 20) {
						Tiles[x][y].setTileID("elements:dirt");
					}
				}
			}
		}
	}
	
	/* Unused. */
	public void AddEntity(Entity entity) {}
	public void renderEntites(Graphics g) {}
	public void renderTiles(Graphics g) {}
}

I’m aware 50x50 isn’t that big, so when I try to do 3000x3000, for example, the game lags really badly. I’m wondering if this is because I’m generating all of the tiles at once and storing them in memory? If so, how would I go about generating only some at a time?

By the way, here is the paint method and how they’re being drawn:

/* Our render method. */
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D)g;
		
		int xOffset = (int)player.Coordinates.x - (GAME_WIDTH / 2);
		int yOffset = (int)player.Coordinates.y - (GAME_HEIGHT / 2);
		
		this.cameraView = new Rectangle(xOffset, yOffset, GAME_WIDTH, GAME_HEIGHT);
		
		g2d.translate(camera.getX(), camera.getY());
		this.player.render(g);
		
		for (int x = 0; x < level.Tiles.length; x++) {
			for (int y = 0; y < level.Tiles[0].length; y++) {
				if (cameraView.contains(x*32, y*32)) { // I thought this would fix the problem, if I were to render only what was in view but it didn't fix it.
					level.Tiles[x][y].render(g);
				}
			}
		}
		
		g2d.translate(-camera.getX(), -camera.getY());
	}

Any help?

Generating the entire map at once isn’t all that bad. It’s a one-time thing, it’s not like you’re doing it every frame… right…?

3000x3000 is a lot. That’s 9 million tiles. You’re iterating through all of them each frame. What you should be doing is setting the bounds on the for loops so you’re only iterating through the general area you’re rendering. You can keep the intersects method, but I also advise you to not re-create a new rectangle object each frame and instead just modify the existing one.

Hm, I’ll mess around with some things, thanks :slight_smile:

Assuming your tiles are of an equal size and don’t move I’d suggest you use the indices in the array to keep its position - this means you don’t need to allocate thousands of Rectangles in memory.
You could, as chrislo27 suggested, store indices for when you loop through the map, and check if points are onscreen somewhat like this:


boolean pointOnScreen(int x, int y, int w, int h){
return (x + camera.getX() >= 0 &&
x + camera.getX() <= (width of game window) &&
y + camera.getY() >= 0 &&
y + camera.getY() <= (height of game window);
}

You’d use the above somewhat like this (assuming what I’ve said above is correct):

if (pointOnScreen(x * (tile width), y * (tile width), (tile width), (tile width)){
//call render
}

I use a method similar to this in games I’ve written in the past, and I can’t remember it exactly off my head because it takes width and height into account. That should be enough to get you going though.

Just for some sort of reference - my tile maps can theoretically support tens of millions of tiles (I’ve tried it with 64 * 64 * 10000) and frame rates aren’t heavily impacted, all because the render loop is “blind” to these extra tiles, although if your maps are getting that large it’s unreasonable to keep them all cached because memory usage climbs into the gigabytes.

Hm, a lot of good points being made. You are correct in assuming my tiles are of all equal size and don’t currently move. I’ll mess around with this when I get the time, as well. Thank you :slight_smile:

I would advise the use of HashMaps here, and an object that stores an X and Y position that implements a hashcode. This way, you can store all of your tile information based on the x/y position object, and look it up instantly, rather than iterating through everything.

Here is an example: Using a Point for the position of a tile…



HashMap<Point,String> TileDataHashMap = new HashMap<Point,String>();

ClassConstructor(){

   int Width = 10;
   int height = 10;
   
   for(int x = 0; x < Width; x++){
   for(int y = 0; y < Height; y++){

      TileDataHashMap.put(new Point(x,y), "tile:info");

   }
   }

}


This is great for procedural generation, because you can keep track of what has/hasn’t been generated yet.
This works well in the same sense, for path finding, because you can go through and find which tiles are solid or not by holding that info in another hashmap. Easy.

I’ve found that hashmaps are soooo useful that you can cut out cpu use by ALOT, and I mean ALOT. For example, if you upkeep all of your tile groups that are separated by solid tiles and confined, you update that group when a tile is no longer solid, therefore making pathfinding even more efficient when you already know which tiles to iterate through and have no excess…

I came to a point where I started using special HashMap helper classes for these sorts of things…

http://pastebin.com/Z6uuvLKL Basic HashMap rules, store 1 value per Key. Has methods to lookup which keys contain a specified value.

http://pastebin.com/6hfXEvqF for storing multiple values per key, and being able to look up what keys hold the same value.

Back to your main question:

Get your screen area.

Iterate over it with an x/y for loop, offset by the screen coordinates.



public void render(){
   int camIndexOffsetX = (camX/TILE_SIZE);
   int camIndexOffsetY = (camY/TILE_SIZE);
   Rectangle screen = new Rectangle(camIndexOffsetX, camIndexOffsetY, camWidth/TILE_SIZE, camHeight/TILE_SIZE);

   for(int x = 0; x < screen.width; x++){
   for(int y = 0; y < screen.height; y++){
        TileArray[x+camIndexOffsetX][y+camIndexOffsetY].render();
   }
   }


}


This draws exactly what is in your screen based on the offsets of the screen/camera.
You might have to start x and y at -1, and add 1 to width and height, so that it encapsulates everything intersecting with your screen, rather than starting 1 tile from the offset (You would see a blank column to the left and top).

@micecd, Thank you, so much for the suggestions and pointers :smiley: I’ve been struggling with areas such as those, trying to find a more efficient way to do it all; so I thank you. I’ll attempt to implement some of these ideas and do some research and reading on HashMaps ^^