Tutorial de SDL – Parte 14 – Clips animados e VSync

Continuando nossa série de artigos traduzidos do site lazyfoo, iremos ver agora como exibir diferentes clips para criar uma animação de um boneco palito. Isso basicamente é o conceito de animação: exibir uma imagem depois de outra para criar a ilusão de movimento.

Digamos que temos esse frames de animação (que claramente demonstram que eu não sou um profissional):

Mostrando cada clip um após o outro a cada décimo de segundo, obtemos essa animação:

Como imagens no SDL 2 são tipicamente SDL_Textures, animação no SDL é uma questão de mostrar diferentes partes de uma textura (ou diferentes texturas completas) uma após a outra.
//Walking animation
const int WALKING_ANIMATION_FRAMES = 4;
SDL_Rect gSpriteClips[ WALKING_ANIMATION_FRAMES ];
LTexture gSpriteSheetTexture;
Assim, aqui temos os clips que iremos usar para nossa animação.
 //Create vsynced renderer for window
 gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
 if( gRenderer == NULL )
 {
 printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
 success = false;
 }
Para esse artigo (além de artigos futuros), iremos querer usar o Vertical Sync. O Vsync permite que a renderização ocorra ao mesmo tempo que a atualização do monitor durante a atualização vertical (vertical refresh). Nesse artigo, iremos nos certificar que a animação não ocorra muito rapidamente. A maioria dos monitores atualiza a 60 frames por segundo, então usaremos esse valor aqui. Se você tiver um monitor com uma taxa de atualização diferente, isso explicaria o porquê da animação está sendo executada muito rapidamente ou lentamente.
bool loadMedia()
{
 //Loading success flag
 bool success = true;

 //Load sprite sheet texture
 if( !gSpriteSheetTexture.loadFromFile( "14_animated_sprites_and_vsync/foo.png" ) )
 {
 printf( "Failed to load walking animation texture!\n" );
 success = false;
 }
 else
 {
 //Set sprite clips
 gSpriteClips[ 0 ].x = 0;
 gSpriteClips[ 0 ].y = 0;
 gSpriteClips[ 0 ].w = 64;
 gSpriteClips[ 0 ].h = 205;

 gSpriteClips[ 1 ].x = 64;
 gSpriteClips[ 1 ].y = 0;
 gSpriteClips[ 1 ].w = 64;
 gSpriteClips[ 1 ].h = 205;

 gSpriteClips[ 2 ].x = 128;
 gSpriteClips[ 2 ].y = 0;
 gSpriteClips[ 2 ].w = 64;
 gSpriteClips[ 2 ].h = 205;

 gSpriteClips[ 3 ].x = 196;
 gSpriteClips[ 3 ].y = 0;
 gSpriteClips[ 3 ].w = 64;
 gSpriteClips[ 3 ].h = 205;
 }

 return success;
}
Depois disso, carregamos os clips que queremos que definam os quadros individuais da animação.
 //Main loop flag
 bool quit = false;

 //Event handler
 SDL_Event e;

 //Current animation frame
 int frame = 0;
Antes do loop principal temos que declarar uma variável para manter registro do quadro atual da animação.
 //Render current frame
 SDL_Rect* currentClip = &gSpriteClips[ frame / 4 ];
 gSpriteSheetTexture.render( ( SCREEN_WIDTH - currentClip->w ) / 2, ( SCREEN_HEIGHT - currentClip->h ) / 2, currentClip );

 //Update screen
 SDL_RenderPresent( gRenderer );

Depois que a tela é limpa no loop principal, queremos renderizar o frame atual da animação.

A animação vai dos frames 0 ao 3. Como temos 4 frames de animação, queremos reduzir um pouco a velocidade da animação. Isso porque quando obtivermos o clip atual, iremos querer dividir o frame por 4. Dessa maneira o frame atual da animação apenas é atualizada a cada 4 frames, já que com o tipo de dado int temos 0 / 4 = 0, 1 / 4 = 0, 2 / 4 = 0, 3 / 4 = 0, 4 / 4 = 1, 5 / 4 = 1, etc.

Depois que obtemos o clip atual, queremos renderiza-lo na tela e atualiza-la.

 //Go to next frame
 ++frame;

 //Cycle animation
 if( frame / 4 >= WALKING_ANIMATION_FRAMES )
 {
 frame = 0;
 }
 }

Agora para que frame seja atualizado, precisamos incrementar o valor do frame a cada frame. Se não fizermos isso, a animação ficará no mesmo frame.

Também queremos que animação entre em um ciclo, de forma que quanto o valor final (16 / 4 = 4) seja atingido, resetamos o frame de volta para 0 para que animação seja iniciada novamente.

Após atualizar o frame ou pelo incremento ou resetando o ciclo de volta ao ponto 0, atingimos o final do loop principal, Esse loop continuará exibindo um frame e atualizando a animação para animar o clip.

Baixe os arquivos de mídia e código fonte desse artigo aqui.