Animation
Last Updated 2/15/14
Up until this point we've pretty much have just been working with still images. This tutorial makes a stick figure walk across the screen to teach the basics of animating sprites.An Animated Sprites and VSync tutorial with SDL 2 is now available.
The basic concept of animation is to take a series of images like the ones in this sprite sheet:
Then show one right after the other to create the illusion of movement:
So when you're animating in SDL, you're showing a sequence of SDL_Surfaces.
Then show one right after the other to create the illusion of movement:
So when you're animating in SDL, you're showing a sequence of SDL_Surfaces.
//The stick figure
class Foo
{
private:
//The offset
int offSet;
//Its rate of movement
int velocity;
//Its current frame
int frame;
//Its animation status
int status;
public:
//Initializes the variables
Foo();
//Handles input
void handle_events();
//Moves the stick figure
void move();
//Shows the stick figure
void show();
};
Here's the class of the stick figure that we're going to move across the screen.
First we have the "offSet" and "velocity" variables. Since we're only moving the stick figure right or left, we only keep track of the x offset and velocity.
Then we have the "frame" and "status" variables. "frame" keeps track of which frame in the animation to show. "status" keeps track of which animation to show, either the animation of the Foo walking left or the animation of Foo walking right.
Then of course we have the constructor, the event handler, and the functions that move and show the stick figure.
First we have the "offSet" and "velocity" variables. Since we're only moving the stick figure right or left, we only keep track of the x offset and velocity.
Then we have the "frame" and "status" variables. "frame" keeps track of which frame in the animation to show. "status" keeps track of which animation to show, either the animation of the Foo walking left or the animation of Foo walking right.
Then of course we have the constructor, the event handler, and the functions that move and show the stick figure.
void set_clips()
{
//Clip the sprites
clipsRight[ 0 ].x = 0;
clipsRight[ 0 ].y = 0;
clipsRight[ 0 ].w = FOO_WIDTH;
clipsRight[ 0 ].h = FOO_HEIGHT;
clipsRight[ 1 ].x = FOO_WIDTH;
clipsRight[ 1 ].y = 0;
clipsRight[ 1 ].w = FOO_WIDTH;
clipsRight[ 1 ].h = FOO_HEIGHT;
clipsRight[ 2 ].x = FOO_WIDTH * 2;
clipsRight[ 2 ].y = 0;
clipsRight[ 2 ].w = FOO_WIDTH;
clipsRight[ 2 ].h = FOO_HEIGHT;
clipsRight[ 3 ].x = FOO_WIDTH * 3;
clipsRight[ 3 ].y = 0;
clipsRight[ 3 ].w = FOO_WIDTH;
clipsRight[ 3 ].h = FOO_HEIGHT;
clipsLeft[ 0 ].x = 0;
clipsLeft[ 0 ].y = FOO_HEIGHT;
clipsLeft[ 0 ].w = FOO_WIDTH;
clipsLeft[ 0 ].h = FOO_HEIGHT;
clipsLeft[ 1 ].x = FOO_WIDTH;
clipsLeft[ 1 ].y = FOO_HEIGHT;
clipsLeft[ 1 ].w = FOO_WIDTH;
clipsLeft[ 1 ].h = FOO_HEIGHT;
clipsLeft[ 2 ].x = FOO_WIDTH * 2;
clipsLeft[ 2 ].y = FOO_HEIGHT;
clipsLeft[ 2 ].w = FOO_WIDTH;
clipsLeft[ 2 ].h = FOO_HEIGHT;
clipsLeft[ 3 ].x = FOO_WIDTH * 3;
clipsLeft[ 3 ].y = FOO_HEIGHT;
clipsLeft[ 3 ].w = FOO_WIDTH;
clipsLeft[ 3 ].h = FOO_HEIGHT;
}
Here's the function that sets the clips for the individual sprites in the sprite sheet.
We have two sets of sprites, the sprites clipped by clipsRight which are frames of the animation of Foo walking right and the sprites clipped by clipsLeft which are frames of the animation of Foo walking left.
We have two sets of sprites, the sprites clipped by clipsRight which are frames of the animation of Foo walking right and the sprites clipped by clipsLeft which are frames of the animation of Foo walking left.
Foo::Foo()
{
//Initialize movement variables
offSet = 0;
velocity = 0;
//Initialize animation variables
frame = 0;
status = FOO_RIGHT;
}
In the constructor for the Foo class, first we initialize the offset and velocity.
Then we set the animation to be frame 0, and we set the status to FOO_RIGHT so that the default animation is that of the stick figure walking right.
Then we set the animation to be frame 0, and we set the status to FOO_RIGHT so that the default animation is that of the stick figure walking right.
void Foo::move()
{
//Move
offSet += velocity;
//Keep the stick figure in bounds
if( ( offSet < 0 ) || ( offSet + FOO_WIDTH > SCREEN_WIDTH ) )
{
offSet -= velocity;
}
}
Now in move(), we first move the stick figure and keep it in bounds like always.
void Foo::show()
{
//If Foo is moving left
if( velocity < 0 )
{
//Set the animation to left
status = FOO_LEFT;
//Move to the next frame in the animation
frame++;
}
//If Foo is moving right
else if( velocity > 0 )
{
//Set the animation to right
status = FOO_RIGHT;
//Move to the next frame in the animation
frame++;
}
//If Foo standing
else
{
//Restart the animation
frame = 0;
}
After the stick figure is moved, it's time to do the actual animation.
First we check which way it's moving.
If it's moving left, we set the status to FOO_LEFT, then increment the frame counter so the next sprite in the animation is shown.
If it's moving right, we set the status to FOO_RIGHT, then increment the frame counter so the next sprite in the animation is shown.
If the figure is still, we set the frame to 0 to restart the animation. This is so the stick figure doesn't look like it's in mid-step when it's standing still.
If it's moving left, we set the status to FOO_LEFT, then increment the frame counter so the next sprite in the animation is shown.
If it's moving right, we set the status to FOO_RIGHT, then increment the frame counter so the next sprite in the animation is shown.
If the figure is still, we set the frame to 0 to restart the animation. This is so the stick figure doesn't look like it's in mid-step when it's standing still.
//Loop the animation
if( frame >= 4 )
{
frame = 0;
}
After that we check if the frame counter already went past the fourth frame, since there's only 4 frames in the animation.
If the frame counter has gone too far, we restart the animation so it will keep looping while the stick figure is moving.
//Show the stick figure
if( status == FOO_RIGHT )
{
apply_surface( offSet, SCREEN_HEIGHT - FOO_HEIGHT, foo, screen, &clipsRight[ frame ] );
}
else if( status == FOO_LEFT )
{
apply_surface( offSet, SCREEN_HEIGHT - FOO_HEIGHT, foo, screen, &clipsLeft[ frame ] );
}
}
Lastly, we show the proper sprite on the screen.
If the stick figure is moving right we apply the proper sprite from the walking right animation, if the stick figure is moving left we apply the proper sprite from the walking left animation.
If the stick figure is moving right we apply the proper sprite from the walking right animation, if the stick figure is moving left we apply the proper sprite from the walking left animation.
//Set the sprite sheet clips
set_clips();
//The frame rate regulator
Timer fps;
//Make the stick figure
Foo walk;
In our main function after the initialization and file loading, we set the clips for the sprite sheet, then declare a FPS timer, then declare the stick figure object.
//While the user hasn't quit
while( quit == false )
{
//Start the frame timer
fps.start();
//While there's events to handle
while( SDL_PollEvent( &event ) )
{
//Handle events for the stick figure
walk.handle_events();
//If the user has Xed out the window
if( event.type == SDL_QUIT )
{
//Quit the program
quit = true;
}
}
//Move the stick figure
walk.move();
//Fill the screen white
SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );
//Show the stick figure on the screen
walk.show();
//Update the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
//Cap the frame rate
if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
{
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
}
Here we have the main loop. It's pretty much the same story as before with our Dot class from previous lessons.
So as you can see for an animation engine, all you have to do is keep track of which animation you're using and which frame you're blitting.
So as you can see for an animation engine, all you have to do is keep track of which animation you're using and which frame you're blitting.