Whew... it's taken seven articles to get this far, but by now, we've all got a good idea of how to fight the beast known as Windows, and how DirectDraw works. We're ready to start putting a game together! In this article I'll show you how to construct a basic tile engine with DirectDraw. We'll go over the different ways to design such an engine, and then take a look at how to implement scrolling and tile animations. I'll be focusing on RPGs as we go along in the series, but you can certainly use a tile engine for other things as well, like strategy or puzzle games.
If you're reading this, I assume that you have a working knowledge of basic Windows programming and DirectDraw, using the C or C++ programming language. Just about everything I went through in the previous seven articles will be coming together today, so make sure you're up to speed, fire up Visual C++, and let's get going!
Before you jump in and start coding, you have to have some idea of what it is you're trying to create. There are any number of variables you have to consider when designing a tile engine, like tile size and shape, tile animations, number of layers, map sizes, and so on. As I pointed out more than once last time, you don't want to just start writing code without having all this stuff planned out. So before we get into the actual development of the engine, let's briefly consider a few of these variables.
Tile size and shape: Here you have a few options. There are three common choices for tiles: squares, hexagons, and diamonds. Square tiles are good for RPGs since the arrow keys or directional buttons on a gamepad normally provide four major directions of movement. Conversely, hexagons are generally not good for the type of game where the player will be actively controlling the character's movement... unless you've got some kind of strange keyboard with six arrow keys on it. Hexagons are usually a good choice for games where most of the movement will be done with some sort of pathfinding, as in many realtime strategy games. The diamond-shaped tiles make for a game that looks like it's using square tiles, but with the viewpoint rotated 45 degrees. It all depends on what kind of game you're going for. Normally when someone brings up tile-based games, I tend to think of the good old days of the Genesis and Super Nintendo, of games like Final Fantasy VI. So for the example in this article, we'll go that route and use square tiles.
Tile size is something you want to choose based on how much of the world around the character you want to be able to see, and what resolution you'll be using. For a resolution of 640x480, I've found that making square tiles at 32x32 works well. So that's what we'll do.
Tile animations: The next thing to consider is if you want to support animated tiles, and if so, what kind of restrictions you want to place on the animations so that they look nice, but don't become too tough for you to handle. The answer to the first question is, of course we want animations! Who wants to have water that doesn't flow, fire that doesn't burn, or fountains that have little water droplets somehow magically suspended in the air above them?
Don't get worried about this; animating tiles is actually very easy to do, and I'll take you through it step by step. What we'll do is to create a system that substitutes one tile for another at given intervals of time. Since the screen will be getting redrawn during every frame, swapping the tiles around like this will give the effect of animation. It will be flexible enough to support animations with any number of frames, moving at varying speeds, but simple enough that all you need to know is how to use a linked list. Incidentally, if you don't know how to use a linked list, I'm sure GameDev.Net has an article or two that will show you how.
There's one more thing to consider for tile animations, and that's realtime effects. Imagine this: what if instead of making a torch burn by drawing the frames of animation, you actually applied an algorithm to the fire during each frame to make it look like it's burning? Realtime effects can be very cool, but they're just a little too advanced for what I'll be showing you here. Perhaps I'll cover how to do it in a later article, or maybe you can figure it out on your own!
Layers: First let me clear up the question a little bit: by layers I mean layers of graphics on the screen. The simplest maps have only one layer: the whole map is drawn in one pass, with one tile in each map position. Complicated maps might have separate layers for backgrounds, background objects, items that can be picked up, foreground objects, foreground effects... I've seen some engines where I could just swear the person who wrote it was just trying to come up with as many layers as possible. :)
I recommend using at least two layers: one for background, and one for foreground. This will let the character walk "behind" certain objects, and add some depth to your maps. In Terran, I use three layers. The first is for background, the second is for objects that can appear on multiple backgrounds (like grass, dirt, etc.), and the third is foreground. Let me show you an example of this to make it a little clearer. This is a small section of a map from the game, drawn one layer at a time so you can see exactly what's going on.
Layer 1: Here you see only the tiles from layer 1, which constitute the background for the map. None of these tiles will ever have anything behind them, which is why they are in layer 1.
Layer 2: Now layer 2 is added. Color keys are used for layers 2 and up, so that the tiles don't have to be square objects. Notice how the flower can appear on different backgrounds because it is above layer 1. The second row of the tree is also in layer 2, so that trees can grow near different backgrounds such as grass, coastlines, or other trees.
Characters: Characters are drawn next, so that they appear in front of the first two layers, but behind the third. An important thing to remember here is that characters must be drawn in order from top to bottom so that they occlude each other properly. Hence before drawing characters, Terran sorts all the visible characters by their y-values.
Layer 3: Drawing this layer completes the image. Since the characters have already been drawn, notice how the top of the tree occludes the character to give a sense of depth. Other things that can go in layer 3 are the tops of mountains or houses, the arch above a doorway, etc. So depth is actually relatively easy to implement!
Map size: Finally, you should consider what sizes you will be using for your maps. It goes without saying that not all of your maps will be of the same size, so the better question would be to consider what the maximum map size will be. My thoughts are, why limit the map size at all? If you want to make a ridiculously huge map that takes an hour to cross, you should be able to! The only thing to keep in mind is how much memory this will take. If you have fewer than 256 tiles in a given tileset, then you can represent each tile by a single byte. If you use three layers, and set the maximum map size to 300x300, you get 300*300*3 = 270K bytes. That's certainly not an unreasonable amount of memory to use for maps. And if you want to create larger maps, you can always implement a system that works by loading the map one 300x300 section at a time, for whichever part of the map the player happens to be at. For this example, though, we'll say 300x300 is the maximum size.
We're now starting to get an idea for what sorts of things we will include in our tile engine. Before moving on, we need to take a closer look at maps for a moment, to figure out how we will store them.