Tutorial de SDL – Parte 11 – Renderização de clips

Continuando com nossa série de artigos traduzido do site lazyfoo, agora veremos como renderizar clips contidos em uma única imagem.

Algumas vezes você quer renderizar apenas parte de uma textura. Em várias situações jogos preferem manter múltiplas imagem na mesma folha ao invés de ter que usar um monte de texturas. Usando a renderização de clips, podemos definir uma parte da textura a ser renderizada ao invés de renderizar ela inteira.

//Texture wrapper class
class LTexture
{
    public:
        //Initializes variables
        LTexture();
        //Deallocates memory
        ~LTexture();
        //Loads image at specified path
        bool loadFromFile( std::string path );
        //Deallocates texture
        void free();
        //Renders texture at given point
        void render( int x, int y, SDL_Rect* clip = NULL );
        //Gets image dimensions
        int getWidth();
        int getHeight();
    private:
        //The actual hardware texture
        SDL_Texture* mTexture;
        //Image dimensions
        int mWidth;
        int mHeight;
};

Aqui fazemos uma pequena alteração na função de renderização da classe da textura. A função agora aceita um retângulo que define qual parte da textura queremos renderizar. Damos a esse argumento o valor padrão NULL no caso de querermos renderizar a textura inteira.

//Scene sprites
SDL_Rect gSpriteClips[ 4 ];
LTexture gSpriteSheetTexture;

Nesse artigo, iremos usar essa folha de imagens:
sprites
E renderizar cada unidade em um canto da tela diferente:
final
Assim, iremos precisar de uma imagem de textura e 4 retângulos para definir as unidades, que são variáveis que você declara.

void LTexture::render( int x, int y, SDL_Rect* clip )
{
    //Set rendering space and render to screen
    SDL_Rect renderQuad = { x, y, mWidth, mHeight };
    //Set clip rendering dimensions
    if( clip != NULL )
    {
        renderQuad.w = clip->w;
        renderQuad.h = clip->h;
    }
    //Render to screen
    SDL_RenderCopy( gRenderer, mTexture, clip, &renderQuad );
}

Aqui temos a nova função de renderização da classe da textura que suporta a renderização de clips. É basicamente a mesma da função antiga mas com duas mudanças.
Em primeiro lugar, como quando ao recortar a textura estaremos usando as dimensões de um retângulo ao invés de usar a textura inteira. iremos ter que ajustar a largura/altura do retângulo de destino (aqui chamado de renderQuad) para o tamanho do retângulo a ser recortado.
Em segundo lugar, iremos passar o retângulo a ser recortado para SDL_RenderCopy como retângulo fonte. Esse retângulo define qual parte da textura você quer renderizar. Quando o retângulo fonte é NULL, a textura inteira é renderizada.

bool loadMedia()
{
    //Loading success flag
    bool success = true;
    //Load sprite sheet texture
    if( !gSpriteSheetTexture.loadFromFile( "11_clip_rendering_and_sprite_sheets/dots.png" ) )
    {
        printf( "Failed to load sprite sheet texture!\n" );
        success = false;
    }
    else
    {
        //Set top left sprite
        gSpriteClips[ 0 ].x =   0;
        gSpriteClips[ 0 ].y =   0;
        gSpriteClips[ 0 ].w = 100;
        gSpriteClips[ 0 ].h = 100;
        //Set top right sprite
        gSpriteClips[ 1 ].x = 100;
        gSpriteClips[ 1 ].y =   0;
        gSpriteClips[ 1 ].w = 100;
        gSpriteClips[ 1 ].h = 100;
        //Set bottom left sprite
        gSpriteClips[ 2 ].x =   0;
        gSpriteClips[ 2 ].y = 100;
        gSpriteClips[ 2 ].w = 100;
        gSpriteClips[ 2 ].h = 100;
        //Set bottom right sprite
        gSpriteClips[ 3 ].x = 100;
        gSpriteClips[ 3 ].y = 100;
        gSpriteClips[ 3 ].w = 100;
        gSpriteClips[ 3 ].h = 100;
    }
    return success;
}

A função de carregamento de imagem carrega a textura e então define os retângulo a ser recortada para cada unidade da textura carrega com sucesso.

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }
                //Clear screen
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
                SDL_RenderClear( gRenderer );
                //Render top left sprite
                gSpriteSheetTexture.render( 0, 0, &gSpriteClips[ 0 ] );
                //Render top right sprite
                gSpriteSheetTexture.render( SCREEN_WIDTH - gSpriteClips[ 1 ].w, 0, &gSpriteClips[ 1 ] );
                //Render bottom left sprite
                gSpriteSheetTexture.render( 0, SCREEN_HEIGHT - gSpriteClips[ 2 ].h, &gSpriteClips[ 2 ] );
                //Render bottom right sprite
                gSpriteSheetTexture.render( SCREEN_WIDTH - gSpriteClips[ 3 ].w, SCREEN_HEIGHT - gSpriteClips[ 3 ].h, &gSpriteClips[ 3 ] );
                //Update screen
                SDL_RenderPresent( gRenderer );
            }

Finalmente, no loop principal temos que renderizar a mesma textura 4 vezes, mas renderizamos uma parte diferente da folha em locais diferentes a cada renderização.

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