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