jnrdev - the 2d jump'n'run tutorial series
jnrdev #3 - big maps and scrolling - Florian Hufsky

1. Introduction

welcome to the third issue of jnrdev.
at first i want to thank you for the great feedback i've got so far - it keeps me motivated to keep writing.
i hope i've choosen an important topic for this tutorial - big maps and scrolling.

adding big scrollable maps to the existing jnrdev code was in fact... very easy.
it took me one afternoon to integrate the new stuff.
i had to completely rewrite CMap (150 lines of code), but no changes were necessary in CPlayer (beside of adding the scroll code in think()).

and one final thing: you might want to check out the source code of my latest game: super mario war: smw.72dpiarmy.com - it's... a multiplayer deathmatch jump'n'run :).

2. Theory

2.1 Concept of Scrolling

the basic concept of scrolling is the difference between screen and world coordinates.
everything game object in your game stores is world coordinate.
and you have a scrollx/y variable that tells you the world coordinate of the top left edge of the screen.
now when drawing subtract scrollx/y from the world coordinate of your object and you're done.

the nice thing is that you have to change almost nothing in your existand code.
you just have to change the map format and the drawing functions of your objects.

2.2 Adding Scrolling to the jnrdev code

adding big maps and scrolling isn't hard, here's a brief overview of what needs to be done:

1. change the map file format
2. add scroll_x and scroll_y - the global scroll offset
3. rewrite the map draw code
4. alter every draw routine to use scroll_x, scroll_y 5. add a method to change scroll_x/y and a function to limit the scrolling to the map boundaries


scroll_x/y are the x,y scroll offset.

3. Implementation

3.1 the new map format

i decided to use this straight forward map format:

(int)width of map
(int)height of map
(int*width*height)tile data

note that the tile data is now stored in a one dimensional array instead of the two dimensonial array used in the previous tutorials
this makes loading from the file easier.

i've also added a way to acess the map with a 2d array.
cmap.tiles2d is a 2d array [tiley][tilex].
it's filled with pointers to the tile rows everytime a map is loaded or an empty map is created.

the tiles are now stored in a tileset and the map only contains the tileset index.

at the moment the tileset is hardcoded, this will change in a later tutorial.

then i rewrote CMap::load to load the new format.
first you read the mapwidth and height, then you allocate memory for the tile data and read that.
i've added no filesize-ok checks or anything else to keep the code straightforward, but maybe i'll write a tutorial about such things somewhen.

void CMap::loadMap(const char *file){
	FILE *mapfile;
	int i, j, t;

	mapfile = fopen(file, "rb");

	if(tiles)		//map has already been initialized
		freetiles();


	fread(&width, sizeof(int), 1, mapfile);		//read map width
	fread(&height, sizeof(int), 1, mapfile);	//read map height

	tiles = new int[width*height];				//get memory for the map

	t = 0;	
	for(j = 0; j < width; j++){
		for(i = 0; i < height; i++){
			fread(&tiles[t], sizeof(int), 1, mapfile);	//read tile
			t++;
		}
	}
	fclose(mapfile);

	tiles2d = new int*[height];					//build 2d index array to map data
	for(i=0; i<height; i++){					
		tiles2d[i] = &(tiles[i*width]);			//tiles[i] is a pointer to tile row i
	}

}

3.2 CMap::draw

the next thing is changing CMap::draw() to use the new global variables scroll_x and scroll_y and the new map aray.

void CMap::draw(){
	int x, y;	//screen coord for current tile
	int mx, my;	//current tile map coord
	int sx = 0-(scroll_x%TILESIZE);	//startx
	int smx = (sx+scroll_x)/TILESIZE;	//starty

	
	for( y = 0-(scroll_y%TILESIZE), my = (y+scroll_y)/TILESIZE;  y < 480;  y+=TILESIZE){
		for( x = sx, mx = smx;  x < 640;  x+=TILESIZE){
			if(tileset[ tiles2d[my][mx] ].spr != NULL)
				tileset[ tiles2d[my][mx] ].spr->draw(x,y);
			mx++;
		}
		my++;
	}
}

3.3 add scrolling to the game

the last step was setting scroll_x/y in CPlayer::think() so the screen is always centered on the player and calling limit_scroll().

i've added a function CMap::limit_scroll() - it has to be called after setting scroll_x and scroll_y to prevent too far scrolling.

void CMap::limit_scroll(){
	if(scroll_x < 0)				//too far left?
		scroll_x = 0;

	else if(scroll_x+640 > width*TILESIZE)		//don't scroll over the map boundaries on the right side (640 is the screenwidth)
		scroll_x = width*TILESIZE-640;
	

	if(scroll_y < 0)				//too far up?
		scroll_y = 0;
	
	else if(scroll_y + 480 > height*TILESIZE)	//bottom map boundaries (480 - screenheight)
		scroll_y = height*TILESIZE-480;
}

3.5 making the collision detection work with the big map

the only change here was to change CMap::tile() so it uses the one dimensional tile array.

3.5 other changes

i've splitted the source code into several files to make it easier to understand.
i splitted it like i did in super mario war: every important data structure gets its own .cpp/.h file and every .cpp file includes global.h
this file contains everything that's needed by all parts of the game - data structures, global variables and global defines.

beside of this no other changes to the jnrdev code where made.

5. the example

the source example is here: jnrdev3example.zip.

for running the game you will also need SDL.dll, which you can get here: SDL-1.2.7-win32.zip.

for compiling under VisualC++6 you can download the VC++6 SDL zip, which can be found here: SDL-devel-1.2.7-VC6.zip.
note: you have to add some include / library directories if you use VC++, take a look at VisualC.html, which comes with SDL how to do that.
for other compilers the packages are available on the official SDL page: www.libsdl.org.

6. That's it

i think this was a very easy tutorial.
adding big scrollable maps isn't very hard and is a must have for almost every jump'n'run.

i'm very satisfied with this tutorial. the code written for it is very clean and beatiful and it works quite reliable. the only ugly thing is the background tiling.
that will be solved later, i'm already thinking of a tutorial about tilesets and multiple layers...

questions / critics / comments? forum


http://jnrdev.72dpiarmy.com © 2003-2004 Florian Hufsky