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.
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.
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.