Search code examples
javarenderlag

Check if Rendering Off Screen?


Recently, I've been trying to improve some lag issues in my game, and one way I'm doing that is my removing any unnecessary rendering.

This is the render() method in my TileMap class, which handles creating, updating, and rendering game maps.

public void render(Graphics2D g) {

    for(int row = 0; row < numRows; row++) {

        for(int col = 0; col < numCols; col++) {

            if(map[row][col] == 0) continue;

            int rc = map[row][col];
            int r = rc / numTilesAcross;
            int c = rc % numTilesAcross;

            g.drawImage(tiles[r][c].getImage(), x + (col * tileSize) - 8, y + (row * tileSize) - 8, null);

        }

    }

}

I've been trying things like this:

if(x + (col * tileSize) - 8 < x + (col * tileSize) - 8 + Panel.WIDTH) continue;

With Panel.WIDTH being the width of the window. I'm not really sure what algorithms are needed to test for out of bounds rendering.

To check if the tiles were off the left of the screen, but that didn't work.

I also think looping through all the rows and columns might be laggy, and I want to change it so it only loops though the amount of tiles that can be rendered on the screen.


Solution

  • Creating some temporary variables would help make that more understandable:

    int pixelX = x + (col * tileSize) - 8;
    int pixelY = y + (row * tileSize) - 8;
    

    Using these, the check you suggested is equivalent to this:

    if(pixelX < pixelX + Panel.WIDTH) continue;
    

    which obviously isn't going to skip anything.

    You want something like this:

    if(pixelX + tilesize <= 0) continue; // tile is off left side of screen
    if(pixelY + tilesize <= 0) continue; // tile is off top of screen
    if(pixelX >= Panel.WIDTH) continue; // tile is off right side of screen
    if(pixelY >= Panel.HEIGHT) continue; // tile is off bottom of screen
    

    Assuming Panel.WIDTH and Panel.HEIGHT are the width and height of the thing you're drawing on. This is a collision check for axis-aligned bounding boxes, in case you need a name to search for.

    This is still not the most efficient way - you end up looping over the entire map, but then ignore most of the tiles. A more efficient way is to calculate the part of the map that you can see, and then draw those tiles:

    // assuming x <= 8 and y <= 8
    int firstCol = ((8-x) / tileSize);
    int firstRow = ((8-y) / tileSize);
    int lastCol = firstCol + ((Panel.WIDTH + tileSize - 1) / tileSize); // Panel.WIDTH/tileSize, but rounding up
    int lastRow = firstRow + ((Panel.HEIGHT + tileSize - 1) / tileSize);
    
    for(int row = lastRow; row <= firstRow; row++) {
        for(int col = lastCol; col <= firstCol; col++) {
            // drawing code goes here
            // there's no need to test the tile is onscreen inside the loop
        }
    }