От переводчика:
Это продолжение серии переводов туториалов от Twinklebear, в оригинале доступных тут. Перевод отчасти вольный и может содержать незначительные поправки или дополнения от переводчика. Перевод первых двух уроков — за авторством InvalidPointer. Рисунки из оригинальной статьи локализованы с максимальным сохранением стиля автора.
Список уроков:
- Урок 1. Hello World!
- Урок 2. Не запихивайте все в main
- Урок 3. Библиотеки-расширения SDL
- Урок 4. Обработка событий
- Уроки 5-6 — To be continued
Обработка событий
В этом уроке мы изучим основы получения пользовательского ввода, а для простоты примера будем воспринимать любое действие пользователя как попытку
Но для начала, давайте поменяем картинку по центру экрана, чтобы пользователю, впервые увидевшему вашу программу, было понятно, что она делает, и что требуется от него.
Человечек автора сохранён
Простейший главный цикл
Мы добавим в программу главный цикл, который заставит программу работать, пока пользователь не захочет выйти (и не сообщит об этом программе в доступной ей форме, само собой), вместо фиксированной задержки, как это было в предыдущих уроках. Вот приблизительная структура такого цикла:
while (!quit) // Пока не вышли, делаем следующее:
{
// Считываем пользовательский ввод и обрабатываем его
// Рисуем что-нибудь на экране
}
Очередь событий SDL
Чтобы правильно использовать систему событий SDL, нам понадобится хотя бы минимальное представление о её функционировании. Когда SDL получает событие от операционной системы, оно помещает его в конец очереди, после всех остальных событий, которые были получены ранее, но ещё не были извлечены оттуда программой. Если бы после запуска программы мы бы поочерёдно изменили размер окна, щёлкнули по нему мышью и нажали на какую-нибудь клавишу, то очередь событий выглядела бы так:
При вызове SDL_PollEvent мы получаем событие из начала очереди, самое старое из оставшихся. Получение событий из очереди при помощи SDL_PollEvent удаляет их оттуда, чтобы этого избежать, можно «подглядеть» событие, воспользовавшись функцией SDL_PeepEvents с установленным флагом SDL_PEEKEVENT. Подробнее об этой функции можно прочитать в документации, в рамках этой статьи она не потребуется (и вам, скорее всего, тоже) и потому рассматриваться не будет.
Обработка событий
В главном цикле мы хотим получать все доступные события, пришедшие после отрисовки предыдущего кадра, и обрабатывать их. Чтобы это сделать, достаточно поместить SDL_PollEvent в условие цикла while, поскольку он возвращает 1, если она получила событие, и 0, если получать нечего. Раз уж всё, что делает программа — это завершает свою работу при определённых событиях, достаточно будет использовать булевскую переменную (bool quit), обозначающую, хотим мы закончить работу программы или нет, и установить её значение в истинное при получении этих событий.
// e - это переменная типа SDL_Event, которую стоит объявить до главного цикла
while (SDL_PollEvent(&e))
{
// Если пользователь попытался закрыть окно
if (e.type == SDL_QUIT)
{
quit = true;
}
// Если пользователь нажал клавишу на клавиатуре
if (e.type == SDL_KEYDOWN)
{
quit = true;
}
// Если пользователь щёлкнул мышью
if (e.type == SDL_MOUSEBUTTONDOWN)
{
quit = true;
}
}
Этот цикл следует разместить внутри главного цикла приложения.
Событие типа SDL_QUIT происходит, когда пользователь закрывает окно, SDL_KEYDOWN — когда нажата клавиша на клавиатуре (и приходит много-много раз, пока она удерживается, подобно тому, как повторяются буквы при удержании клавиши во время печати текста), а событие типа SDL_MOUSEBUTTONDOWN — при нажатии клавиши мыши. Это лишь некоторые из событий, которые может получить ваше приложение — а всего SDL может получить более 20 типов событий, покрыть которые эта статья, само собой, не в силах, поэтому о них стоит почитать в документации к SDL_Event
Завершение главного цикла
С обработкой событий закончили, но в главном цикле не хватает ещё одной части — отображения сцены. Эту тему мы уже рассмотрели в предыдущих уроках, осталось лишь применить эти знания, и главный цикл примет следующий вид:
// Структура события
SDL_Event e;
// Флаг выхода
bool quit = false;
while (!quit)
{
// Обработка событий
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
quit = true;
}
if (e.type == SDL_KEYDOWN)
{
quit = true;
}
if (e.type == SDL_MOUSEBUTTONDOWN)
{
quit = true;
}
}
// Отображение сцены
SDL_RenderClear(renderer);
renderTexture(image, renderer, x, y);
SDL_RenderPresent(renderer);
}
Эта программа будет работать вечно. Ну, или, по крайней мере, до тех пор, пока пользователь
Дополнение от переводчика: программа в том виде, в каком она есть, скорее всего, будет потреблять все доступные ей ресурсы процессора, полностью нагружая одно ядро. Будет рациональным добавить в конец главного цикла небольшую задержку (1-5 мс) с помощью SDL_Delay, чтобы при отсутствии событий программа освобождала процессор для других программ. Это не распространяется на тяжёлые игры, которым и так требуются все доступные ресурсы, когда при 100fps тратить 100ms ежесекундно на ожидание — непозволительная роскошь.
Конец четвёртого урока
Вот и подошёл к концу очередной урок. Всем до встречи в уроке 5: Выборка из текстурного атласа
Домашнее задание: попробуйте добавить движение изображения, например, при помощи стрелок.
Synoecium
а что будете делать после перевода 6 урока? Автор оригинала пишет, что забросил эту серию туториалов.
k1-801 Автор
Попробую свои силы в написании оригинальных статей, поближе к теме бекэнда. Выберу что-нибудь интересное из последних наработок.
BerkutEagle
Автор оригинала советует посмотреть сюда lazyfoo.net/tutorials/SDL — есть чего переводить.