Tutorial de SDL – Parte 30 – Rolagem

Continuando nossa série de artigo traduzidos do site lazyfoo, agora veremos como rolar a tela para navegar por uma imagem cujo tamanho é maior do que a área visível.

Até agora temos lidado apenas com níveis do tamanho da tela. Com a rolagem você pode navegar através de níveis de qualquer tamanho renderizando qualquer coisa em relação à câmera.
O principio básico da rolagem é que você tem um retângulo que funciona com um câmera:
E então você renderiza apenas o que está na câmera, que normalmente envolve renderizar as coisas em relação à câmera ou apenas mostrar partes do objeto dentro da área da câmera.
//The dimensions of the level
const int LEVEL_WIDTH = 1280;
const int LEVEL_HEIGHT = 960;
//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

Como o nível não é mais do tamanho da tela temos que ter um conjunto separado de constantes para definir o tamanho do nível.

//The dot that will move around on the screen
class Dot
{
 public:
 //The dimensions of the dot
 static const int DOT_WIDTH = 20;
 static const int DOT_HEIGHT = 20;
 //Maximum axis velocity of the dot
 static const int DOT_VEL = 10;
 //Initializes the variables
 Dot();
 //Takes key presses and adjusts the dot's velocity
 void handleEvent( SDL_Event& e );
 //Moves the dot
 void move();
 //Shows the dot on the screen relative to the camera
 void render( int camX, int camY );
 //Position accessors
 int getPosX();
 int getPosY();
 private:
 //The X and Y offsets of the dot
 int mPosX, mPosY;
 //The velocity of the dot
 int mVelX, mVelY;
};

Dessa vez o ponto tem que ser renderizado relativamente à câmera, assim sua função de renderização recebe a posição da câmera.

void Dot::move()
{
 //Move the dot left or right
 mPosX += mVelX;
 //If the dot went too far to the left or right
 if( ( mPosX < 0 ) || ( mPosX + DOT_WIDTH > LEVEL_WIDTH ) )
 {
 //Move back
 mPosX -= mVelX;
 }
 //Move the dot up or down
 mPosY += mVelY;
 //If the dot went too far up or down
 if( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > LEVEL_HEIGHT ) )
 {
 //Move back
 mPosY -= mVelY;
 }
}

Dessa vez quando movemos o ponto, verificamos se o ponto se moveu para fora do nível ao contrário de checar se ele se moveu para fora da tela, já que a tela irá se mover em torno do nível.

void Dot::render( int camX, int camY )
{
 //Show the dot relative to the camera
 gDotTexture.render( mPosX - camX, mPosY - camY );
}

Agora quando renderizarmos objetos na tela, renderizaremos ele relativamente à câmera, pela subtração do deslocamento da câmera.

 //Main loop flag
 bool quit = false;
 //Event handler
 SDL_Event e;
 //The dot that will be moving around on the screen
 Dot dot;
 //The camera area
 SDL_Rect camera = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };

Antes de entrarmos no loop principal, declaramos o ponto e a câmera que iremos seguir.

 //Move the dot
 dot.move();
 //Center the camera over the dot
 camera.x = ( dot.getPosX() + Dot::DOT_WIDTH / 2 ) - SCREEN_WIDTH / 2;
 camera.y = ( dot.getPosY() + Dot::DOT_HEIGHT / 2 ) - SCREEN_HEIGHT / 2;
 //Keep the camera in bounds
 if( camera.x < 0 )
 {
 camera.x = 0;
 }
 if( camera.y < 0 )
 {
 camera.y = 0;
 }
 if( camera.x > LEVEL_WIDTH - camera.w )
 {
 camera.x = LEVEL_WIDTH - camera.w;
 }
 if( camera.y > LEVEL_HEIGHT - camera.h )
 {
 camera.y = LEVEL_HEIGHT - camera.h;
 }

Depois que movemos o ponto, queremos alterar a posição da câmera para centralizá-la em torno do ponto. Não queremos que a câmera vá para fora do nível, então mantemos ele nos limites depois de  movê-la.

 //Clear screen
 SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
 SDL_RenderClear( gRenderer );
 //Render background
 gBGTexture.render( 0, 0, &camera );
 //Render objects
 dot.render( camera.x, camera.y );
 //Update screen
 SDL_RenderPresent( gRenderer );
Depois que a câmera estiver em sua posição, renderizamos a parte do fundo que está dentro da câmera e então renderizamos o ponto em relação à câmera.

Baixe os arquivo de mídia e o código fonte sesse artigo aqui.