# Efficient way to render tile-based map in Java

Some time ago I posted here because I was having some memory issues with a game I'm working on. That has been pretty much solved thanks to some suggestions here, so I decided to come back with another problem I'm having.

Basically, I feel that too much of the CPU is being used when rendering the map. I have a Core i5-2500 processor and when running the game, the CPU usage is about 35% - and I can't accept that that's just how it has to be.

This is how I'm going about rendering the map:

• I have the X and Y coordinates of the player, so I'm not drawing the whole map, just the visible portion of it;
• The number of visible tiles on screen varies according to the resolution chosen by the player (the CPU usage is 35% here when playing at a resolution of 1440x900);
• If the tile is "empty", I just skip drawing it (this didn't visibly lower the CPU usage, but reduced the drawing time in about 20ms);
• The map is composed of 5 layers - for more details;
• The tiles are 32x32 pixels;

And just to be on the safe side, I'll post the code for drawing the game here, although it's as messy and unreadable as it can be T_T (I'll try to make it a little readable)

private void drawGame(Graphics2D g2d){
//Width and Height of the visible portion of the map (not of the screen)
int visionWidht = visibleCols * TILE_SIZE;
int visionHeight = visibleRows * TILE_SIZE;

//Since the map can be smaller than the screen, I center it just to be sure
int xAdjust = (getWidth() - visionWidht) / 2;
int yAdjust = (getHeight() - visionHeight) / 2;

//This "deducedX" thing is to move the map a few pixels horizontally, since the player moves by pixels and not full tiles
int playerDrawX = listOfCharacters.get(0).getX();
int deducedX = 0;
if (listOfCharacters.get(0).currentCol() - visibleCols / 2 >= 0) {
playerDrawX = visibleCols / 2 * TILE_SIZE;
map_draw_col = listOfCharacters.get(0).currentCol() - visibleCols
/ 2;

deducedX = listOfCharacters.get(0).getXCol();
}

//"deducedY" is the same deal as "deducedX", but vertically
int playerDrawY = listOfCharacters.get(0).getY();
int deducedY = 0;
if (listOfCharacters.get(0).currentRow() - visibleRows / 2 >= 0) {
playerDrawY = visibleRows / 2 * TILE_SIZE;
map_draw_row = listOfCharacters.get(0).currentRow() - visibleRows
/ 2;

deducedY = listOfCharacters.get(0).getYRow();
}

int max_cols = visibleCols + map_draw_col;
if (max_cols >= map.getCols()) {
max_cols = map.getCols() - 1;
deducedX = 0;
map_draw_col = max_cols - visibleCols + 1;
playerDrawX = listOfCharacters.get(0).getX() - map_draw_col
* TILE_SIZE;
}

int max_rows = visibleRows + map_draw_row;
if (max_rows >= map.getRows()) {
max_rows = map.getRows() - 1;
deducedY = 0;
map_draw_row = max_rows - visibleRows + 1;
playerDrawY = listOfCharacters.get(0).getY() - map_draw_row
* TILE_SIZE;
}

//map_draw_row and map_draw_col representes the coordinate of the upper left tile on the screen

//iterate through all the tiles on screen and draw them - this is what consumes most of the CPU
for (int col = map_draw_col; col <= max_cols; col++) {
for (int row = map_draw_row; row <= max_rows; row++) {
Tile[] tiles = map.getTiles(col, row);
for(int layer = 0; layer < tiles.length; layer++){
Tile currentTile = tiles[layer];
boolean shouldDraw = true;

//I only draw the tile if it exists and is not empty (id=-1)
if(currentTile != null && currentTile.getId() >= 0){
//The layers above 1 can be draw behing or infront of the player according to where it's standing
if(layer > 1 && currentTile.getId() >= 0){
if(playerBehind(col, row, layer, listOfCharacters.get(0))){
//the tiles that are infront of the player wont be draw right now
shouldDraw = false;
}
}
if(shouldDraw){
g2d.drawImage(
tiles[layer].getImage(),
null);
}
}
}
}
}
}


There's some more code in this method but nothing relevant to this question.

Basically, the biggest problem is that I iterate over around 5000 tiles (in this specific resolution) 60 times each second.

I thought about rendering the visible portion of the map once and storing it into a BufferedImage and when the player moved move the whole image the same amount but to the opposite side and then drawn the tiles that appeared on the screen, but if I do it like that, I wont be able to have animated tiles (at least I think).

That being said, any suggestions?

• Are you actually running into performance issues or are just worried you might? – MichaelHouse Aug 30 '12 at 23:03
• @Byte56 I'm worried I might in computers slower than mine – Lucius Aug 30 '12 at 23:46
• There are plenty of resources on this site about efficient rendering. Given the specifics of this question, it's unlikely anyone would run into this same problem. In my opinion, this question is too localized and a duplicate. – MichaelHouse Aug 31 '12 at 14:30
• @Byte56 I did some research but wasn't able to find this specific topic you posted amid all the results I got, but there was no clear answer to my specific problem there. Even so, if this is a duplicate, I'm sorry. – Lucius Sep 1 '12 at 17:16

I'm afraid my answer is to reply with a question: What is slow?

• Is the rendering itself slow (g2d.drawImage)?
• Is playerBehind slow?
• Is iterating over the tiles slow?

Use a profiler like YourKit or (quicker & easier) snip sections of code and re-run.

BTW using "35% of CPU" isn't a useful statistic, especially. Stick to milliseconds so that you can compare optimisations accurately.

• It's not really slow - I have a solid 60fps which is all I need - it's just using too much CPU compared to other games I have here. I do know that if I remove the g2d.drawImage the CPU usage here falls from 35% to 15% which is what I would like to have all the time. I haven't had a chance to test this on slower computers - my friend and partner in this project have a computer with the same processor as mine - but I do believe that the frames may drop. I'll look into this YourKit you talk about and see if I can use it - never had too much luck with profiling tools. – Lucius Aug 31 '12 at 12:29
• You are probably right to flag it as "high" and somewhat of a concern. Ultimately the answer will probably be to use a proper graphics API that will alow you to use sprite sheets/texture atlassing, but if this is still at the prototyping stage I would concentrate on the game itself. Once you've got that fun you can then decide if it is "broke" and needs a "fix". – Jason Chown Aug 31 '12 at 13:17
• Asking questions goes in the comments, not as an answer. – MichaelHouse Aug 31 '12 at 14:26
• I can't say this project is near it's end, but it MUST be complete in about 3 months because it's a school project and can't be delayed at all. But I installed YourKit and I can't say how thankfull I am to you. I said that the problem probably was at g2d.drawImage but I wasn't sure exactly why. Turns out that each call of tile.getImage() would go through a huge tileset and cut the part corresponding to the tile. By simply adding a HashMap containing the tiles that were previously draw, the CPU usage now is about 17%. I just hope this wont use too much memory - probably not. Thanks! – Lucius Aug 31 '12 at 15:01
• @Byte56: I agreed, but how do you do that? There's no "Add Comment" in the question panel. As it happens, my answer was the answer they needed. – Jason Chown Aug 31 '12 at 15:19

You've got an idea to go on with rendering to a single image. If every tile has to be animated, it may not be the best idea, but with just a few you could have large areas using a single in-memory image.

Another way to get some frames is to only render the top tile (which you may already be doing).

Perhaps you could break up tiles into larger sections or "chunks", and only check the ones in chunks that are visible to the player.

I certainly hope you're already drawing tiles of the same type all at once, there's a huge amount of performance to be gained in sprite batching.

EDIT Only draw changes to the screen, for instance only when the player moves over a tile.

• By "only render the top tile" do you mean the top layer? If so, then that's not really an option because some tiles are not fully opaque and show the tiles below. About this "sprite batching" I'm not doing it and it's actually the first time I've ever heard of it. I'm going to look into it and see if I can use it, but I'm not really sure it will work since if I were to draw all the tiles of the same type at once I would have to loop through the same layer a couple of times and not just once. – Lucius Aug 30 '12 at 23:49