What tiles are currently visible in a viewport

I’ve been wrestling with this for a while, and it’s really boilerplate code. I found myself writing weird cases for different scenarios with if-statements and it was just a mess, when it really should only be one line of code. I hope one of you can help me, and I think a lot of you have this code lying around somewhere.

What I need is a way to calculate the first visible tile, and the last visible tile on both axises. This is for optimising rendering of my tiled map. I am currently drawing all tiles in the map, when in reality only about 140 can be visible at any given time.
The variables I have access to is the viewport dimensions, tile dimensions and the offset of viewport on the map.

I thank you very much for being such awesome individuals. :slight_smile:

Well, if you know the width and height of the screen, you can figure out how many tiles on each axis you need.

And if you know the (x,y) position of the player(assuming he’s always in the middle), then you can calculate which tile he should be on.

From this you know you have to render all tiles from player_x_tile - x_tiles/2 to player_x_tile + x_tiles/2 in width and it’s the same for height.

Though you’ll also need to throw in a buffer zone, or else you’ll be able to see edges without any tiles rendered.

EDIT:
Actual code I use for this thing.


// Tiles on the screen
int tilesAcross = (int) Math.ceil(this.me.getWidth() / MapEditor.TILE_SIZE) + 3;
int tilesDown = (int) Math.ceil(this.me.getHeight() / MapEditor.TILE_SIZE) + 3;

// Tile the player is on
int ptx = this.me.player.x / MapEditor.TILE_SIZE;
int pty = this.me.player.y / MapEditor.TILE_SIZE;

// The offset of the tile
int xOffset = this.me.player.x % MapEditor.TILE_SIZE;
int yOffset = this.me.player.y % MapEditor.TILE_SIZE;

Note the +3 in tilesAcross and tilesDown. That’s the buffer to make sure it’ll never show any black edges. :slight_smile:

I sincerely salute you for your post. It’s awesome. I’m serious. However, there’s one flaw:
I don’t have a centered actor in this game :frowning:

I have very similar code to yours in other prototypes - I think yours is neater though. I’ll definitely keep it. :slight_smile:

Instead of a centralized player in my game, I have the offset at which the map is drawn. This can be both negative and positive, and that makes it hard for me.

Sounds like you can just translate that offset to a position on the map, and then do the same thing. :slight_smile:

I’m not sure why you’d have negative values in the offset though. I’d always just keep the top left point of the map as (0, 0). :slight_smile:

Negative offset is when the top-left corner of the map goes off-screen. On a large map, this will happen every time a map bigger than the viewport is displayed.

Hm, I’m not sure how you handle that.

When my player is located in the top left, he’s at (0, 0) and when he moves right it’s in a positive direction, same goes for down. This way my location range just goes from x = [0…width_in_pixels], y = [0…height_in_pixels] (width/height being that of the map).

Here’s my complete render code, and as you can see, then the “player” is actually just an offset in this case. So if I’d want my player to be able to move independent of the viewport, I’d just keep track of him and my offset, both in the same way as I explained above. :slight_smile:


public void render(Graphics2D g) {
	// Tiles on the screen
	int tilesAcross = (int) Math.ceil(this.me.getWidth() / MapEditor.TILE_SIZE) + 3;
	int tilesDown = (int) Math.ceil(this.me.getHeight() / MapEditor.TILE_SIZE) + 3;

	// Tile the player is on
	int ptx = this.me.player.x / MapEditor.TILE_SIZE;
	int pty = this.me.player.y / MapEditor.TILE_SIZE;

	// The offset of the tile
	int xOffset = this.me.player.x % MapEditor.TILE_SIZE;
	int yOffset = this.me.player.y % MapEditor.TILE_SIZE;

	int tilesRendered = 0;

	for (int x = 0; x < tilesAcross; x++) {
		for (int y = 0; y < tilesDown; y++) {
			tilesRendered++;

			int tx = ptx + x - tilesAcross / 2;
			int ty = pty + y - tilesDown / 2;

			int rx = x * MapEditor.TILE_SIZE - (MapEditor.TILE_SIZE + xOffset) - 16;
			int ry = y * MapEditor.TILE_SIZE - (MapEditor.TILE_SIZE + yOffset) - 10;

			if (tx < 0 || tx >= this.me.map.width || ty < 0 || ty >= this.me.map.height) {
				g.setColor(Color.BLACK);
				g.fillRect(rx, ry, MapEditor.TILE_SIZE, MapEditor.TILE_SIZE);
				continue;
			}

			Tile currentTile = this.me.map.getTile(tx, ty);
			g.drawImage(currentTile.getImage(), rx, ry, null);

			drawCorners(rx, ry, currentTile, g);
		}
	}

	// Paint the player
	g.setColor(Color.black);
	g.drawLine(this.me.getWidth() / 2, this.me.getHeight() / 2 - 3, this.me.getWidth() / 2, this.me.getHeight() / 2 + 3);
	g.drawLine(this.me.getWidth() / 2 - 3, this.me.getHeight() / 2, this.me.getWidth() / 2 + 3, this.me.getHeight() / 2);
}

(this.me is a reference to the MapEditor object)

Here’s a screenshot of my Map Editor:

The little x in the middle of the water is the “players” location

The map is 100x100 tiles, so 3200x3200 pixels. :slight_smile:

As you can see, there’s no negative offsets. :slight_smile:

I hope this helps a little