Back to Articles

Programming with SDL - Guide 1


Introduction:

After working on the SDL tutorials, I realized that there are a few questions that a straight "SDL" tutorial wouldn't cover. Many of the things people would be interested in doing, are already covered in terms of the actual functions in the tutorial, but that isn't always enough.

So, now that the tutorial series is nice and underway, I've started the SDL Guide to go along with it. (woot!) Like the series, this is based on my notes as I went along and discovered ways of doing various things. Most likely, the routines in this series will be useful in using enviroments other than SDL.

So, without wasting anymore time, this tutorial will focus on - basic (parallax) scrolling.

An introduction to scrolling:

Scrolling is perhaps THE most frequently asked question in game development circles. It's also one of those subjects for which there is no easy poison pill. Unlike previous tutorials where a copy and paste solution, like the setpixel function could exist - scrolling requires a knowledge of what you as the programmer are trying to go after.

Basic scrolling asks the simple question - how can I get an image larger than the screen to display(in different parts)? There are various methods of scrolling - this guide will cover only the basics. There are several methods of optimizing this code, I'll discuss some as time goes on.

What is parallax scrolling?

As opposed to standard scrolling, parallax scrolling involves multiple layers, each scrolling independently. Generally this is used to give a 3d illusion, or the impression of motion. (Think of Super Mario world.)

The key to parallax scrolling is independence of each of the individual layers: they must all move differently.

The Really Big Image Method:

A lesser used method of scrolling is what I will call the Really Big Image method. Here, we create a single image several times larger than the screen size, and "crop" the image to the screen.

Why use this? It's fast. It works. It's easy to do. Why not? It sucks ram, and limits the size of your world. The size of your world will add up quickly to large amounts of memory. (A 1024x768 world alone could take up 3 megabytes of RAM.) Obviously, if you want to lower system requirements, or have a really large world, this isn't the way to go.

Yet, this method makes for an excellent introduction to scrolling. Using the SDL_BlitSurface function, scrolling using this method is easy. First, load or create the bitmap. Now, remember how the SDL_BlitSurface took 5 parameters, two of which where rectangles? This is the key to scrolling a really big image.

The second parameter to SDL_BlitSurface is a source rectangle. In general, we simply pass NULL to this parameter. Today however, it actually matters. We setup the rectangle to specify what portion of the image we want to draw:

	SDL_Rect a;

	a.w = 640; a.h = 480;
	a.x = SCROLL_X; a.y = SCROLL_Y;
And we're scrolling. The width(a.w) and height(a.h) should be the dimensions of the screen(or the size of a window you want to scroll). The example program scrolls the screen around in the X direction. Using this method.

Tile Based Scrolling:

The next, much more popular method, is tile based scrolling. Tiles allow you to repeat images over - saving ram. When optimized, tile based scrolling can be much faster than Really Big Image scrolling.

The first key to scrolling tiles, it to create something to render them. Here's a really simple tile rendering routine:

	for(x = 0; x < XTILES; x++) {
		for( y = 0; y < YTILES; y++) {
			RenderTile(x*TILE_X_SIZE, y*TILE_Y_SIZE, tileList[x][y]);
		}
	}

Here we simply loop through all the tiles on the screen, and display each one. Now, how do we handle this if we want to scroll by the tile? - Simply by modifying the X and Y starting and ending points:

	for(x = XSTART; x < XTILES+XSTART; x++) {
		for( y = YSTART; y < YTILES+YSTART; y++ ) {
 			RenderTile((x-XSTART)*TILE_X_SIZE, (y-YSTART)*TILE_Y_SIZE, tileList[x][y]);
		}
	}
There's more than one way of doing this. Here, I keep the loop the same, but modify the way position that each tile is rendered in. Another, simpler method might be to change the reference to the tile:
	for(x = 0; x < XTILES; x++) {
		for( y = 0; y < YTILES; y++ ) {
			RenderTile( x*TILE_X_SIZE, y*TILE_Y_SIZE, tileList[x+XSTART][y+YSTART]);
		}
	}
Note, in this code sample, as in all others, it would be prudent to do some sort of bounds checking when referencing the tileList array. (Nasty things might happen otherwise.) Now, this is all well and good, but it isn't exactly smooth. (If your going to do scrolling nowadays, it should at least be smooth.) So, what next?

Well, first we need to keep track of where we are, as a per pixel value - how many pixels into the map we want to move. Let's call this masterX. Now, with masterX, we can figure out an offset, and a pixel offset. This is simple enough, isn't it? Just divide and take the modulus(remainder).

	xmap = masterX / 36;
	xoff = masterX % 36;
	for(x = 0; x < XTILES; x++) {
		for(y = 0; y < YTILES; y++) {
			RenderTile( x*TILE_X_SIZE+xoff, y*TILE_Y_SIZE, tileList[x+xmap][y+YSTART]);
		}
	}

And draw. Or is it? This code will not work as expected. As we scroll the map the pixel offset needs to move in the opposite direction of the map offset. This is easily done by multiplying xoff by -1 or inverting it:

	xmap = masterX / 36;
	xoff = -(masterX % 36);
	for(x = 0; x < XTILES; x++) {
		for(y = 0; y < YTILES; y++) {
			RenderTile( x*TILE_X_SIZE+xoff, y*TILE_Y_SIZE, tileList[x+xmap][y+YSTART]);
		}
	}

That's all there is to it! I leave it to the reader to figure out how to perform Y scrolling. (Hint: it works the exact same.)

Parallax Scrolling

The key to parallax scrolling is to scroll multiple layers at different speeds. To do this, use independent variables to control the scrolling of each layer. There are methods of optimizing parallax scrolling, which I'll cover in later tutorials - such as clipping rectangles. The example code simply uses overdraw. Overdraw generally works, and is generally fast *enough* to use.

Make sure that you draw each layer back first. Unless you are using an optimization technique, you must draw the background first, then the foreground. (If you don't let any of the background show through via Alpha Blending or Color Keying - you won't get the nifty parallax scrolling!) The source code to this tutorial contains a simple parallax scrolling example. Here's the code that makes it possible in the example program:

void DrawPicture() {
	DrawBackground( background );
	DrawForeground();
	SDL_Flip( mainScreen );
}
Simply by controlling the foreground and background at different rates, the parallax look that we all look for jumps through.

Finishing Up:

That does it for the first of this series. I'm looking for interesting topics - ones that follow along the tutorials hopefully.

This tutorial brought to you by MIGHTY VIM.

Code: guide1.tgz


Back to Articles