In the previous SDL tutorial, I showed you how to read from the keyboard and make something move around on the screen. Unfortunately, the movement was not very convincing. Since there was no animation the sprite just slid around the screen. We can do much better than that.

This program builds on the example from the previous tutorial, so if you haven't read it yet, or if you're not clear on the basics of SDL, now's a good time to go check it out.

The Animated Sprite Program

There are only two big changes between this program and the previous sample.

First, I've added a new function called HandleEvents to handle SDL events. In the previous program I used SDL_GetKeyState to get an array of keys being pressed during each iteration of the game loop. For this program I decided to simply move the sprite in response to keyboard events. Be sure to also call SDL_EnableKeyRepeat while initializing SDL to make key events repeat at the specified times.

The other change is the addition of a source rect to SDL_BlitSurface when we're drawing the sprite. The destination rect tells the function where to draw the image on the screen, the source rect tells it which part of the image to use. The sprite image looks like this:

Note that all eight frames of the animation are in one file. The first two frames are moving up, then right, down, and left. As the player moves around the screen we'll toggle between the two frames that represent the direction of movement. This is accomplished by changing the x member of spriteImage in the keyboard handler. For example, while moving up the x value will go back and forth between 0 and 32 in order to switch between the two frames.

The rest of the program is almost exactly the same as the moving sprite program in the previous tutorial.

Save this file as sdlanim.c. You'll also need the bitmap files that go with this program. They're available for download with the source code at the bottom of this page.

#include <SDL.h>

/* Size of the window */
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
/* Size of the grass texture picture */
#define GRASS_SIZE    32

/* In the sprite, we have 8 images of a 32x32 picture,
 * 2 images for each direction. */
/* Size of one image: */
#define SPRITE_SIZE     32
/* Order of the different directions in the picture: */
#define DIR_UP          0
#define DIR_RIGHT       1
#define DIR_DOWN        2
#define DIR_LEFT        3

/* Number of pixels for one step of the sprite */
#define SPRITE_STEP     5

/* Handle events coming from the user:
        - quit the game?
        - use of arrows to move the sprite
   In the second case, we modify the current direction and the
   position of the sprite in the window.
   We also change the animation bit used for creating the "walk" effect.
   */
void HandleEvent(SDL_Event event,
        int *quit, int *currDirection, int *animFlip, SDL_Rect *position)
{
    switch (event.type) {
        /* close button clicked */
        case SDL_QUIT:
            *quit = 1;
            break;

        /* handle the keyboard */
        case SDL_KEYDOWN:
            switch (event.key.keysym.sym) {
                case SDLK_ESCAPE:
                case SDLK_q:
                    *quit = 1;
                    break;
                case SDLK_LEFT:
                    *currDirection = DIR_LEFT;
                    *animFlip = 1 - *animFlip;
                    position->x -= SPRITE_STEP;
                    break;
                case SDLK_RIGHT:
                    *currDirection = DIR_RIGHT;
                    *animFlip = 1 - *animFlip;
                    position->x += SPRITE_STEP;
                    break;
                case SDLK_UP:
                    *currDirection = DIR_UP;
                    *animFlip = 1 - *animFlip;
                    position->y -= SPRITE_STEP;
                    break;
                case SDLK_DOWN:
                    *currDirection = DIR_DOWN;
                    *animFlip = 1 - *animFlip;
                    position->y += SPRITE_STEP;
                    break;
                default:
                    break;
            }
            break;
    }
}

int main(int argc, char* argv[])
{
    SDL_Surface *screen, *temp, *sprite, *grass;
    int colorkey;

    /* Information about the current situation of the sprite: */
    int currentDirection = DIR_RIGHT;
    /* Walking in a direction is animated with 2 images; we use a boolean
     * that we flip after each movement to show them in turn and create
     * the illusion */
    int animationFlip = 0;

    /* Rectangle to store the position of the sprite in the window.
     * Only the x and y coordinates are used. */
    SDL_Rect spritePosition;


    /* initialize SDL */
    SDL_Init(SDL_INIT_VIDEO);

    /* set the title bar */
    SDL_WM_SetCaption("SDL Animation", "SDL Animation");

    /* create window */
    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);

    /* set keyboard repeat */
    SDL_EnableKeyRepeat(10, 10);

    /* load sprite */
    temp   = SDL_LoadBMP("sprite.bmp");
    sprite = SDL_DisplayFormat(temp);
    SDL_FreeSurface(temp);

    /* setup sprite colorkey and turn on RLE */
    colorkey = SDL_MapRGB(screen->format, 255, 0, 255);
    SDL_SetColorKey(sprite, SDL_SRCCOLORKEY | SDL_RLEACCEL, colorkey);

    /* load grass */
    temp  = SDL_LoadBMP("grass.bmp");
    grass = SDL_DisplayFormat(temp);
    SDL_FreeSurface(temp);

    /* set sprite position in the middle of the window */
    spritePosition.x = (SCREEN_WIDTH - SPRITE_SIZE) / 2;
    spritePosition.y = (SCREEN_HEIGHT - SPRITE_SIZE) / 2;

    int gameover = 0;

    /* main loop: check events and re-draw the window until the end */
    while (!gameover)
    {
        SDL_Event event;

        /* look for an event; possibly update the position and the shape
         * of the sprite. */
        if (SDL_PollEvent(&event)) {
            HandleEvent(event, &gameover, &currentDirection,
                    &animationFlip, &spritePosition);
        }

        /* collide with edges of screen */
        if (spritePosition.x <= 0)
            spritePosition.x = 0;
        if (spritePosition.x >= SCREEN_WIDTH - SPRITE_SIZE) 
            spritePosition.x = SCREEN_WIDTH - SPRITE_SIZE;

        if (spritePosition.y <= 0)
            spritePosition.y = 0;
        if (spritePosition.y >= SCREEN_HEIGHT - SPRITE_SIZE) 
            spritePosition.y = SCREEN_HEIGHT - SPRITE_SIZE;

        /* draw the background */
        for (int x = 0; x < SCREEN_WIDTH / GRASS_SIZE; x++) {
            for (int y = 0; y < SCREEN_HEIGHT / GRASS_SIZE; y++) {
                SDL_Rect position;
                position.x = x * GRASS_SIZE;
                position.y = y * GRASS_SIZE;
                SDL_BlitSurface(grass, NULL, screen, &position);
            }
        }

        /* Draw the selected image of the sprite at the right position */
        {
            /* Define the source rectangle for the BlitSurface */
            SDL_Rect spriteImage;
            spriteImage.y = 0;
            spriteImage.w = SPRITE_SIZE;
            spriteImage.h = SPRITE_SIZE;
            /* choose image according to direction and animation flip: */
            spriteImage.x = SPRITE_SIZE*(2*currentDirection + animationFlip);

            SDL_BlitSurface(sprite, &spriteImage, screen, &spritePosition);
        }

        /* update the screen */
        SDL_UpdateRect(screen, 0, 0, 0, 0);
    }

    /* clean up */
    SDL_FreeSurface(sprite);
    SDL_FreeSurface(grass);
    SDL_Quit();

    return 0;
}

Note, there is no error checking in this program. If something fails, it will be hard to track down. I left out the error checking just to make the program shorter and easier to follow. In a real program you should check the return values of all the SDL function calls.

Compiling

You can compile this program by typing this command:

gcc -Wall -g -std=c99 sdlanim.c `sdl-config --cflags --libs` -o sdlanim

Then run the program by typing:

./sdlanim

If everything worked correctly, a 640x480 window should open with a field of grass and an animated sprite that you can move with the keyboard.

Downloads