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

//Таймер
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, постольку поскольку оно хранится в миллисекундах. Дальше мы создаем поверхность из строки со временем. И потом отображаем новую поверхность на экране, после чего освобождаем память из-под нее. Далее обновляем экран и продолжаем главный цикл.

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