Продвинутый таймер

preview_13
Вы уже научились тому, как написать простой таймер. В этот раз займемся созданием класса таймера с функциями запуска, остановки и паузы, который вам пригодится при программировании игр.

//Таймер
class Timer
{
    private:
    //Сколько было времени при запуске таймера
    int startTicks;
 
    //Сколько было времени, когда таймер поставили на паузу
    int pausedTicks;
 
    //Статус таймера
    bool paused;
    bool started;
 
    public:
    //Инициализируем переменные
    Timer();
 
    //Действия с часами
    void start();
    void stop();
    void pause();
    void unpause();
 
    //Получить время таймера
    int get_ticks();
 
    //Проверки статуса таймера
    bool is_started();
    bool is_paused();
};

Здесь мы можем наблюдать краткое описание нашего класса.

startTicks — точка отсчета времени, а pausedTicks — время, когда таймер поставили на паузу.

Конструктор инициализирует переменные, и я вполне уверен, что вы прекрасно представляете что делают start(), stop(), pause() и unpause().

Функция get_ticks() возвращает время таймера в миллисекундах. is_started() проверяет запущен ли таймер, а is_paused() поставлен ли он на паузу.

Timer::Timer()
{
    //Инициализировать переменные
    startTicks = 0;
    pausedTicks = 0;
    paused = false;
    started = false;
}

Здесь в нашем конструкторе, мы инициализируем переменные. Тут особо нечего объяснять.

void Timer::start()
{
    //Запустить таймер
    started = true;
 
    //Снять с паузы
    paused = false;
 
    //Запомнить текущее время
    startTicks = SDL_GetTicks();
}

Теперь когда мы запускаем таймер, мы выставляем статус started в true, снимаем его с паузы и делаем время запуска равным текущему времени при помощи SDL_GetTicks().

void Timer::stop()
{
    //Остановить таймер
    started = false;
 
    //Снять таймер с паузы
    paused = false;
}

Когда мы останавливаем его, мы выставляем статус started в false и, опять-таки, снимаем с паузы.

int Timer::get_ticks()
{
    //Если таймер запущен
    if( started == true )
    {
        //Если таймер на паузе
        if( paused == true )
        {
            //Возвращаем время работы таймера до паузы
            return pausedTicks;
        }
        else
        {
            //Возвращаем текущее время минус время запуска
            return SDL_GetTicks() - startTicks;
        }
    }
 
    //Таймер не запущен
    return 0;
}

Итак, функция возвращающая значение таймера.
Для начала проверяем, запущен ли таймер. Если да, проверяем не стоит ли он на паузе.
Если он на паузе, возвращаем время работы до паузы. Мы поговорим о паузах позже.
Если таймер не на паузе, вы возвращаем разницу во времени между запуском и текущим моментом времени. Как вы можете заметить, формула такая же, как и в предыдущем уроке.
Если таймер не запускался возвращаем ноль.

void Timer::pause()
{
    //Если таймер запущен и еще на паузе
    if( ( started == true ) && ( paused == false ) )
    {
        //Поставить на паузу
        paused = true;
 
        //Вычислить время работы таймера до паузы
        pausedTicks = SDL_GetTicks() - startTicks;
    }
}

Теперь, когда мы хотим поставить таймер на паузу, для начала мы проверяем запущен ли таймер и не на паузе ли он уже.
Если мы можем поставить таймер на паузу, мы меняем статус paused на true и сохраняем время работы таймера в pausedTicks.

void Timer::unpause()
{
    //Если таймер на паузе
    if( paused == true )
    {
        //Снять с паузы
        paused = false;
 
        //Скинуть время запуска
        startTicks = SDL_GetTicks() - pausedTicks;
 
        //Обнулить время работы до паузы
        pausedTicks = 0;
    }
}

Когда мы хотим снять таймер с паузы, мы проверяем был ли таймер на паузе до этого.
Если был, меняем статус paused на false, и затем изменяем время запуска на текущее минус время работы до паузы.
В конце концов обнуляем pausedTicks, впрочем без особой на то причины, по сути лишь потому что мне не нравятся бессвязные переменные.

bool Timer::is_started()
{
    return started;
}
 
bool Timer::is_paused()
{
    return paused;
}

Функции проверяющие статус таймера. Я вполне уверен что они очевидны.

    //Создаем таймер
    Timer myTimer;
 
    //Генерируем сообщения для поверхностей
    startStop = TTF_RenderText_Solid(
                    font,
                    "Press S to start or stop the timer",
                    textColor);
    pauseMessage = TTF_RenderText_Solid(
                       font,
                       "Press P to pause or unpause the timer",
                       textColor);

Теперь, в функции main после инициализации и загрузки файлов, мы объявляем объект таймера и рендерим поверхности с сообщениями.

    //Запустить таймер
    myTimer.start();
 
    //Пока пользователь не захотел выйти
    while( quit == false )
    {

Преждем чем начать главный цикл, запускаем таймер.

        //Пока есть события для обработки
        while( SDL_PollEvent( &event ) )
        {
            //Если нажали клавишу
            if( event.type == SDL_KEYDOWN )
            {
                //Если нажали s
                if( event.key.keysym.sym == SDLK_s )
                {
                    //Если таймер запущен
                    if( myTimer.is_started() == true )
                    {
                        //Остановить таймер
                        myTimer.stop();
                    }
                    else
                    {
                        //Запустить таймер
                        myTimer.start();
                    }
                }

В этом месте мы обрабатываем нажатия клавиш. При нажатии «s», если таймер запущен останавливаем его, а в противном случае запускаем.

                //Если нажали p
                if( event.key.keysym.sym == SDLK_p )
                {
                    //Если таймер на паузе
                    if( myTimer.is_paused() == true )
                    {
                        //Снять с паузы
                        myTimer.unpause();
                    }
                    else
                    {
                        //Поставить на паузу
                        myTimer.pause();
                    }
                }
            }

При нажатии на «p», если таймер на паузе снимаем его с нее, а иначе ставим.

        //Строка для времени таймера
        std::stringstream time;
 
        //Преобразовываем время таймера в строку
        time << "Timer: " << myTimer.get_ticks() / 1000.f;
 
        //Рендерим поверхность
        seconds = TTF_RenderText_Solid( font, time.str().c_str(), textColor );
 
        //Отображаем ее
        apply_surface( ( SCREEN_WIDTH - seconds->w ) / 2, 0, seconds, screen );
 
        //Освобождаем память
        SDL_FreeSurface( seconds );
 
        //Обновляем экран
        if( SDL_Flip( screen ) == -1 )
        {
            return 1;
        }
    }

После обработки событий и отрисовки фона и сообщений, мы показываем время таймера на экране
Сначала мы создаем строковой поток, затем кладем в него время таймера. Так как мы хотим время в секундах, мы делим значение времени таймера на 1000, постольку поскольку оно хранится в миллисекундах.

Дальше мы создаем поверхность из строки со временем. И потом отображаем новую поверхность на экране, после чего освобождаем память из-под нее. Далее обновляем экран и продолжаем главный цикл.


Скачать исходники и материалы

Добавить комментарий