Strategies for Handling Large Tile Maps?

I’m trying to get into java game development and I’ve gotten as far as a small very basic top-down tile-based game.

I have my tiles in a chunk-type format. To update the tiles (only drawing them right now) I loop through the chunks, loop through all tiles in the each chunk and draw them according to the chunks position based off of the chunk offset according to the player’s position and the position of the tile itself within the context of the chunk.

This works fine until I have a large number of tiles. If I have a map of 2x2 chunks, with 50x50 tiles in each chunk, everything runs fast enough. Whenever I have a map of 2x2 chunks with 100x100 tiles in each chunk, the program runs significantly slower.

I am currently only drawing chunks that have tiles displayed on the map, but I am still looping through tiles to determine what needs to be drawn even if I’m not drawing them - I’m thinking about determining the edge tile positions based on the chunk position, and evaluating whether to draw the chunk that way, but it seems this would still limit the total number of chunks that could be available in the map before affecting performance.

I’m looking for some high-level strategies I can research further, tutorials, or any advice on how to solve this problem.

I am very much a noob at game development, and I know this is probably something basic that most people know how to work around.

Any help is appreciated, any ideas or pointers?

Only draw tiles that are on screen.

instead of looping through your map and drawing its tiles, think instead about looping through each position on the screen where a tile can be drawn and then find out which tile to draw there.

What code are you running against each tile to check it’s on screen? Iterating through 10,000 tiles per frame isn’t much at all, so long as it’s a simple test.

Find a balance between chuck size and performance and only draw the chunks which are on the screen. You could go further and also only draw each tile that is not on the screen but when there is nothing in a chunk, make sure the chunk itself I checking whether or not if any of its tiles are on the screen instead of looping through the whole lust when nothing should be showing up anyway. So:

  1. Loop through chunks and if chunk(s) area is on screen start looping through blocks inside chunk(s) else disable chunk rendering
  2. Check if blocks within chunk(s) are on screen and if so, draw them
  3. Repeat

This should help you. I think this is similar to how minecraft renders chunks but I’m not sure.

Maybe this is where my problem lies, rather than drawing only tiles that are on the screen, I’m drawing all tiles of any chunk on the screen - so if the player stands in the middle of where 4 chunks 100x100-sized chunks meet, I’m still drawing 40,000 tiles.

Anyway, here’s the code

    if(gameManager.chunkManager != null) {
			for(int i = 0; i < gameManager.chunkManager.chunks.size(); i++) {
				for(int j = 0; j < gameManager.chunkManager.chunks.get(i).size(); j++) {
					if(gameManager.chunkManager.chunks.get(i).get(j).onScreen()) {
						gameManager.chunkManager.chunks.get(i).get(j).draw(g);
					}
				}
			}
		}

The chunks variable above is a 2-dimensional ArrayList of chunks (I’m using the position in the ArrayList as x/y axes.

Here is the code I’m using to determine what to draw -

//see if chunk parameter needs to be drawn or not
	public void setOnScreen(Chunk curChunk) {
		boolean onScreen = true;
		ArrayList<ArrayList<Tile>> tiles = curChunk.tiles;
		//biggest tile value in array list for each axis
		int xMax = tiles.size() - 1;
		int yMax = tiles.get(0).size() - 1;
		//northern tile - original position
		Tile northernTile = tiles.get(0).get(0);
		//southern tile - go all the way right, then all the way down
		Tile southernTile = tiles.get(xMax).get(yMax);
		//western tile - go all the way down
		Tile westernTile = tiles.get(0).get(yMax);
		//eastern tile - go all the way right
		Tile easternTile = tiles.get(xMax).get(0);
		
		int tileWidth = northernTile.width;
		int tileHeight = northernTile.width;
		
		//run through tile corners.. 
		//if bottom of screen y val < northernTile y val -> onscreen is false
		//if top of screen y val > southernTile y val + tileHeight -> onscreen = false
		//if right of screen x val < westernTile x val -> onscreen = false
		//if left of screen x val > eastern tile-> onscreen = false
		if(northernTile.pixelY + northernTile.parentChunk.getYOffset() >  (camera.pixelY + (screenHeight/2))) {
			onScreen = false;
		}
		else if((southernTile.pixelY + southernTile.parentChunk.getYOffset() + tileHeight) < camera.pixelY - (screenHeight/2)) {
			onScreen = false;
		}
		else if(westernTile.pixelX + westernTile.parentChunk.getXOffset() > screenWidth) {
			onScreen = false;
		}
		else if((easternTile.pixelX + easternTile.parentChunk.getXOffset() + tileWidth) < 0) {
			onScreen = false;
		}
		curChunk.setOnScreen(onScreen);
		//System.out.println("pixelX: " + (northernTile.pixelX + northernTile.parentChunk.getXOffset()));
		//System.out.println("pixelY: " + (northernTile.pixelY + northernTile.parentChunk.getYOffset()));
	}

This method still doesn’t work perfectly, but at the moment I’m more focused on trying to get a manageable performace.

Like trollwarrior1 says, don’t iterate through all your tiles checking if they are visible. Instead you can directly calculate what needs to be drawn based on where your player is in the world.

For example, let’s say your player is at X: 30, Y: 30, and your window can fit 10 tiles in both the X and Y axis.

Using that information, we know we only need to iterate from 25 to 35 in your tiles array, in both directions. You can apply the same method to chunks since they are essentially just huge tiles. Instead of iterating 40,000 times, you are now only iterating 100 times, and without the expensive checks. :slight_smile: