# Rendering tiles on 3/4 perspective

Using a 3/4th perspective, I'm trying to create a way to render cliffs where nothing is overlapped, and it creates an accurate representation of the elevation. This is a rough mockup of how it might look in-game:

The way the tile data is organized is by layer. Each elevation has a layer class which holds all of the ground data for that specific layer, and all unoccupied space is simply null in the array. I can't seem to come up with a way to render the data so it would look something like this. I've thought about rendering from highest to lowest elevation, the other way around, etc. All entailed a situation where something is getting overlapped that shouldn't be.

What's the best way to do this?

• My guess is that older games placed the tiles manually on the X,Y plane and flagged them as visible or not to get the affect you're looking for. – Tetrad Sep 23 '11 at 23:46
• Draw the top rows [on the screen] first, and build up from the ground, overwriting the higher rows as you draw those tiles (you'll have to traverse the upper layers as you do this, but that should be easy as the X and Y will remain unchanged), and do so until all rows are completely drawn for you. You can even loop beyond the bottom and just draw other things that are higher up even though their corresponding ground tiles are off the screen. +1 for an interesting question, and for including some very nice pixel art! – Randolf Richardson Sep 24 '11 at 0:09
• @Tetrad I would do that, but the terrain will be procedurally generated, and I would much rather the solution for this be in the rendering than in the terrain generation code. – Patrick Moriarty Sep 24 '11 at 0:09
• @RandolfRichardson you should post that as an answer instead of a comment on the question, so we can upvote it. ;) – Nathan Reed Sep 24 '11 at 0:45
• @Nathan Reed: Done. Thanks! I also added another idea about using alpha-blending to that answer (which I think solves a visibility problem that Patrick's players could encounter). – Randolf Richardson Sep 24 '11 at 16:10

I suggest that you draw the top rows [on the screen] first, and build up from the ground, overwriting the higher rows as you draw those tiles (you'll have to traverse the upper layers as you do this, but that should be easy as the X and Y will remain unchanged), and do so until all rows are completely drawn for you.

You can even loop beyond the bottom and just draw other things that are higher up even though their corresponding ground tiles are off the screen (creating a sense of even more depth for your scenes as your players navigate in any direction).

Additionally, if alpha-blending (transparency) mode is support, and if your character is always positioned in the centre of the screen (e.g., because the background scrolls when the player's character moves), you could also use transparency effects (with alpha-blending) when drawing the tiles that would block the view of the character (so the player can see what's going on), and gradually reduce this transparency effect with each tile that is further away from the character (80% transparency for the blocking tile, 60% for the next ones, 40% for the ones outside of that, then 20% outside of that, and finally 0% for all the rest).

I don't know if it's important to how your game works to actually make the tile have depth information or just APPEAR to have depth. If appearing to have depth is ok, then here's the way I addressed this in my tile engine:

Each map has a two-dimensional array of MapCell objects. A MapCell can be thought of as a "tile slot" in that it represents an area on the map where a tile would be drawn.

Now, each MapCell has two (it could be more, but I only use two) MapCellLayer objects. The first MapCellLayer object is always defined, but the second one is optional (i.e. often == null).

A MapCellLayer can have up to five Tile objects. Tile objects represent the actual tiles being drawn to the screen and hold a reference to the tile index from the tilesheet. The reason there are five Tile objects is because of how they're drawn and used. They're set up like this:

1. Ground - This is the base tile, it's drawn on the screen first and appears below all other tiles in the MapCell and the players.
2. Mask 1 - This tile is drawn second (if it exists) and provides a way to add tiles with alpha transparency over top of the ground tile. It's drawn below the player.
3. Mask 2 - This is exactly the same as the Mask 1 tile except it's drawn after to allow for even more control of the map by overlaying more tiles.
4. Fringe 1 - This tile is drawn after Mask 2 and is effectively the same as Mask 1 with the only exception being that it is drawn OVER the player. Players get drawn between Mask 2 and Fringe 1.
5. Fringe 2 - You may have guessed it already, but this is the same as Fringe 1, but drawn over it.

My draw call works like this:

Loop through all the MapCells within the camera's view.
Loop through all MapCellLayers for the current MapCell
If the layer exists
Draw the Ground tile if it exists
Draw the Mask 1 tile if it exists
Draw the Mask 2 tile if it exists
Draw the Player on the tile if any
Draw the Fringe 1 tile if it exists
Draw the Fringe 2 tile if it exists
End if layer exists
End MapCellLayer loop
End MapCell loop

A few notes:

• If you you build your maps with this system, you can create some advanced looking terrain features with the right tileset. See my example pictures below.
• The point of the MapCellLayer is so that I can have areas where two players occupy the same cell but one is above another one such as on bridges or inside houses (i.e. first and second floor, although inside houses I only draw the MapCellLayer that the player is on).
• I don't actually draw the players in the tile loop, I just wanted to make it simple. I actually make use of SpriteSortMode.FrontToBack so that each layer has it's own values and gets drawn in the correct order. The player/npc loop is then done separately. I recommend this method especially if your players/npcs are larger than a single tile.

Here's some example pictures from one of the maps in my game. I've taken screenshots working up from drawing only Ground tiles to all tiles up to Fringe 2. The player is stationary.

Ground

+ Mask 1 (Note the use of transparency on the bushes and tree roots.)

+ Fringe 1 (Note that the player is being drawn behind the tree.)

+ Fringe 2 (Note how the branches of the pink tree on the right now overlap the ones from the tree on the left.)

I hope this helps you. Let me know if you have any questions.

• Great answer (+1), and I really like those screen shots (the trees look great {I particularly found it interesting that the big tree trunk on the left initially looked like a star fish in the second and third screen shots} and the river is very wide unlike a lot of games where people often seem to limit themselves to one or two tiles wide). – Randolf Richardson Sep 26 '11 at 15:42
• @RandolfRichardson Thanks! I'm quite happy with the mapping in the game. I didn't do it myself (I helped though!), just gave a general overview of the world. :) – Richard Marskell - Drackir Sep 26 '11 at 15:49
• Neat! What's the web site address for that game (assuming there is one)? – Randolf Richardson Sep 26 '11 at 16:26
• @RandolfRichardson Fondusis.com I'm actually in the process of rewriting it in XNA and C# (it was formerly a VB6 app). The site is a little out of date showing the old version, but I've been keeping a log of my work on the new client (it's an online RPG) on my blog. The screens above are from the new map editor. – Richard Marskell - Drackir Sep 26 '11 at 17:15

I would calculate the visible portion when rendering each block so that you only ever render each screen block once.

I would implement it something like this:

TileImage GetTileImage(int screenx, int screeny)
{
for(int height = layers.count - 1; height >= 0; height--)
{
//translate height down the screen to get the correct block
TileImage image = layers[height].GetTile(screenx,screeny-height);
if(image != NULL)
{
return image;
}
}
//If we got here no tile makes up this screen tile, handle accordingly
}


So what this code does, is start off at the max height layer, and looks for a tile at X: ScreenX Y: ScreenY - Height

As the block that shows at screen position 5,5 at height 3 is actually at word position 5,2

So the loop will search from the top height until it finds a block for that screen coord.

Hope this makes sense.

So in your rendering loop you would loop over x and y in screen coords and call GetTileImage() once per image square.