Эта статья является продолжением данного поста.
В этот раз мы рассмотрим события нажатия клавиш клавиатуры и мыши, отрисовку текста с помощью готовой библиотеки и руками. Да к тому же получим моральное удовлетворение, дописав игру до конца.
Теперь, когда у нас есть вполне работающая отрисовка карты нужно реагировать на действия игрока.
Вообще, каждое действие пользователя(закрытие программы, нажатие клавиш, движение мыши) можно получить с помощью SLD_PollEvent. В качестве параметра передается переменная(точнее, её адрес) типа SDL_Event, которая и будет хранить последнее событие. Получив событие, мы должны проверить, нужно ли оно вообще нам. Для начала реализуем важную вещь — закрытие программы по событию выхода. Будем ходить бесконечно по циклу в ожидании нужного нам события, а по получению его выйдем из главного цикла.
Запустим и проверим результат. Теперь окно будет закрываться на события завершения(ALT+F4 и соответствующая кнопка окна программы). Но, тут мы не учли одну важную вещь. Если у нас нет события, то программа будет бесполезно тратить процессорное время, ходя по циклу:

Вместо этого будем просто ожидать событие:
Тем самым очень сильно снизив потребности игры.
Результат существенен:

Реализуем событие нажатия кнопки мыши. Игрок сможет, нажимая на любой блок, выбрать необходимый ему цвет. Блоки лежат от в координатах от 0 до 30 * maps. Поэтому абы какие клики нас не интересуют.
Процедура AddColor представляет из себя поиск в глубину с отрисовкой каждой измененной клетки с помощью DrawBlock и изменением счетчика количества ходов на 1, поэтому она не представляет для нас особого интереса.
Реализовать ход по заданной клавише не сложнее, но придется рассмотреть каждую кнопку отдельно:
Не хватает только вывода количества ходов.
Стандартно в SDL не реализована поддержка текста. И тут есть 2 варианта, как поступить:
1) Сделать все сам
2) Воспользоваться библиотекой SDL_ttf.h
Первый способ очевиден — букву можно также нарисовать в виде изображения и выводить нужную, как с блоками. Заготовки можно хранить по-разному. Либо каждую букву в отдельном изображении, либо создать один большой рисунок и выводить кусок, соответствующей нужной букве.

В этом случае вам понадобится SDL_SetColorKey, который установит выбранный на поверхности цвет прозначным(на данном рисунке — 255 0 255)
Цвет (255, 0, 255) теперь на поверхности img будет являться прозрачным.
Какой способ выбирать — решать вам. В этом примере я выбрал второй способ, как менее трудоемкий. Но стоит учесть, что этот способ и более медленный.
Он также несколько удобнее, позволяет каждый раз задавать цвет текста без лишних хлопот. Потребуется только шрифт формата TTF.
Для начала нужно поступить как и с SDL — проинициализировать. На этот раз никакие параметры не потребуются.
Код вывода текста же следующий:
Теперь можно рассмотреть реализацию DrawStep
Во-первых она использует процедуру CleanImage(), которая будет зачищать область с заданными размерами для дальнейшего вывода текста туда.
Настало время запустить проект и увидеть, что получилось:

На этом все, вы можете сыграть пару десятков, сотен раз или дополнить что-то.
Исходники, которые содержат небольшие дополнения в коде, вы можете сказать здесь.
В этот раз мы рассмотрим события нажатия клавиш клавиатуры и мыши, отрисовку текста с помощью готовой библиотеки и руками. Да к тому же получим моральное удовлетворение, дописав игру до конца.
События
Теперь, когда у нас есть вполне работающая отрисовка карты нужно реагировать на действия игрока.
Вообще, каждое действие пользователя(закрытие программы, нажатие клавиш, движение мыши) можно получить с помощью SLD_PollEvent. В качестве параметра передается переменная(точнее, её адрес) типа SDL_Event, которая и будет хранить последнее событие. Получив событие, мы должны проверить, нужно ли оно вообще нам. Для начала реализуем важную вещь — закрытие программы по событию выхода. Будем ходить бесконечно по циклу в ожидании нужного нам события, а по получению его выйдем из главного цикла.
while (!done) { while (SDL_PollEvent(&event)) // Пока есть хоть одно необработанное событие { swtich(event.type) { case SDL_QUIT: // Событие выхода done = true; } } }
Запустим и проверим результат. Теперь окно будет закрываться на события завершения(ALT+F4 и соответствующая кнопка окна программы). Но, тут мы не учли одну важную вещь. Если у нас нет события, то программа будет бесполезно тратить процессорное время, ходя по циклу:

Вместо этого будем просто ожидать событие:
while (!done) { SDL_WaitEvent(&event); // Проверка события }
Тем самым очень сильно снизив потребности игры.
Результат существенен:

Реализуем событие нажатия кнопки мыши. Игрок сможет, нажимая на любой блок, выбрать необходимый ему цвет. Блоки лежат от в координатах от 0 до 30 * maps. Поэтому абы какие клики нас не интересуют.
case SDL_MOUSEBUTTONDOWN: { if (event.button.button = SDL_BUTTON_LEFT) { int x = event.button.x, y = event.button.y; // Координаты клика if (x < 30 * maps && y < 30 * maps) AddColor(map[x / 30][y / 30]); DrawStep(); // Процедура отрисовки количества ходов, к которой мы вернемся позднее. SDL_Flip(screen); } break; }
Процедура AddColor представляет из себя поиск в глубину с отрисовкой каждой измененной клетки с помощью DrawBlock и изменением счетчика количества ходов на 1, поэтому она не представляет для нас особого интереса.
Реализовать ход по заданной клавише не сложнее, но придется рассмотреть каждую кнопку отдельно:
case SDL_KEYDOWN: { switch(event.key.keysym.sym) { case SDLK_ESCAPE: // Выход из программы по нажатию Esc { done = true; break; } case SDLK_b: // Клавиша B { AddColor(COLOR_BLUE), DrawStep(), SDL_Flip(screen); // COLOR_BLUE — define номера цвета break; } case SDLK_g: { AddColor(COLOR_GREEN), DrawStep(), SDL_Flip(screen); break; } case SDLK_o: { AddColor(COLOR_ORANGE), DrawStep(), SDL_Flip(screen); break; } case SDLK_r: { AddColor(COLOR_RED), DrawStep(), SDL_Flip(screen); break; } case SDLK_v: { AddColor(COLOR_VIOLETT), DrawStep(), SDL_Flip(screen); break; } case SDLK_y: { AddColor(COLOR_YELLOW), DrawStep(), SDL_Flip(screen); } } break; }
Не хватает только вывода количества ходов.
Отрисовка текста
Стандартно в SDL не реализована поддержка текста. И тут есть 2 варианта, как поступить:
1) Сделать все сам
2) Воспользоваться библиотекой SDL_ttf.h
Первый способ очевиден — букву можно также нарисовать в виде изображения и выводить нужную, как с блоками. Заготовки можно хранить по-разному. Либо каждую букву в отдельном изображении, либо создать один большой рисунок и выводить кусок, соответствующей нужной букве.

В этом случае вам понадобится SDL_SetColorKey, который установит выбранный на поверхности цвет прозначным(на данном рисунке — 255 0 255)
SDL_SetColorKey(img, SDL_SRCCOLORKEY, SDL_MapRGB(img->format,255, 0, 255));
Цвет (255, 0, 255) теперь на поверхности img будет являться прозрачным.
Какой способ выбирать — решать вам. В этом примере я выбрал второй способ, как менее трудоемкий. Но стоит учесть, что этот способ и более медленный.
Он также несколько удобнее, позволяет каждый раз задавать цвет текста без лишних хлопот. Потребуется только шрифт формата TTF.
Для начала нужно поступить как и с SDL — проинициализировать. На этот раз никакие параметры не потребуются.
TTF_Init();
Код вывода текста же следующий:
void WriteText(int x, int y, char text[100], int sz, int r, int g, int b) { SDL_Color clr; // Тип для цвета. 4 числа — R, G, B и A, соответственно. clr.r = r; clr.g = g; // Зададим параметры цвета clr.b = b; TTF_Font * fnt = TTF_OpenFont("courier.ttf", sz); // Загружаем шрифт по заданному адресу размером sz SDL_Rect dest; dest.x = x; dest.y = y; SDL_Surface * TextSurface = TTF_RenderText_Blended(fnt, text, clr); // Переносим на поверхность текст с заданным шрифтом и цветом SDL_BlitSurface(TextSurface, NULL, screen, &dest); SDL_FreeSurface(TextSurface); // Освобождаем память уже ненужной поверхности TTF_CloseFont(fnt); // Закрываем шрифт }
Теперь можно рассмотреть реализацию DrawStep
Во-первых она использует процедуру CleanImage(), которая будет зачищать область с заданными размерами для дальнейшего вывода текста туда.
void CleanImage(int x, int y, int w, int h) { SDL_Rect desc; desc.x = x; desc.y = y; desc.w = w; desc.h = h; SDL_FillRect(screen, &desc, 0); // Заполняем область черным цветом(ему соответствует 0) } void DrawStep() { CleanImage(430, 50, 150, 100); for (int i = 0; i < 100; i++) tmpch[i] = 0; if (FinishGame()) // Заполнено ли все поле одним цветом { CleanImage(430, 50, 150, 20); for (int i = 0; i < 100; i++) tmpch[i] = 0; if (step <= 25) // Если число ходом не больше 25 sprintf(tmpch, "You win!"), WriteText(430, 50, tmpch, 20, 0, 255, 0); // цвет - (0, 255, 0) else sprintf(tmpch, "Not bad!"), WriteText(430, 50, tmpch, 20, 255, 255, 0); for (int i = 0; i < 100; i++) tmpch[i] = 0; sprintf(tmpch, "Result:%d", step); WriteText(430, 80, tmpch, 20, 255, 255, 0); step = 0; GenMap(); // Обнуляем количество шагов, заново создает поле и выводим его DrawMap(); } else { sprintf(tmpch, "%d%s", step, "/25"); if (step <= 25) WriteText(430, 50, tmpch, 20, 0, 255, 0); // Текст зеленый, пока число ходов не больше 25 else WriteText(430, 50, tmpch, 20, 255, 0, 0); } }
Настало время запустить проект и увидеть, что получилось:

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