Tutorial de SDL – Parte 26 – Movimento

Continuando nossa série de artigo traduzido do site lazyfoo, agora que sabemos como renderizar, lidar com entrada de dados e gerenciar o tempo, agora sabemos tudo que é necessário para mover coisas pela tela.

//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
 void render();
 private:
 //The X and Y offsets of the dot
 int mPosX, mPosY;
 //The velocity of the dot
 int mVelX, mVelY;
};

Aqui temos a classe para o ponto que iremos mover pela tela. Ela possui constantes que definem suas dimensões e velocidade. Possui um construtor, um manipulador de eventos, uma função para mover cada frame, e uma função de renderização. Como dados, possui variáveis para a posição x e y e velocidade em x e y.

Dot::Dot()
{
 //Initialize the offsets
 mPosX = 0;
 mPosY = 0;
 //Initialize the velocity
 mVelX = 0;
 mVelY = 0;
}

O construtor simplesmente inicializa variáveis.

void Dot::handleEvent( SDL_Event& e )
{
 //If a key was pressed
 if( e.type == SDL_KEYDOWN && e.key.repeat == 0 )
 {
 //Adjust the velocity
 switch( e.key.keysym.sym )
 {
 case SDLK_UP: mVelY -= DOT_VEL; break;
 case SDLK_DOWN: mVelY += DOT_VEL; break;
 case SDLK_LEFT: mVelX -= DOT_VEL; break;
 case SDLK_RIGHT: mVelX += DOT_VEL; break;
 }
 }

Em nosso manipulador de eventos, iremos ajustar a velocidade baseado no pressionamento de algumas teclas.
Você pode estar se perguntando por quê nós não simplesmente aumentamos a posição quando pressionamos uma tecla. Se estivéssemos dizendo para adicionar um valor à posição x a cada vez que pressionarmos a tecla seta para direita, teríamos que repetidamente pressionar essa tecla para continuar movendo o ponto. Ajustando a velocidade, temos que pressiona a tecla uma única vez.
Se você está se perguntando por quê estamos checando se a repetição da tecla é 0, é porque repetição de tecla é ativa por padrão e se você pressionar e segurar um tecla, será reportado múltiplos pressionamentos de tecla. Isso significa que temos que checar se o pressionamento da tecla é o primeiro pois nós nos importamos apenas com essa primeira vez.
Para aqueles que ainda não estudaram física, velocidade é a rapidez/direção de um objeto. Se um objeto está se movendo para a direita a 10 pixels por frame, ele possui uma velocidade de 10. Se está se movendo para a esquerda a 10 pixels por frame, ele possui uma velocidade de -10. Se a velocidade do ponto é 10, isso significa que após 10 frames ele terá se movido 100 pixels nessa direção.

 //If a key was released
 else if( e.type == SDL_KEYUP && e.key.repeat == 0 )
 {
 //Adjust the velocity
 switch( e.key.keysym.sym )
 {
 case SDLK_UP: mVelY += DOT_VEL; break;
 case SDLK_DOWN: mVelY -= DOT_VEL; break;
 case SDLK_LEFT: mVelX += DOT_VEL; break;
 case SDLK_RIGHT: mVelX -= DOT_VEL; break;
 }
 }
}

Quando soltamos uma tecla, temos que desfazer a mudança de velocidade para o valor anterior a quando ela foi pressionada pela primeira vez. Quando pressionamos a tecla seta para direita, adicionamos o valor de x à velocidade. Quando soltamos essa tecla, subtraímos x à velocidade para retornar para 0.

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 > SCREEN_WIDTH ) )
 {
 //Move back
 mPosX -= mVelX;
 }

Aqui temos a função que chamamos a cada frame para mover o ponto.
Primeiro movemos o ponto pelo eixo x baseado em sua velocidade x. Depois  checamos se o ponto se moveu para fora da tela. Caso afirmativo, desfazemos o movimento  realizado através do eixo x.

 //Move the dot up or down
 mPosY += mVelY;
 //If the dot went too far up or down
 if( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > SCREEN_HEIGHT ) )
 {
 //Move back
 mPosY -= mVelY;
 }
}

Em seguida fazemos o mesmo para o eixo y.

void Dot::render()
{
 //Show the dot
 gDotTexture.render( mPosX, mPosY );
}

Na função de renderização, renderizamos a textura do ponto na posição dele.

 //Main loop flag
 bool quit = false;
 //Event handler
 SDL_Event e;
 //The dot that will be moving around on the screen
 Dot dot;

Antes de entrarmos no loop principal declaramos um objeto da classe do ponto.

 //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;
 }
 //Handle input for the dot
 dot.handleEvent( e );
 }
 //Move the dot
 dot.move();
 //Clear screen
 SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
 SDL_RenderClear( gRenderer );
 //Render objects
 dot.render();
 //Update screen
 SDL_RenderPresent( gRenderer );
 }
Finalmente, usamos nosso ponto no loop principal. No loop de eventos lidamos com os eventos do ponto. Depois de atualizamos a posição do ponto e em seguida renderizamos ele na tela.
Nesse artigo, nós baseamos a velocidade como a quantidade de pixels movidos por frame. Na maioria dos jogos, a velocidade é calculada por segundos. A razão pela qual fizemos aqui por frame é por ser mais fácil, mas se você souber física não deve ser difícil atualizar a posição do ponto baseado no tempo. Se você não tiver conhecimento de física, então fique com a velocidade calculada por frame por enquanto.