LinuxDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


Animation in SDL

by Bob Pendleton
05/15/2003

What is SDL?

The Simple DirectMedia Layer (SDL), a powerful, commercial grade and cross platform game development library, has been used to write or port more than 40 commercial games. SDL runs on pretty much any PC or PDA which has a graphic screen and something at least roughly like an operating system.

Full documentation for SDL can be downloaded or read online. You can also look at the SDL documentation project for user annotated documentation. You can find the latest version of SDL at http://www.libsdl.org/download-1.2.php. SDL has been extended by several auxiliary libraries of which SDL_image, SDL_net, SDL_mixer, and SDL_ttf are the most widely used. Although all the code in this article is C/C++, there are SDL bindings for languages from Ada to Ruby .

Animation In SDL

Every SDL program must first initialize SDL, then select and initialize a video mode. After that we get to the fun stuff: making things move on the screen.

Initializing SDL

You initialize SDL by calling SDL_Init() and shut down SDL by calling SDL_Quit(). The only tricky part is that SDL_Init() lets you decide which of the five SDL subsystems you want to start. You can initialize any or all of SDL timers, audio, video, CD-ROM, and joystick support. SDL_Init() also lets you turn on threaded event processing on operating systems which support it. There is also an option to turn off the SDL parachute, which is on by default. (The SDL parachute tries to return the machine to a usable state when you program crashes, but it has to be disabled to run a debugger on an SDL program.) Typical code to initialize all subsystems and to terminate SDL might look like this:

if (-1 == SDL_Init(SDL_INIT_EVERYTHING))
{
    printf("Can't initialize SDL");
    exit(1);
}
         .
         .
         .
SDL_Quit();

Picking a Video Mode

The video mode controls how graphics are displayed and how your program interacts with the screen. Set the video mode with SDL_SetVideoMode(), which returns an SDL_Surface. All graphics in SDL, including the screen, are stored in a surface. Choose a video mode based on the following information:

  • Full screen or windowed? Does your program try to take over the whole screen, which is normal for games, or does it run in a window? SDL hides the details of creating a window or taking over the screen for full screen games.

  • Related Reading

    Games, Diversions & Perl Culture
    Best of the Perl Journal

  • Screen size. What is the pixel size of the window or the screen resolution when running full screen? If you ask for a full screen size that is not supported on the current machine, SDL will switch to full screen, hiding all other windows. Your program will display in a smaller rectangle centered on the screen.

  • Window properties. If you choose to run in a window, SDL lets you decide whether or not your window has a border and whether or not it can be resized.

  • Bits per Pixel. You can choose how many bits per pixel to work with or you can use the current display depth. If you ask for a display depth that is not available, SDL will give you a software surface of the requested depth, converting your data to the actual display depth when it updates the visible screen.

  • Surface type. You can ask for a software surface (the default), a hardware surface, or an OpenGL surface. If you ask for a hardware surface, you can also ask for it to be double buffered.

The three kinds of display surfaces are stored and accessed differently, and each has its own pros and cons:

  • Software surfaces live in your computer's memory, not on the video card. All drawing done on those surfaces is done in software. They can be slow but you can count on being able to get a software surface.

  • Hardware surfaces live in your video card. Drawing on a hardware surfaces is done using a mixture of hardware and software, depending on the operating system and video card drivers. You may or may not be able to get a hardware surface; it depends on both the hardware and the operating system. Hardware surfaces are not necessarily faster than software surfaces.

  • OpenGL surfaces are completely controlled by the OpenGL hardware and software in your computer. If it is available, OpenGL is usually the fastest and most flexible way to do graphics under SDL.

The following code asks for a 640x480 full screen software surface with a pixel format that is that same as the current display setting.:

int options = (
    SDL_ANYFORMAT  |
    SDL_FULLSCREEN |
    SDL_SWSURFACE
);

SDL_Surface *screen = NULL;

screen = SDL_SetVideoMode(640, 480, 0, options);
if (NULL == screen)
{
    printf("Can't set video mode");
    exit(1);
}

The option SDL_ANYFORMAT tells SDL to leave the number of bits per pixel the same as the current screen settings. SDL_FULLSCREEN tells SDL to take over the whole screen. SDL_SWSURFACE tells SDL that you want a software surface. There is a little trickery going here. The value of SDL_SWSURFACE is zero, so it is only there for documentation purposes. If you leave it out, you still get a software surface. The zero (0) in the call to SDL_SetVideoMode() also tells SDL to not change the number of bits per pixel. SDL_SetVideoMode() returns NULL if it can't set the video mode, so we check for the screen being NULL and give up if it is.

The Animation Loop

Animation is drawing an image on the screen, drawing it again with a small change, and then doing it again, until you are done. Each step of an animation is called a "frame". The time between frames is called a "frame time". The number of frames you can draw each second is the "frame rate". Animation is done in a two-part loop. The first part checks for input and lets you interact with the animation. The second part draws the next frame.

Any time you press a key or move the mouse, SDL stores an event in a queue and waits for your program to process it. The interactive part of the animation loop reads and processes these events. The following code outlines the most basic interactive animation loop:

int done = 0;
SDL_Event event;
while (!done)
{
	while (!done && SDL_PollEvent(&event))
	{
		switch (event.type)
		{
			case SDL_KEYDOWN:
				done = 1;
				break;
		}
	}
	.
	.
	draw something
	.
	.
	SDL_Flip(screen);
}

As long as there are events waiting to be processed, SDL_PollEvent() returns 1 (true) and copies the event in the queue to the event structure pointed to by its lone parameter. When the input queue is empty, it returns 0 (false) and the program falls out of the event loop. The sample loop processes events until either the queue is empty or a key is pressed. When a key press is processed, done is set to 1 (true) and both loops eventually exit. When all other events have been processed, the program draws the next frame of the animation and the process continues.

Nothing you draw on a software surface is visible until it has been copied from memory to the display buffer on the video card. SDL provides two ways to do that: SDL_Flip() and SDL_UpdateRect(). SDL_Flip() copies the entire software surface to the screen. If your screen is set to 640x480 at 4 bytes per pixel, SDL_Flip() will copy 1.2 megabytes per frame and your frame rate will be limited by how fast your computer can copy images to the screen.

SDL_UpdateRect() is designed to let you use a "dirty pixels" scheme. It lets you specify a list of rectangular areas that have been changed and only copies those areas to the screen. This technique is ideal for a game with a complex background but only a small number of moving or changing items. Tracking dirty pixels can give you a dramatic improvement in performance.

This is the simplest kind of animation loop you can write, but it uses all available CPU time and gets in the way of anything else that needs to run. Eventually the OS will pause your program to let waiting tasks run and you will get jerky animation. There is also a good chance that a program written this way will generate animation frames at a rate faster than the refresh rate of the display. I've never seen the point in generating more frames per second than the hardware can display cleanly. Both of these problems are reasons to have your game pause for a few milliseconds between frames. Pausing will let other tasks run without interfering with your program. You can limit the frame rate to something close to what the hardware can display. Use SDL_Delay() to pause briefly the animation loop between frames.

Many people make the mistake of thinking that their animation programs will generate frames at a constant rate and animate by moving objects by a fixed distance per frame. You quickly learn that, no matter what you do, sometimes the time between frames is not constant. In fact, it is surprisingly variable. Even if you tie the frame rate to the display refresh rate, you find that the refresh rate is different for each display your program runs on.

Basing animation on a constant frame rate is a fairy tale that produces jerky animation. When it comes to frame rates I like to hope for a constant frame rate, assume a random frame rate, and smooth it out by basing motion on the time that has elapsed since the last frame. Objects that move at a fixed distance per millisecond will appear on the screen where you expect them, appearing to move at a constant rate, no matter what the frame rate really is.

Sample Program

I've included a sample program (softlines.cpp) that uses all the features and deals with all of the problems described in this article. The program is lumped into one source file for convenience but is organized into four sections.

The first section is a simple Bresenham line drawing routine tailored for use with SDL. SDL supports pixels that are 8, 16, 24, or 32 bits long, and SDL graphics code needs to support all four depths. The internal structure of a pixel doesn't matter to graphics routines like a line drawer--I count on SDL_MapRGB() to set that up. Because SDL supports four pixel depths, I have a line drawing routine for each pixel depth and the main routine line() picks which one to use based on the pixel depth of the drawing surface.

The fact that my sample program includes its own line drawing routine says something important about SDL. SDL is simple: there is nothing in it that isn't commonly used in game programming. Most games written for software buffers do their graphics by copying images. SDL has highly optimized code for copying images in software buffers. To remain simple, SDL has avoided the trap of trying to define a complete two dimensional graphics API. Of course, the SDL community has provided several libraries of graphics functions for use with SDL.

The second section is a class named sweepLine. This class implements a moving line. When you create a sweepLine you provide the starting positions of the two end points of the line and their velocities. The initializer clips the endpoints to make sure they are on the surface. When a sweepLine is told to update(), it computes new positions of the line end points based on the difference between the current time and the last time it was called. If an end point moves off the surface, it is forced back to the edge of the surface and its velocity in the direction of that edge is reversed. The end points appear to bounce off the edge of the screen. The bouncing is not physically correct. It's good enough for a demo.

Related Article:

SDL: The DirectX Alternative -- SDL has become known as an essential toolkit for Linux game development. It's similar to MS's DirectX API -- the big difference is that it's open source and supports multiple operating systems.

sweepLine is an example of a time-based animation. By updating positions based on elapsed time, the animation looks pretty good even when the frame rate is far from consistent. Looking at the code, you'll see that sweepLine will work even if time runs backwards.

The third section of the code is a class named gameTime. This class keeps track of the flow of time in the game. It acts just like a clock that can be stopped and started. If the game is paused, the clock does not advance. When the clock restarts, it continues on without making sudden jumps. The class has methods that return the current game time, start and stop the clock, and test whether the clock is running or not. When used with a time-based animation, stopping the clock stops the animation.

You can do some interesting things with the game clock. You can add code to make it run slow or fast. A slow clock simulates slow motion and a fast clock makes your animation look like it has gone into fast forward. In the sample program, I use the game clock to implement a pause key. Pressing F1 pauses the animation. Pressing it again continues the animation where it left off.

The main() program puts everything together. It first initializes SDL. Then it creates some pixel values for the color of each sweepLine and for black to paint over the background of the demo. Once the colors are defined, the program creates three sweepLine objects that will be animated on the screen. Before going into the animation loop, it starts the gameTime object. The animation loop first checks for events. It stops the program if it sees an escape key or a SDL_QUIT event, which means you clicked on the close button on the window. It also responds to the F1 key to pause or to continue the program. Finally, the animation loop gets the current game time and tells sweepLine objects to update themselves based on the time.

For more details, consult the source code.

Conclusion

In this article we've covered SDL basics and provided an example of using software buffers to do animation. In the next article we'll move closer to the hardware with a look at SDL hardware buffers, and we'll go into the problem of matching hardware video modes to the needs of your application.

Bob Pendleton has been fascinated by computer games ever since his first paid programming job -- porting games from an HP 2100 minicomputer to a UNIVAC 1108 mainframe.


Return to the Linux DevCenter.


Linux Online Certification

Linux/Unix System Administration Certificate Series
Linux/Unix System Administration Certificate Series — This course series targets both beginning and intermediate Linux/Unix users who want to acquire advanced system administration skills, and to back those skills up with a Certificate from the University of Illinois Office of Continuing Education.

Enroll today!


Linux Resources
  • Linux Online
  • The Linux FAQ
  • linux.java.net
  • Linux Kernel Archives
  • Kernel Traffic
  • DistroWatch.com


  • Sponsored by: