Login

Game Programming using SDL: Getting Started

Game programmers using OpenGL have often been forced to make a choice between using a library that is platform independent but doesn’t use all the available resources, or powerful but platform dependent. Simple Directmedia Layer (SML) offers a third way. This article will give you a taste of its capabilities.

Game programming has come a long way since early Linux and Windows days. The time is gone when games were limited to Windows or to an extended Mac. Today portability is in the forefront, even in the gaming segment. The birth of OpenGL was the first step in this regard. But OpenGL addressed only the rendering aspect of game programming. The major part, that is communicating with varied input devices, was left to the operating system. That is the reason for the existence of various extensions to OpenGL, including GLUT (platform independent), MESA (OpenGL extension for *nix systems) and WOGL (OpenGL extension for Windows).

Each of these has its own pros and cons. If a library is OS independent, then it is limited in utilization of all the available resources. If it is able to harness the power of the underlying system, then such a library is platform dependent. Apart from portability issues, all the existing libraries left the task of developing the gaming infrastructure on the shoulders of the developer. It was during such times of extreme choices that SDL came into picture.

SDL (Simple Directmedia Layer) is a library "by the game programmers for the game programmers." Hence it doesn’t try to achieve the "unachievable" by starting from scratch. Instead it is built upon the existing libraries for each OS, i.e. it uses DirectX for Windows and XWindows APIs for *nix systems. Additionally, SDL provides for all the infrastructure needs of a varied range of games.

In this discussion, I will focus on setting up SDL and accessing one of its many infrastructure facilities — loading a sprite. In the first section I will enumerate the infrastructure services. The second section will focus on the initializing the video to achieve the best resolution. In the third section, I will discuss how to load a bitmap using SDL APIs. The third section will also detail the real world implementation of using an SDL API for sprite loading.

{mospagebreak title=SDL: The Services Provided}

The implementation works in such a way that it never gets in the way of a programmer’s code, as is evident from the services provided by SDL. In a nutshell one can say that it follows the philosophy of SMILE (Simple Makes It a Lot Easier) which is evident from the following services provided by it:

Initialization and Shutdown

Input processing

Timers

Sound effects

Graphics manipulation

Network integration

Threading requirements

Of these services, the first five are the basis of any game. SDL makes dealing with each of them easier. Let’s see how.

Initialization and Shutdown:

Whenever a game starts, it must perform initialization routines including memory allocation, resource acquisition, loading any required data from the disk, and so forth. To perform these routines, the programmer has to query the underlying OS to know the boundaries set by it. To achieve this end some code must be written, and code must be written again to use the result of the query. SDL abstracts this with a single function: SDL_Init().

Input Processing:

In a gaming environment the input can come from the keyboard, joystick, mouse and so on. The processing model provided by SDL is event based. Anyone who has worked in VB, Delphi or Xlib (or any of its variants) will feel at home with SDL’s event model. The base of this model is the SDL_WaitEvent() method that takes SDL_Event as a reference.

Timers:

Without timers it is nearly impossible to imagine any challenging game. If one goes by standard methods, one would have to rely on the timers provided by the platform. But with SDL, this is a thing of past. The Time and Timer APIs provided by it are lean, mean and clean in a platform and OS independent way. SDL_getTicks() is the core of the SDL Timer API.

Sound Effects:

As with other functionalities provided by SDL, the functionalities related to sound are provided with minimum hassles. The sound support as a core sub-system is minimal in nature, adhering to the keep-it-lean philosophy of SDL. But there are other libraries that provide extended capabilities around SDL’s APIs.

Graphics Manipulation:

With SDL one has the option of working at the raw pixel level or at a higher level using OpenGL. Since OpenGL is available for every platform and it can render both 2D and 3D graphics in hardware accelerated mode, it is better to use OpenGL in conjunction with SDL.

Networking Requirements:

Like other functionalities, networking is also important in the current genre of games. Understanding this importance, the developers of SDL provided an APIs that does the ground-level work to set up the network connections and manage them, thus making networked multiplayer games less of an enigma.

Threading Requirements:

The pthreads library provided by POSIX is a platform independent way of working with threads. But the API works at a low level, which can be confusing. To make threading simpler, SDL provides all the required functionalities in a high-level manner.

In essence, SDL provides for all gaming requirements in a simple and portable way. Now that the introduction to the functionalities is out of our way, we can actually see how the theory works by looking at how handling the video subsystem works.

{mospagebreak title=Working with Video the SDL Way}

When working with gaming libraries, one has to drop into system specific APIs(Win SDK on Windows and Xlib and et al on *nix) to access the video related functionalities. These functionalities include initializing the video, setting the best video mode and loading the bitmapped images among other things. But SDL encapsulates all these within the video sub-system. The functions that provide access to these are SDL_Init() and SDL_SetVideoMode.

SDL_Init() initializes the sub-system that has been passed as parameter. To initialize video the parameter would be SDL_INIT_VIDEO. To elucidate:

SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)

would initialize video as well as audio.

Once the video is initialized, the next obvious step is setting the best video mode. To achieve this end, SDL contains a method called SDL_SetVideoMode. This method sets up a video mode with specified width, height and bits per pixel i.e. depth. In short using this function one could set up the required resolution. The parameters include width, height, bpp (bits-per-pixel) and flags. The first three parameters take integer values. They represent height, width and depth of the screen respectively.

The fourth parameter needs some consideration. The flags parameter defines the properties of the surface of the screen. They are eleven in number. The important and most commonly used are:

a. SDL_SWSURFACE:

This instructs SDL to create the surface in the system memory. In other words, the rendering area is created using a software renderer. This is useful when support for software-based acceleration is on cards.

b. SDL_HWSURFACE:

To create a surface in hardware memory i.e. the memory of the graphics card, use this value as the flag value. In other words, to support hardware acceleration use this value. It can be used with SDL_SWSURFACE to support both.

c. SDL_ANYFORMAT:

When the passed depth value is unsupported on the target machine, then SDL emulates it with a shadow surface, i.e. to emulate required depth, SDL would use shadows. To prevent this pass SDL_ANYFORMAT as the flag value. By using SDL_ANYFORMAT, SDL can be instructed to use the video surface even if the required depth is not available.

d. SDL_DOUBLEBUF:

This flag enables hardware double buffering. The double buffering works only if called with SDL_HWSURFACE. Otherwise when the flipping function is called only updating of the surface takes place.

e. SDL_OPENGL:

This flag creates an OpenGL rendering context. This is useful when SDL is used in conjunction with OpenGL.

f. SDL_FULLSCREEN:

By passing this as the flag value, the mode could be changed to full screen. If SDL is unable to do so, it will use the next available higher resolution. But the window will be centered on a black screen.

g. SDL_NOFRAME:

To show the window without decoration (without the title bar and frame decoration), use this as the value. By setting SDL_FULLSCREEN as the flag value, this flag is automatically set.

All the above values are the same as that of SDL_Surface. The SDL_SetVideoMode() function returns a pointer to the structure SDL_Surface. Now let’s see how to use it in a program.

#include "SDL.h"#include<stdio.h>

int main(int argc,char* argv[]) {SDL_Surface *screen;

/*The following code does the initialization for Audio and Video*/int i_error=SDL_Init(SDL_INIT_VIDEO);

If you recall, most of the above code covers the same ground as I discussed earlier in this article. The parts I want to focus on are set in bold. First a point to the structure SDL_Surface is declared. When the video mode is set, this comes into the picture. Then the video is initialized using SDL_Init(). If initialization fails, then exit the application.

As I said before, the function to set video mode returns a pointer to the initialized SDL_Surface structure. The above code sets the resolution at 640×480 at 8 bit depth. It also sets the rendering to software based, i.e. the surface is created in system memory and not in the graphics card’s memory. Now that video mode has been set we can move to the next section, which will cover loading a bitmap onto the returned surface.

{mospagebreak title=SDL in Real World: Loading Sprites}

In a game a sprite can be anything from an image to a 3-D model. To keep things simple, let’s consider a bitmap as a sprite. In SDL loading a bitmap is like loading a file and reading its content using the standard C library.

To load a bitmap onto the surface the following functions come handy:

SDL_LoadBMP:

This function forms the basis of loading a bitmap. It returns a pointer to the surface for the name of the bitmap given as the parameter. If the loading of the bitmap has not been successful, null is returned. For example, if the file parameter is "Tux.bmp," then the following code will load it:

SDL_Surface *image=SDL_LoadBMP("Tux.bmp");

SDL_SetColors:

The default palette would be an 8x8x4 color cube. To get better color matching we must palletize the image itself. For this the SDL_Setcolors function is quite useful. The first parameter is the surface for which the palette has to be created, second parameter is the SDL color component which decides the number of displayable colors. The third and fourth parameters set the range of colors to be used.

To palletize the loaded image, the first parameter would be the surface returned by SDL’s initialization routine, the second is the color component of the image to be palletized, the third would be 0 (that would be the lower range of the colors to be used), and the maximum value of the color palette of the image would be the fourth parameter. To put it in code:

where screen is the surface returned by the initialization and image is the loaded bitmap.

SDL_BlitSurface:

This function performs a fast blit from the source surface to the destination surface. The parameters are source surface, source rectangle, destination surface and destination rectangle. If the source and destination rectangle are specified as null, the entire surface is copied. For example the following code copies the loaded image surface to the screen surface:

SDL_BlitSurface(image, NULL, screen, NULL);

SDL_UpdateRect:

Once the loaded image surface is copied onto the screen surface, it must be ensured that the screen display is updated accordingly. The surface to be updated is the first parameter, the rectangle of the screen to be updated is specified as the second parameter. The following fragment updates the screen according to the height and width of the loaded image:

SDL_UpdateRect(screen, 0, 0, image->w, image->h);

SDL_FreeSurface:

Once work is completed with the loaded image, the surface has to be freed so that the memory occupied by the surface is released. To free a surface, SDL library contains SDL_FreeSurface. The parameter is the surface to be freed. In code it would be:

SDL_FreeSurface(image);

where image is the surface to be freed.

Now that the functions to be used have been introduced, let’s see them in action. First define a function that loads a bitmap image passed to it as a parameter.

/** Palettized screen modes will have a default palette (a standard* 8*8*4 colour cube), but if the image is palettized as well we can* use that palette for a nicer colour matching*/if (image->format->palette && screen->format->palette) {SDL_SetColors(screen, image->format->palette->colors, 0, image->format->palette->ncolors);}

As you can observe the code is a compilation of all the functions I had discussed earlier. The only difference is that error handling code has been used. To use it first declare a global variable of the type SDL_Surface:

SDL_Surface *screen=NULL;

Then call the display_bmp() as follows:

int main(int argc,char* argv[]){/*variable to hold the file name of the image to be loaded*In real world error handling code would precede this */char* filename="Tux.bmp";

/*The following code does the initialization for Audio and Video*/int i_error=SDL_Init(SDL_INIT_VIDEO);

/* Now call the function to load the image and copy it to the screen surface*/load_bmp(filename);

}

That brings us to the end of this part of the discussion on SDL. In the next part I will discuss working at the pixel level and handling keyboard events as well as details of the initialization of SDL itself. Till next time…