Smooth Scrolling a Tile Map
by Jim Adams

Copyright © 1998 by Jim Adams. All Rights Reserved

< disclaimer >

No part of this article may be reprinted or duplicated in any manner without prior written permission by the Author, Jim Adams with one exception- You may display it unmodified on your Web Page for free public viewing. It must include credits to author, Jim Adams.

The author, Jim Adams, will not to be held resposible for any damages, directly or indirectly, resulting from the use of this file or it's information.

< /disclaimer >


Introduction

Well, here's another attempt at bringing a little information into the world that seems to be a problem for some of you out there. This article will quickly go over scrolling a map view using a tile engine.

I will not be showing how to make a tile blitting routine. If you're reading this, I'm assuming you already have one. What I will be showing is a way to use your tile blitter to render a view, and a very simple one at that. It will consist of one layer.

Also - I didn't have time to add some graphics, but if I get some response, I'll put it in html format and add some pictures.

With that all said, let's get on with it, shall we?

Jim Adams
tcm@pobox.com


Map Structure

As I mentioned, we'll use a very simple one layered map. It will consist of one thing - the tile number to draw at that location. Let's say you have 256 tiles, numbered 0-255.

typedef struct {
   unsigned char tile_number;
} MAP_INFO;

Now comes the map structure array that actually holds the map. Also comes the dimensions of the map in memory through a couple of definitions. The example one will be 128x128.

#define  MAP_HEIGHT    128
#define  MAPWIDTH      128

MAP_INFO map[MAP_HEIGHT][MAP_WIDTH];

Since we are using just the tile number, we could have just created an array, but I'm leaving it open for you to play with by adding to the structure.

Our map can be stored inside a PCX file, with a 128x128 image representing it. Each color palette entry (0-255) would represent the tile number to draw. If you have a standard PCX reader, just load a map like:

signed short map_load(char *pcx_filename)
{
   char *pcxmapbuf;
   short i, j;

   if(pcx_loadimage(pcx_filename, pcxmapbuf) == -1)
      return -1;

   for(i = 0; i < 128; i++) {
      for(j = 0; j < 128; j++)
         map[i][j] = get_pixel(pcxmapbuf, j, i);
   }

   free(pcxmapbuf);

   return 1;
}

Rendering a Simple View

From there you should have a map in memory along with your tiles. I'll assume you have your tiles in an array. I'll use a structure array for example. You should be able to follow along.

#define NUM_TILES        256

TILE_INFO tiles[NUM_TILES];

To render a simple view, with no scrolling, you simply start at the top-left of the screen and render across and down until the screen is full. Let's say we're using 16x16 tiles and a screen res of 320x240. That means we will fit (320/16) 20 tiles across and (240/16) 15 vertically. That means we will draw 300 tiles a screen.

void map_draw(short mapx, short mapy)
{
   short i, j;

   for(i = 0; i < 15; i++) {
      for(j = 0; j < 20; j++)
         tile_draw(map[mapy + i][mapx + j].tile_number, j * 16, i * 16);
   }
}

The above doesn't correct for bounds checking, I'll leave that up to you. Also, you'll see the two variables passed represent what map coordinate to use to start drawing at the top-left of the screen. For us, that runs from 0-127.

If you draw with the above increasing a coordinate, like X, every time, you'll see it scrolls by the size of the tile, or 16 pixels at a time. We need it smoother.

Smooth Scrolling

As a matter of fact, it's extremely easy to change this to smooth scrolling. First thing we have to do is ensure our tile sizes are in the power of 2 (2, 4, 8, 16, 32, 64, etc). This helps the math. There are ways to do other sizes, but I'm dealing with those as they are most popular.

We now create a fine coordinate system for the map. The fine coordinates are simply the size of the tiles, or 16x16. So each map coordinate now is 16x16 in size. That means a map 128x128 is 2048x2048 in fine coordinates.

When we render a map from now on, we tell it to draw from those coordinates. For the rendering function to deal with it, it must decide how much to scroll all the tiles we draw left and up.

So basically, we're now making the map seem like a huge bitmap. We pick an exact coordinate from that to start drawing in the top-left of the screen. To do that from tiles, we have to decided how the tiles align into that large area.

That's why we use tile sizes with the power of 2. First of all, when we want to render the map, say at 100,126 , we have to find the coordinate in the map array.

Since the fine coordinates run off the tile size, we just divide the numbers by 16, coming up with (100/16) 6, (126/16) 7. That tile will be first draw at the top-left of the screen.

mapx = map_drawx / 16;
mapy = map_drawy / 16;

Now comes the fine scroll. If you look, there is actually a remainder from the divisions. 100/16 = 6.25 and 126/16 = 7.88. Those are the fine scroll parts. If we take the coordinates to draw and AND them by the size of the tile -1, we have those remainders. In our case that's (100 & 15) = 4, (126 & 15) = 14.

map_xoff = map_drawx & 15;
map_yoff = map_drawy & 15;

So we now start drawing the tiles 4 pixels to the left and 14 pixels up from where you normally would. Note that because of this, we will have to draw one more row and column of tiles or the right and bottom edge but mess up because we are not drawing enough. It's too easy to understand why, so I'll let you play with it to see what I mean.

So our new rendering function looks like:

void map_draw(short map_drawx, short map_drawy)
{
   short i, j;
   short mapx, mapy;
   short map_xoff, map_yoff;

   mapx = map_drawx / 16;
   mapy = map_drawy / 16;

   map_xoff = map_drawx & 15;
   map_yoff = map_drawy & 15;

   for(i = 0; i < 16; i++) {
      for(j = 0; j < 21; j++)
         tile_draw(map[mapy + i][mapx + j].tile_number, j * 16 - map_xoff, i * 16 - map_yoff);
   }
}

The End

Hope it helps!

Jim Adams
the Collective Mind
tcm@pobox.com
tcm.lv@worldnet.att.net

Discuss this article in the forums


Date this article was posted to GameDev.net: 9/16/1999
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
General

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!