Наверняка, каждому да приходила идея написать какую-нибудь графическую игру или программу. Но не всегда есть время или желание осилить графику в программировании. Надеюсь, это статья поможет вам научиться писать(или просто написать) простую, но очень забавную игру.
Это будет немалоизвестная Flood-It. Идея крайне проста:
Требуется закрасить все поле в один цвет, при том, что красить можно только всю часть одного цвета, прилегающую к левому верхнему квадрату.
Каждая программа на SDL должна начинаться с очень важного дела — инициализации нужных подсистем и задания основных параметров. В нашем примере нам понадобится проинициализировать только видео.
Сей процесс проходит с помощью команды SDL_Init
которая принимает в качестве параметра константы(разделенные логическим «или»), отвечающие за ту или иную часть. Так, для видео-подсистемы нужно написать
Если вам нужные другие системы можно воспользоваться этим списком:
Ох, если б и тут все было так просто. На самом деле не всегда это может завершиться успешно, и нужно аварийно завершить выполнение в случае неудачи. И для этого нужно всего-лишь следить за значением, возвращаемым SDL_Init — в случае неудачи всегда возвращается отрицательное число.
Не слишком информативно. Пользователям(и нам) полезно знать, что же привело к проблеме. SDL_GetError() всегда вернет текст проблемы. Воспользовавшись этой функцией сделаем сообщение об ошибках приятнее
Ну вот, теперь, когда мы готовы начать творить, надо разобраться, как это происходит. Изображения хранятся на поверхностях(surface'ах), с которыми можно делать много полезных вещей — накладывать друг на друга, рисовать на них и так далее.
Для видимой на экране части также потребуется поверхность. Задать эту поверхность нетрудно, для этого есть команда SDL_SetVideoMode, которая установит размер
этой поверхности(а значит, и экрана программы), количество бит на цвет, а так же всякие важные параметры, включающие аппаратное/программное ускорение и многое другое.
Так же есть один полезный параметр SDL_DOUBLEBUF, добавляющий этой поверхности буфер. Все действия не будут отображаться на экране до необходимого момента. Это неплохо ускорит нашу программу(хоть она и совершенно не требовательна, но так приятнее и культурнее).
Итак, создав поверхность следующим образом
мы опять можем не получить желаемый результат. Снова выведем сообщение об ошибке и завершим выполнение, если поверхности нет.
Теперь, когда мы учли все возможные проблемы, ничего плохо не должно произойти.
Игровое поле 14x14 будет состоять из блоков 6 цветов. Каждый блок — ни что иное, как простая картинка. Расстраивает, но SDL может работать только с изображениями формата BMP. Впрочем, это проблема решается подключением библиотеки SDL_Image, но в нашем примере у нас нет колоссальных объемов изображений, и нам хватит BMP.
Загрузить изображение на поверхность можно подобным образом:
И если в процессе загрузки изображений что-то пойдет не так, то возвращено будет отрицательное значение(а дальше-то мы знаем, что делать).
Изображение после загрузки представлено уже известным нам типом — surface. Поэтому хранить блоки мы будет в массиве поверхностей block. Похоже, у нас есть есть все, чтобы вывести игровое поле. Кроме одного… Есть поверхность с изображением, есть поверхность экрана, но как перенести на screen другую поверхность(или часть)?
Для этих целей есть SDL_BlitSurface, который может переписать с одной поверхности прямоугольник с координатами x1, y1 и размерами w, h на другую поверхность в место x2, y2. Как-то многовато параметров, да к тому же координаты передаются не простыми числами. Раз выводить мы будем все только на экранную поверхность, то напишем небольшую процедуру
Разберем передаваемые параметры:
img — поверхность, часть которой мы будем добавлять. X, Y — координаты, на которые нужно вывести. SX, SY, W, H — параметры выводимого прямоугольника(координаты угла и размер). Волшебство передаваемых значений заключается в том, что координаты должны быть заданы в типе SDL_Rect(от rectangle), который имеет 4 значения — X, Y, W, H. Ну и после задания координат в новом типе выполняется накладывание.
Теперь точно самое время создать и, главное, нарисовать игровое поле. Создадим процедурку генерации поля GenMap, которая заполняет map случайными числами от 0 до 5(обозначающими цвет).
Добавим ещё процедуру, выводящую по координатам блока изображение на законное место(для блока X, Y им будет являться прямоугольник с 30 * X, 30 * Y, по 30 * (X + 1), 30 * ( Y + 1), для блоков размером 30x30 пикселей).
А заодно сразу и все поле создадим
Время проверить, что у нас вышло. Генерируем поле, выводим, и… Ничего! Как же так? Вроде все правильно, но… Буфер экрана, не стоит забывать его выводить. Это делается просто
Именно SDL_Flip выполнит нужное действие со screen. И теперь можно увидеть результат:
Отлично, все работает.
На этом, пожалуй, стоит остановиться в этой статье. Продолжение здесь.
Это будет немалоизвестная Flood-It. Идея крайне проста:
Требуется закрасить все поле в один цвет, при том, что красить можно только всю часть одного цвета, прилегающую к левому верхнему квадрату.
Инициализация
Каждая программа на SDL должна начинаться с очень важного дела — инициализации нужных подсистем и задания основных параметров. В нашем примере нам понадобится проинициализировать только видео.
Сей процесс проходит с помощью команды SDL_Init
которая принимает в качестве параметра константы(разделенные логическим «или»), отвечающие за ту или иную часть. Так, для видео-подсистемы нужно написать
SDL_Init(SDL_INIT_VIDEO)
Если вам нужные другие системы можно воспользоваться этим списком:
- SDL_INIT_AUDIO
- SDL_INIT_VIDEO
- SDL_INIT_TIMER
- SDL_INIT_CDROM
- SDL_INIT_JOYSTICK
- SDL_INIT_EVERYTHING — инициализация всех подсистем
- SDL_INIT_NOPARACHUTE — не ловить фатальные сигналы
- SDL_INIT_EVENTTHREAD — запускать менеджер событий в отдельном потоке
Ох, если б и тут все было так просто. На самом деле не всегда это может завершиться успешно, и нужно аварийно завершить выполнение в случае неудачи. И для этого нужно всего-лишь следить за значением, возвращаемым SDL_Init — в случае неудачи всегда возвращается отрицательное число.
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "Unable to init SDL"); return 1; }
Не слишком информативно. Пользователям(и нам) полезно знать, что же привело к проблеме. SDL_GetError() всегда вернет текст проблемы. Воспользовавшись этой функцией сделаем сообщение об ошибках приятнее
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "Unable to init SDL: %s", SDL_GetError()); return 1; }
Поверхности
Ну вот, теперь, когда мы готовы начать творить, надо разобраться, как это происходит. Изображения хранятся на поверхностях(surface'ах), с которыми можно делать много полезных вещей — накладывать друг на друга, рисовать на них и так далее.
Для видимой на экране части также потребуется поверхность. Задать эту поверхность нетрудно, для этого есть команда SDL_SetVideoMode, которая установит размер
этой поверхности(а значит, и экрана программы), количество бит на цвет, а так же всякие важные параметры, включающие аппаратное/программное ускорение и многое другое.
Так же есть один полезный параметр SDL_DOUBLEBUF, добавляющий этой поверхности буфер. Все действия не будут отображаться на экране до необходимого момента. Это неплохо ускорит нашу программу(хоть она и совершенно не требовательна, но так приятнее и культурнее).
Итак, создав поверхность следующим образом
SDL_Surface * screen = SDL_SetVideoMode(550, 420, 16, SDL_HWSURFACE | SDL_DOUBLEBUF); /// 550x420 — таким будет размер нашего окна. А цвет будет 16-битным. Видео с аппаратным ускорением из-за параметра SDL_HWSURFACE // Если не поддерживается аппаратное ускорение, то может SDL_SWSURFACE(программное ускорение)
мы опять можем не получить желаемый результат. Снова выведем сообщение об ошибке и завершим выполнение, если поверхности нет.
If (!screen) { printf("Can't set videomode: %s", SDL_GetError()); return 1; }
Изображения
Теперь, когда мы учли все возможные проблемы, ничего плохо не должно произойти.
Игровое поле 14x14 будет состоять из блоков 6 цветов. Каждый блок — ни что иное, как простая картинка. Расстраивает, но SDL может работать только с изображениями формата BMP. Впрочем, это проблема решается подключением библиотеки SDL_Image, но в нашем примере у нас нет колоссальных объемов изображений, и нам хватит BMP.
Загрузить изображение на поверхность можно подобным образом:
int InitImages() { for (int i = 0; i < 6; i++) // У нас будет 6 цветов { char path[20]; for (int j = 0; j < 20; j++) path[j] = 0; sprintf(path, "Block%d.bmp", i); block[i] = SDL_LoadBMP(path); // Загружаем BMP по заданному пути в block[i] if (!block[i]) return -1; } return 1; }
И если в процессе загрузки изображений что-то пойдет не так, то возвращено будет отрицательное значение(а дальше-то мы знаем, что делать).
Изображение после загрузки представлено уже известным нам типом — surface. Поэтому хранить блоки мы будет в массиве поверхностей block. Похоже, у нас есть есть все, чтобы вывести игровое поле. Кроме одного… Есть поверхность с изображением, есть поверхность экрана, но как перенести на screen другую поверхность(или часть)?
Для этих целей есть SDL_BlitSurface, который может переписать с одной поверхности прямоугольник с координатами x1, y1 и размерами w, h на другую поверхность в место x2, y2. Как-то многовато параметров, да к тому же координаты передаются не простыми числами. Раз выводить мы будем все только на экранную поверхность, то напишем небольшую процедуру
void DrawImage(SDL_Surface * img, int x, int y, int w, int h, int sx, int sy) { SDL_Rect desc; // координаты, куда нужно наложить часть. desc.x = x; desc.y = y; SDL_Rect src; // накладываемый прямоугольник. src.x = sx; src.y = sy; src.w = w; src.h = h; SDL_BlitSurface(img, &src, screen, &desc); }
Разберем передаваемые параметры:
img — поверхность, часть которой мы будем добавлять. X, Y — координаты, на которые нужно вывести. SX, SY, W, H — параметры выводимого прямоугольника(координаты угла и размер). Волшебство передаваемых значений заключается в том, что координаты должны быть заданы в типе SDL_Rect(от rectangle), который имеет 4 значения — X, Y, W, H. Ну и после задания координат в новом типе выполняется накладывание.
Теперь точно самое время создать и, главное, нарисовать игровое поле. Создадим процедурку генерации поля GenMap, которая заполняет map случайными числами от 0 до 5(обозначающими цвет).
void GenMap() { for (int i = 0; i < maps; i++) for (int j = 0; j < maps; j++) map[i][j] = rand() % 6; }
Добавим ещё процедуру, выводящую по координатам блока изображение на законное место(для блока X, Y им будет являться прямоугольник с 30 * X, 30 * Y, по 30 * (X + 1), 30 * ( Y + 1), для блоков размером 30x30 пикселей).
void DrawBlock(int x, int y) { DrawImage(block[map[x][y]], 30 * x, 30 * y, 30, 30, 0, 0); // блок в координаты 30*x, 30*y размером 30x30 }
А заодно сразу и все поле создадим
void DrawMap() { for (int i = 0; i < maps; i++) // maps(map size) — размер игрового поля. for (int j = 0; j < maps; j++) DrawBlock(i, j); }
Время проверить, что у нас вышло. Генерируем поле, выводим, и… Ничего! Как же так? Вроде все правильно, но… Буфер экрана, не стоит забывать его выводить. Это делается просто
GenMap(); DrawMap(); SDL_Flip(screen);
Именно SDL_Flip выполнит нужное действие со screen. И теперь можно увидеть результат:
Отлично, все работает.
На этом, пожалуй, стоит остановиться в этой статье. Продолжение здесь.