Rendering only visible isometric tiles

Hi all, my question is about how I would best go about rendering a scene of isometric tiles, while not attempting to render tiles outside of the viewport.

What I currently have is a 15x15 array of tiles, that I’m using nested for loops to cycle through. I’m transforming the position of each tile using the formula below and rendering them to the screen:

int isoX = (x - y) / 2;
int isoY = x + y;

display.drawImage(isoX * TILEWIDTH, isoY * (TILEHEIGHT >> 1), TILEWIDTH, TILEHEIGHT);

This renders as expected as a diamond shape, with +X leading to the bottom right and +Y leading to the bottom left. This is illustrated by the image below:

This issue is that I’m completely stumped about how I would go about rendering a square of tiles, with the ability to clip tiles that aren’t within the viewport. Notice the large black areas in the image above.

I like the idea of rendering a diamond shaped map, although ideally, rather than not rendering a tile at all, I’d like to loop over (somehow) the area that this tile would occupy and set it to render a default or “edge of map” tile, such as a boundary defining tile like impassable trees.

If this is possible, then I’m hoping that it wouldn’t be that greater stretch to use this information to then decide which tiles lie outside of the viewport.

I’ve looked through a few tutorials, although my understanding of this area is very sketchy. Would anyone mind walking me through how I could achieve these goals logically? :slight_smile:

Test intersection of tiles and the screen bounding rectangle.
Either by projecting the tiles to find where they will be rendered (if that location is outside of the viewport, don’t render) or you can do the reverse and map the screen boundary rectangle (or one slightly larger) to world space via Camera.unproject (or reverse of the isometric transform), can depending on your datastructure can then loop over only the tiles you know will be in that rectangle.

Cheers for the quick reply! Mapping the isometric coordinates from the screen coordinates makes far more sense than what I was doing. :o I’ll have to play around with that idea and see if I can work out how it’d work.

Right, that seems to work well. :slight_smile: There’s just one small glitch when I introduce a “camera” to offset the scene with. It seems that the Y component is incorrect on every odd X component, as you can see in screenshot 2 (that’s when I move 32 pixels right). Can you spot what I’m doing wrong?

The blue tile signifies the (0, 0) tile coordinate that I’ve just set as a landmark.

Green tiles are the tiles within the map and brown tiles are default “out-of-bounds” tiles that are rendered when scanning over a position outside the map boundaries.

Image 1 is your default starting position and Image 2 is when I move 32 pixels to the right.

Main render method:


Render: function() {
	this.Display.Clear("#000000");

	var intTileWidth = 64;
	var intTileHeight = 32;

	var intMapWidth = 15;
	var intMapHeight = 15;

	var intHorizontalTiles = Math.ceil(this.Display.Viewport.intWidth / intTileWidth) << 1;
	var intVerticalTiles = Math.ceil(this.Display.Viewport.intHeight / intTileHeight);

	var intTileStartX = Math.round(this.Display.Viewport.Camera.intPositionX / intTileWidth);
	var intTileStartY = Math.round(this.Display.Viewport.Camera.intPositionY / intTileHeight);

	for(var y = -1; y < intVerticalTiles; y++) {
		for(var x = -1; x < intHorizontalTiles; x++) {
			var bIsEven = !(x % 2 == 0);
			var intTranslatedX = x - intTileStartX;
			var intTranslatedY = y - intTileStartY;
			var intIsoX = Math.round(intTranslatedX / 2 + intTranslatedY);
			var intIsoY = Math.round(intTranslatedY - intTranslatedX / 2);

			var intSpriteX = 0;
			var intSpriteY = 0;

			if(intIsoX < 0 || intIsoX >= intMapWidth || intIsoY < 0 || intIsoY >= intMapHeight) {
				intSpriteX = 64;
			}
			if(x==y&&x==0) intSpriteX = 128;
			this.Display.DrawImageToViewport(intSpriteX, intSpriteY, 64, 32, x * (intTileWidth >> 1), y * intTileHeight + (bIsEven ? (intTileHeight >> 1) : 0), 64, 32)
		}
	}
}

Sorry for the javascript. :x I’d appreciate any help as the logic should be the same.

Right, I eventually realised where I was going wrong. :slight_smile: It wasn’t that interesting problem in the end - just swapped the order of a few operations. Got it working now - works great!

If anyone’s interested, here’s what I have at the moment. I suppose javascript’s better than nothing: http://www.java-gaming.org/?action=pastebin&id=1204

Thanks for the help. :smiley: