Pull to refresh

Игра на SDL — вдвойне легко

Reading time 4 min
Views 25K
Эта статья является продолжением данного поста.
В этот раз мы рассмотрим события нажатия клавиш клавиатуры и мыши, отрисовку текста с помощью готовой библиотеки и руками. Да к тому же получим моральное удовлетворение, дописав игру до конца.


События



Теперь, когда у нас есть вполне работающая отрисовка карты нужно реагировать на действия игрока.
Вообще, каждое действие пользователя(закрытие программы, нажатие клавиш, движение мыши) можно получить с помощью 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);
    }
}



Настало время запустить проект и увидеть, что получилось:



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

Articles