Game Development with SDL2 Part 4: Animating using Sprite Sheets

Guest Posts Bilal Cheema

Author: Bilal Cheema

Previous Post of the Series: Game Development with SDL2 Part 3: Drawing our first image

In this post, we are going to learn about how to animate using sprite sheets. It is very easy and simple to do with SDL2. You should check out the previous post where we rendered out first image. If you feel like SDL_Texture and SDL_Rect are easy, animating using sprite sheet is not a problem at all.

Sprite Sheet

What is a sprite sheet? It is just a bundle of sprites packed into one image. This is a much better and optimized approach. It will be a mess to have an individual image for each sprite. A sprite sheet looks like this:

We will be using the sprite sheet given above. So, just download it. My codes below will only run fine for a sprite sheet having 8 frames in a single row.

Animation

Animation is the process of drawing similar images in a sequence to simulate some action like walking. Usually, we use the word, 'frame' for each sprite being rendered in an animation. Sprite sheets are very commonly used for animation in 2D. Every animation done using sprite sheet, needs two things:

  1. Two or more frames
  2. Delay between each frame

Our sprite sheet contains 8 frames. Therefore, we already fulfilled the first need. The second need can be fulfilled by doing some calculations with time. This is very easy to do with SDL.

Timer in SDL

SDL provides us its own timer. It starts to count the time in milliseconds when the SDL is initialized. We can get that time in milliseconds using the method:

SDL_GetTicks()

Texture Rectangle

Now that we know SDL timer, we need to know that it requires two rectangles to completely describe an image on the screen.

The first one is the one we already know. But the second one is the one which acts as a texture. We need to tell SDL which part of texture is to be rendered on the screen which is possible through Texture Rectangle. If we don't tell that to SDL, SDL will render the whole texture and fit it to the window rectangle (the first rectangle that we discussed in the previous post).

The mystery of third parameter in SDL_RenderCopy() lies here. That parameter is for texture rectangle.

Using Texture Rectangle with SDL_RenderCopy()

SDL_Rect windowRect = { 300,400,100,100 };
SDL_Rect textureRect = { 0,0,200,200 };

//Now, it will display the some of the top-left part of the texture
//in somewhere near the middle of the screen
SDL_RenderCopy(renderer, texture, &textureRect, &windowRect);

Drawing/Rendering our first animation!

That's all we need to know. The only thing that we haven't discussed yet, is a method for getting texture width and height. It is called SDL_QueryTexture(). Make sure to use the above sprite sheet or else it will not work fine for you!

#include <SDL.h>
#include <SDL_image.h>

int main(int argc, char** args)
{
    SDL_Init(SDL_INIT_EVERYTHING);
    IMG_Init(IMG_INIT_PNG);
    SDL_Window* window = SDL_CreateWindow("Animating using Sprite Sheets", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
    SDL_Event input;
    bool quit = false;

    SDL_Texture* spriteSheet = NULL;
    SDL_Surface* temp = IMG_Load("res/capguy-walk.png");
    spriteSheet = SDL_CreateTextureFromSurface(renderer, temp);
    SDL_FreeSurface(temp);

    //'windowRect' defines the dimensions of the rendering sprite on window
 SDL_Rect windowRect;
    windowRect.x = 0;
    windowRect.y = 0;
    windowRect.w = 140;
    windowRect.h = 200;

    //'textureRect' defines the dimensions of the rendering sprite on texture
 SDL_Rect textureRect;
    textureRect.x = 0;
    textureRect.y = 0;

    //SDL_QueryTexture() method gets the width and height of the texture
 SDL_QueryTexture(spriteSheet, NULL, NULL, &textureRect.w, &textureRect.h);
    //Now, textureRect.w and textureRect.h are filled
 //with respective dimensions of the image/texture

    //As there are 8 frames with same width, we simply
 //get the width of a frame by dividing with 8
 textureRect.w /= 8;
    //Height for each frame is the same as for the whole sheet/texture

    while (!quit)
    {
        while (SDL_PollEvent(&input) > 0)
        {
            if (input.type == SDL_QUIT) quit = true;
        }

        //Total number of frames of the animation
     int totalFrames = 8;

        //Amount of delay in milliseconds for each frame
     int delayPerFrame = 100;

        //SDL_GetTicks() method gives us the time in milliseconds

        //'frame' will give us the index of frame we want to render
     //For example, if 'frame' is 2, it will give us the third frame
     int frame = (SDL_GetTicks() / delayPerFrame) % totalFrames;

        //The index of frame is multiplied by the width of the frame
     //This will give us the appropriate
     //frame dimensions from the sprite sheet
     textureRect.x = frame * textureRect.w;

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        //Copying the texture on to the window using
     //renderer, texture rectangle and window rectangle
     SDL_RenderCopy(renderer, spriteSheet, &textureRect, &windowRect);

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(spriteSheet);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();

    return 0;
}

Concept behind the Code

The concept behind these codes is just the same as what we are getting through in the paragraphs above and in the previous posts. If you still don't get the concept of texture rectangle, you can just have this: textureRect.x = SDL_GetTicks() / 100;. Then, you will see that, with time, the texture is gradually proceeding to the right end. Similarly, you can do that with y-position, width or height. In the above codes, we are just shifting the position of texture rectangle to the position of individual frame with proper and regular delays.

All the other code is as clear and self-explanatory as it looks! And if it is not, you must go through all my previous posts of this series. For having more programming stuff or if you have something to discuss with me, you can visit my blog: bacprogramming.wordpress.com

And as always, Happy Coding!