16 Software
Breevy: A text expander that lets you insert long words or phrases and launch apps, websites, and more just by typing abbreviations.
IcyScreen: Automatic screenshots. Have them saved, e-mailed, and uploaded.

Getting Your Feet Wet in SDL, Part 1

A few weeks ago I started playing around with SDL again, after months of "neglect". I've been considering making a game for some time now, but have been busy working on version 2.00 of IcyScreen. Well, we released it a few days ago, so now seems like as good a time as any to start. I needed a refresher; working on this little introduction got 'er done for me, and hopefully it'll help you out as well.

What's SDL?

SDL is a free, open-source, portable game programming API that makes writing games for multiple platforms pretty darn straightforward. According to Wikipedia, SDL is used by Second Life, DOOM 3, Quake 4, UT2K4, DOSBox, and other applications, so it's definitely popular. Anyway, SDL deals with all of the low-level, hardware/OS-specific stuff so the only thing you have to worry about is your game code.

SDL is implemented in a fairly modular fashion. The core library contains most necessary gaming-related functions -- those dealing with audio, video, joysticks, timers, threads, etc -- and the rest is left to add-on library files. For example, the SDL_image library adds support for a heckuva lot more image formats, SDL_TTF adds font rendering support, SDL_net adds portable networking functions, etc. To utilize these add-on libraries all you have to do is download them, point your compiler to them, and call one of the functions in your code.

In this part I'll be explaining the basic SDL functions and datatypes you need to know in order to make a basic game. I think most of you would agree that the only true way to learn is to code, so in the next part I'll get more technical and post a walk-through of a Pong clone I wrote that will deal with everything I talked about today, plus a lot more (including some basic game programming concepts). My hope is that this will give you a decent understanding of how SDL works and will provide you with the knowledge necessary to start creating games right away.

Prerequisites

To compile an SDL program, you'll need to download the core SDL library for your platform, and point your compiler to the header and library files you downloaded. For the heck of it, you may as well fetch the SDL_TTF library, because we'll be dealing with it in the next part of this introduction.

Edit (4/26): If you're having some issues getting SDL set up, check out this excellent comment.

You'd probably do well to get a copy of the official API documentation as well. This article isn't meant to replace the documentation; it's meant to make the documentation much less necessary, and easier to swallow.

Also note that I'm using C syntax, but SDL has wrappers for many other languages, including C++.

Getting Started

Now that we're set up, lets talk code. The most important SDL function is SDL_Init():

     if (SDL_Init(SDL_INIT_VIDEO) != 0) {
/* Get error string with SDL_GetError(). */
}

You should always call this before any other SDL function in your program; it initializes the subsystems specified in the argument passed to it. For the sake of simplicity, just pass SDL_INIT_VIDEO to this function and forget about it. SDL_Quit() is the polar opposite of this function; it shuts down all initialized SDL subsystems and frees any resources allocated by them.

SDL_Init() only initializes SDL. Now we need a video screen to put all of our graphics in -- what's a game without graphics? We can create a screen by calling SDL_SetVideoMode(), which returns a pointer to an SDL_Surface:

     SDL_Surface *screen = SDL_SetVideoMode(256, 256, 24, SDL_SWSURFACE);
if (screen == NULL) {
/* Get error string with SDL_GetError(). */
}

What the heck is an SDL_Surface?

In SDL, areas of "graphical" memory are represented by the SDL_Surface structure. So if you wanted to load an image with SDL, or create a video screen, it would be loaded into an SDL_Surface structure by SDL. The structure itself contains some useful information pertaining to the surface, such as it's width/height, the raw pixel data, etc. It should be noted, that all SDL_Surfaces should be freed with a call to SDL_FreeSurface() when you're done with them (except for the surface returned by SDL_SetVideoMode(), which is freed with a call to SDL_Quit()).

Back to the SDL_SetVideoMode() function... after you call it with your desired window width, height, bits-per-pixel value, and any desired flags (I passed SDL_SWSURFACE, to tell SDL to create the video surface in system, instead of video, memory), you'll get a pointer to an SDL_Surface that represents the video screen. Consider it the sandbox in which all of your toys (graphics) are placed.

Here's where it starts getting cool. Want to load an image from a file? Easy! Just call SDL_LoadBMP() with the path to the image, and it'll be loaded into an SDL_Surface:

     SDL_Surface *image = SDL_LoadBMP("image.bmp");
if (image == NULL) {
/* Error. You know the drill. */
}

If you want to load images that are in formats other than .BMP, just download the SDL_Image library and call IMG_Load() instead.

To draw that image on screen, make a call to SDL_BlitSurface() with the image's surface (SDL_Surface), the screen's surface, and the X/Y coordinate you'd like the image to be placed at on the screen. Here's a little convenience function to do just that:

int blit_surface(SDL_Surface *src, SDL_Surface *dest, Sint16 x, Sint16 y) {

SDL_Rect offset;
offset.x = x;
offset.y = y;
offset.w = offset.h = 0;

return SDL_BlitSurface(src, NULL, dest, &offset);
}

src is the source surface (in our case, our image); dest is the destination surface -- the screen -- and x and y are of course the X/Y coordinates that the source should start being drawn at on the destination. Note that in SDL, as the X coordinate increases, it moves to the right; as the Y coordinate increases, it moves downward.

After you blit the image onto the screen, you've gotta tell SDL to update the screen so you can see the changes. A simple call to SDL_UpdateRect() will do this:

     blit_surface(image, screen, 25, 30);
SDL_UpdateRect(screen, 25, 30, image->w, image->h);

The above snippet blits the image onto the screen at X/Y coordinate 25, 30, and then tells SDL to update the part of the screen that has changed. To update the entire screen, you can pass the screen, and then all zeros, to the function.

Finishing up

So that's basically that, as far as the basic video screen-creating and graphic loading/drawing functions are concerned. As you can see, SDL doesn't have much of a learning curve, which I find pretty attractive. That certainly doesn't mean game programming is easy; it just means that you don't have to worry about having to deal with an API that's a pain in the butt to use.

What now?

You may be wondering: "How do I detect when a certain key is pressed, make graphics move around on the screen, detect a collision, create a game loop, cap the frame rate, etc?" Don't worry, I'll be covering /all/ of those issues, plus a lot of other stuff, in the next part of the introduction, which will probably be posted in no more than a couple of days.

EDIT (4/22/09): The second part of this article can be read here.

EDIT (5/12/09): Fixed a broken link.

Posted by Patrick on April 20, 2009 at 5:39pm | 6 Comments
Tagged: , , and

6 Comments so far

  1. lemonizer, on April 21, 2009 at 12:57pm, said:

    This is a great start, I've been wanting to learn more about SDL and I'll be waiting for the next part. Thanks!

    Edit Comment

  2. bob, on April 21, 2009 at 5:24pm, said:

    very nice article... hope you get to 3d too...

    Edit Comment

  3. Patrick, on April 21, 2009 at 8:54pm, said:

    Thanks for the positive comments, guys. Hopefully I'll have the next part up by tomorrow.

    @bob: *shudders* I've actually no experience in 3D programming, so unfortunately I won't be writing any tutorials for it anytime soon. hahahah

    Edit Comment

  4. William Bowers, on April 26, 2009 at 3:07am, said:

    Fantastic introduction Patrick. I had a bit of trouble setting up SDL, so I figured I'd post a couple of tips here.

    Note: I'm using g++ 4.0.1 on OS X 10.5.

    First off, this tutorial makes mention in the beginning of "pointing" your compiler to SDL. From my searching on the net, the right way to do that is by using the output of the command "sdl-config --cflags --libs". If you're using a build tool like make or just compiling from the command line, that can be accomplished like this:

    g++ -o <yourprogram> <sources> `sdl-config --cflags --libs`

    Of course, replace 'g++' with whatever compiler you're using.

    And on that note, there are a lot of examples on the web on the web that show SDL being included a couple of different ways, "SDL.h" and "SDL/SDL.h". Most people (in my limited experience) say the right way to do it is "SDL/SDL.h", but for me the output of 'sdl-config --cflags --libs' actually sets '/opt/local/include/SDL' as the SDL include path instead of '/opt/local/include' which means "SDL/SDL.h" won't work. You have two options at this point: 1) include SDL like this "SDL.h", "SDL_Image.h", etc, or 2) add -I/opt/local/include to you call to your compiler.

    The second problem I ran into, which was a particularly gnarly one, was that your main function has to include the argc/argv arguments. If you don't include them, you'll probably get an error something like this:

    Undefined symbols:
    "_SDL_main", referenced from:
    -[SDLMain applicationDidFinishLaunching:] in libSDLmain.a(SDLMain.o)
    ld: symbol(s) not found
    collect2: ld returned 1 exit status

    So, instead of doing this:

    int main ()

    do this:

    int main (int argc, char **argv)

    And that'll (hopefully) fix that.

    That's a particularly confusing error for someone like me who doesn't know much about c/c++, and whose seen a lot of code on the web using the 'int main ()' style.

    Lastly, if you want to use other SDL libraries, like SDL_TTF, SDL_Image, etc, you'll have to add extra flags in your call to your compiler. For example, to link to SDL_Image, you'll need this: -ISDL_Image -lSDL_Image (don't ask why you need both the uppercase I and the lowercase l versions but you do).

    FWIW, after all is said and done, my command looks like this:

    g++ -o <progname> <sources> -I/opt/local/include `sdl-config --cflags --libs` -ISDL_Image -lSDL_Image

    Thanks again Patrick for taking the time to write this up (and for part 2, which I'm about to read next).

    Edit Comment

  5. Patrick, on April 26, 2009 at 12:16pm, said:

    Thanks William. You're right, I probably should've gone into a bit more detail about actually setting up your compiler to link against SDL on each OS.

    First off, this tutorial makes mention in the beginning of "pointing" your compiler to SDL. From my searching on the net, the right way to do that is by using the output of the command "sdl-config --cflags --libs".

    On *nix-like systems (OSX, Linux, etc), you're definitely right, sdl-config is the easiest tool to use, especially when you're installing SDL via a package management system.

    On Windows I find it a pain to set up, so I just manually append -IC:/path/to/sdl_include_dir and -LC:\path\to\sdl_lib_dir to gcc's flags. 'sdl_include_dir' contains the folder "SDL", which then contains the header files SDL.h, SDL_ttf.h, etc etc; 'sdl_lib_dir' contains the development library files.

    The second problem I ran into, which was a particularly gnarly one, was that your main function has to include the argc/argv arguments.

    I believe this can also be solved by appending -lSDL_main to your linker flags (though you may still need the argc/argv arguments in your main() function, too). I completely forgot to mention that in part 2, and I probably should've mentioned it in this part, as well. My apologies.

    I'll update both articles to point to your comment in case anyone has similar troubles. Thanks again, and I'm glad you enjoyed the article.

    Edit Comment

  6. AndrewBoldman, on June 4, 2009 at 5:22pm, said:

    Hi, cool post. I have been wondering about this topic,so thanks for writing.

    Edit Comment

Post a comment