Continuando nossa série de artigos traduzido do site lazyfoo, agora que criamos um timer básico, iremos criar um que possa ser iniciado/pausado e parado.
//The application time based timer class LTimer { public: //Initializes variables LTimer(); //The various clock actions void start(); void stop(); void pause(); void unpause(); //Gets the timer's time Uint32 getTicks(); //Checks the status of the timer bool isStarted(); bool isPaused(); private: //The clock time when the timer started Uint32 mStartTicks; //The ticks stored when the timer was paused Uint32 mPausedTicks; //The timer status bool mPaused; bool mStarted; };
Para podermos usar esses novos recursos, iremos criar uma classe para nosso timer. Ela irá ter todas as funções básicas (iniciar/parar/pausar/retomar) e verificação do status. Em termos de atributos, teremos o tempo inicial como antes, uma variável para armazenar o tempo quando pausamos, e flags de status para registrar se o times está em execução ou parado.
LTimer::LTimer() { //Initialize the variables mStartTicks = 0; mPausedTicks = 0; mPaused = false; mStarted = false; }
Nosso construtor inicializa os atributos internos.
void LTimer::start() { //Start the timer mStarted = true; //Unpause the timer mPaused = false; //Get the current clock time mStartTicks = SDL_GetTicks(); mPausedTicks = 0; }
A função start configura as flags started e paused, obtém o tempo inicial do timer e inicializa o tempo de pausa para 0. Para esse timer, se quisermos reinicia-lo apenas chamamos esta função novamente. Como podemos iniciar o timer se ele estiver pausado e/ou em execução, precisamos nos certificar de limpar os dados de paused.
void LTimer::stop() { //Stop the timer mStarted = false; //Unpause the timer mPaused = false; //Clear tick variables mStartTicks = 0; mPausedTicks = 0; }
A função stop basicamente reinicializa todas as variáveis.
void LTimer::pause() { //If the timer is running and isn't already paused if( mStarted && !mPaused ) { //Pause the timer mPaused = true; //Calculate the paused ticks mPausedTicks = SDL_GetTicks() - mStartTicks; mStartTicks = 0; } }
Quando tivermos que pausar o timer, necessitamos verificar se o timer está sendo executado pois faz sentido pausar o timer que não foi iniciado. Se o timer estiver sendo executado, configuramos a flag paused, armazenamos o tempo quando o timer foi pausado em mPausedTicks e resetamos o tempo inicial.
void LTimer::unpause() { //If the timer is running and paused if( mStarted && mPaused ) { //Unpause the timer mPaused = false; //Reset the starting ticks mStartTicks = SDL_GetTicks() - mPausedTicks; //Reset the paused ticks mPausedTicks = 0; } }
Assim, quando retomamos o timer, queremos nos certificar que o timer esteja sendo executado e pausado porque não podemos retomar um timer que esteja parado ou em execução. Configuramos a flag paused e o novo tempo inicial.
Digamos que você inicia o timer quando SDL_GetTicks reprota 5000 ms e em seguida pause ele em 10000 ms. Isso significa que o tempo relativo do tempo de pausa é 5000 ms. Se quisermos retomar o timer quando SDL_GetTicks reprota 20000 ms, o novo tempo inicial será 20000 – 5000 ou 15000 ms. Dessa forma o tempo relativo ainda será 5000 ms do tempo atual retornado por SDL_GetTicks.
Digamos que você inicia o timer quando SDL_GetTicks reprota 5000 ms e em seguida pause ele em 10000 ms. Isso significa que o tempo relativo do tempo de pausa é 5000 ms. Se quisermos retomar o timer quando SDL_GetTicks reprota 20000 ms, o novo tempo inicial será 20000 – 5000 ou 15000 ms. Dessa forma o tempo relativo ainda será 5000 ms do tempo atual retornado por SDL_GetTicks.
Uint32 LTimer::getTicks() { //The actual timer time Uint32 time = 0; //If the timer is running if( mStarted ) { //If the timer is paused if( mPaused ) { //Return the number of ticks when the timer was paused time = mPausedTicks; } else { //Return the current time minus the start time time = SDL_GetTicks() - mStartTicks; } } return time; }
Obter o tempo é um pouco mais complicado, já que nosso timer pode estar sendo executado, pausado ou parado. Se o timer estiver parado, apenas retornados o valor inicial 0. Se o timer estiver pausado, retornamos o tempo armazenado quando ele foi pausado. Se o timer estiver em execução e não pausado, retornamos o tempo relativo à quando ele foi iniciado.
bool LTimer::isStarted() { //Timer is running and paused or unpaused return mStarted; } bool LTimer::isPaused() { //Timer is running and paused return mPaused && mStarted; }
Aqui temos algumas métodos acessórios para verificar o status do timer.
//Main loop flag bool quit = false; //Event handler SDL_Event e; //Set text color as black SDL_Color textColor = { 0, 0, 0, 255 }; //The application timer LTimer timer; //In memory text stream std::stringstream timeText;
Antes de entrarmos no loop principal, declaramos um objeto de timer e uma stream de string para armazenar o valor do tempo como texto.
else if( e.type == SDL_KEYDOWN ) { //Start/stop if( e.key.keysym.sym == SDLK_s ) { if( timer.isStarted() ) { timer.stop(); } else { timer.start(); } } //Pause/unpause else if( e.key.keysym.sym == SDLK_p ) { if( timer.isPaused() ) { timer.unpause(); } else { timer.pause(); } } }
Quando pressionamos a tecla s, verificamos se o timer foi iniciado. Caso afirmativo, paramos ele. Caso não tenha sido, iniciamos. Quando pressionamos a tecla p, verificamos se o timer está pausado. Caso afirmativo, retomamos a execução. Caso contrário, pausamos ele.
//Set text to be rendered timeText.str( "" ); timeText << "Seconds since start time " << ( timer.getTicks() / 1000.f ) ; //Render text if( !gTimeTextTexture.loadFromRenderedText( timeText.str().c_str(), textColor ) ) { printf( "Unable to render time texture!\n" ); } //Clear screen SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF ); SDL_RenderClear( gRenderer ); //Render textures gStartPromptTexture.render( ( SCREEN_WIDTH - gStartPromptTexture.getWidth() ) / 2, 0 ); gPausePromptTexture.render( ( SCREEN_WIDTH - gPausePromptTexture.getWidth() ) / 2, gStartPromptTexture.getHeight() ); gTimeTextTexture.render( ( SCREEN_WIDTH - gTimeTextTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gTimeTextTexture.getHeight() ) / 2 ); //Update screen SDL_RenderPresent( gRenderer );
Antes de renderizar, escrevemos o tempo atual em um stream de string. A razão para dividirmos esse valor por 1000 é por queremos o tempo em segundos.
Após isso renderizamos o texto em uma textura e finalmente desenhamos todas as texturas na tela.
Após isso renderizamos o texto em uma textura e finalmente desenhamos todas as texturas na tela.