Графика с использованием SDL. Урок 1. Начало работы с SDL.

Marius Andra, “GFX with SDL. Lesson 1: Getting started with SDL”, public translation into Russian from English More about this translation.

Translate into another language.

Приветствую Вас на первом уроке серии "Графика с использованием SDL".

Подключаем SDL в Dev-C++

Первое, что необходимо сделать - это скачать несколько файлов. Они находятся в ZIP-архиве sdlDevCPP-1.2.4.zip. Распакуйте этот архив в директорию Dev-C++ так, чтобы файлы из папок lib и include оказались в соответствующих папках. Например, в моей системе Dev-C++ установлен в директорию c:\Dev-C++. Таким образом после распаковки архива файлы libSDL.a, libSDL.la, libSDLmain.a и SDL.dll появились в каталоге c:\Dev-C++\lib, а множество файлов с расширением .h - в каталоге c:\Dev-C++\include\SDL.

Теперь нужно создать новый консольный проект (console project). В его свойствах (их можно найти в меню Project) в поле "Further object files or linker options:" нужно вписать "-lmingw32 -lSDLmain -lSDL" (без кавычек) и нажать кнопку ОК.

Одно замечание: в Dev-C++ при подключенных к проекту библиотеках SDL функция printf(...) выводит текст не на экран, а в файл stdout.txt.

Вот и все, что касается Dev-C++.

Подключаем SDL в Microsoft Visual C++ 6.0

Для применения SDL в MSVC6 нужно скачать файл SDL-devel-1.2.4-VC6.zip (свежую его версию можно найти на сайте www.libsdl.org). В нём находятся две важные папки - include и lib. Извлеките содержимое папки lib в папку lib вашего MSVC6. В моей системе это C:\Program Files\Microsoft Visual Studio\VC98\Lib. Теперь создайте новую папку с именем SDL в директории include MSVC6 и скопируйте в нее (на моей системе это C:\Program Files\Microsoft Visual Studio\VC98\Include\SDL), а также в саму директорию include (C:\Program Files\Microsoft Visual Studio\VC98\Include) все .h файлы из каталога include ZIP-архива.

Теперь создайте новый пустой проект: выберите "WIN32 Application" (приложение WIN32) и установите флажок напротив пункта 'an empty project' (пустой проект). После этого нужно создать .cpp файл для Вашего проекта. Нажмите File->New и выберите "c++ source file", назовите его main.cpp. В настройках проекта (пункт меню Project->Settings) перейдите на вкладку LINK и в конец списка "Object/library modules" допишите через пробел "sdl.lib sdlmain.lib" (без кавычек). На вкладке C/C++ в выпадающем списке "Use run-time library" выберите пункт "Multithreaded DLL". Вот и всё, ваш MSVC6 готов к работе :).

SDL.dll

Для работы с SDL необходим файл SDL.dll (он включен в архивы для Dev-C++ и MSVC6). Чтобы запускать программы, написанные с использованием библиотеки SDL, следует разместить этот файл в папку %WINDIR%\system (Win95,98,ME) или %WINDIR%\system32 (где %WINDIR% - папка, в которую установлен Windows, обычно это c:\windows), либо можно скопировать его в ту же папку, в которой находится исполняемый файл программы. Если вы хотите поделиться своей SDL-программой с друзьями, вы должны также предоставить им файл SDL.dll. Так что скопируйте этот файл из c:\Dev-C++\lib в c:\windows\system или c:\windows\system32 (для Windows NT, 2000, XP) и распространяйте его вместе с исполняемыми файлами.

Начинаем работу с SDL

Итак, у вас все готово для работы. Единственное, что осталось - это включить заголовочный файл SDL/SDL.h в первых строках программы следующим образом:

#include <SDL/SDL.h>

Инициализирует SDL функция SDL_Init(). SDL_Init() в случае успешного выполнения возвращает значение меньше нуля. Функция принимает всего один параметр: то что нужно инициализировать. Чтобы провести инициализировать изображение, нужно передать в функцию аргумент SDL_INIT_VIDEOб, звук - SDL_INIT_AUDIO. Чтобы инициализировать и изображение, и звук нужно передать в качестве аргумента SDL_INIT_VIDEO|SDL_INIT_AUDIO. Таким образом можно передавать в эту функцию много других значений, разделяя их знаком |. Вот список параметров, которые можно передавать:

SDL_INIT_TIMER

SDL_INIT_AUDIO

SDL_INIT_VIDEO

SDL_INIT_CDROM

SDL_INIT_JOYSTICK

SDL_INIT_NOPARACHUTE

SDL_INIT_EVENTTHREAD

SDL_INIT_EVERYTHING

Так, если мы хотим инициализировать одновременно видео и аудио, нужно ввести:

if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) <0 )

{

printf("Инициализация SDL невозможна: %s\n", SDL_GetError());

return 1;

}

Если возникнет ошибка, функция SDL_GetError() возвратит строку с её описанием.

В конце выполнения программы необходимо отключить режим SDL. Для этого есть функция SDL_Quit(). Она очистит все следы выполнения программы. Если вы её не вызовите, могут возникнуть странные ошибки. Чтобы вызвать SDL_Quit() авоматически используйте:

atexit(SDL_Quit);

Таким образом вам не нужно вызывать SDL_Quit() после каждого return[nr]; в функции main().

В SDL всё является поверхностью (surface). Вы можете рисовать на поверхности, даже рисовать поверхностью по поверхности. Экран также является поверхностью. Поверхность в нашей программе это указатель на структуру SDL_Surface. Чтобы получить поверхность экрана, выполните следующее:

SDL_Surface *screen;

Надеюсь, вы видели когда-нибудь в вашей жизни игры, в которых требовалось установить разрешение экрана. Если нет - БОЛЬШЕ ИГРАЙТЕ В ИГРЫ :). Если вы будете использовать поверхность экрана (запомните, экран - это всего лишь имя указателя на структуру SDL_Surface), как поверхность, на которой можно рисовать, используйте функцию SDL_SetVideoMode();

screen = SDL_SetVideoMode(640, 480, 32,

SDL_HWSURFACE|SDL_DOUBLEBUF);

Первые три параметра: ширина (width), высота (height) и количество бит на пиксел (BPP). Если ввести 0 в поле BPP, то SDL автоматически выберет оптимальное количество бит. Четвертый параметр используется для задания специальных флагов. Для рисования на экране следует (почти всегда) устанавливать флаг SDL_HWSURFACE (или SDL_SWSURFACE):

* SDL_SWSURFACE - создаёт видео поверность в системной памяти

* SDL_HWSURFACE - создаёт видео поверность в видео памяти

* SDL_ASYNCBLIT - включает использование асинхронных обновлений показываемых поверхностей. Приводит к замедлению работы на однопроцессорных машинах, но может увеличить скорость на SMP системах.

* SDL_ANYFORMAT - обычно если не доступна видео поверхность запрошенного бит на пиксель (bits-per-pixel - bpp), SDL проэмулирует bbp теневой поверхностью. Использование SDL_ANYFORMAT предотвращает это и заставляет SDL использовать видео поверхность, не взирая на её глубину цвета.

* SDL_HWPALETTE - даёт SDL эксклюзивный доступ к палитре цветов. Без этого флага вам возможно не всегда будет гарантирован доступ к цветам, запрошенным через SDL_SetColors или SDL_SetPalette.

* SDL_DOUBLEBUF - разрешает двойную буферизацию на аппаратном уровне (доступно только с SDL_HWSURFACE). Вызов SDL_Flip приведёт к переключению буферов и обновлению экрана. Процесс рисования происходит на не отображаемой в данный момент поверхности. Если двойная буферизация не может быть включена, тогда SDL_Flip вызывает только лишь SDL_UpdateRect для всего экрана.

* SDL_FULLSCREEN - SDL попытается использовать режим полного экрана. Если невозможно сменить аппаратное разрешение (по какой-либо причине), будет использовано следующее большее разрешение, а окно будет отображено по центру на черном фоне.

* SDL_OPENGL - создает контекст рендеринга OpenGL. Для использования данного флага следует заранее установить атрибуты видео с помощью SDL_GL_SetAttribute.

* SDL_OPENGLBLIT - аналогичен предыдущему, но позволяет использовать blit-операции. Экранная поверхность (2D) может иметь альфа-канал, по этой причине необходимо использовать SDL_UpdateRects для обновления экранной поверхности.

* SDL_RESIZABLE - создает окно с изменяемыми размерами. Если размеры окна изменяются пользователем, вызывается событие SDL_VIDEORESIZE. Для нового размера можно вызвать функцию SDL_SetVideoMode.

* SDL_NOFRAME - производится попытка создать окно без заголовка или без рамок. Полноэкранный режим автоматически включает данный флаг.

Моя рекомендация: сначала задайте SDL_HWSURFACE|SDL_DOUBLEBUF и, в случае ошибки, попытайтесь снова, задав флаг SDL_SWSURFACE.

В случае успешного выполнения функция SDL_SetVideoMode возвращает указатель на SDL_Surface, в ином случае - NULL. Для проверки на ошибки используйте следующий блок кода:

if ( screen == NULL )

{

printf("Невозможно установить режим 640x480: %s\n", SDL_GetError());

return 1;

}

Это все, что требуется знать про инициализацию SDL. Теперь можно начинать рисовать. Но для начала следует узнать про некоторые типы данных, предоставляемых SDL:

Uint8 - эквивалент типа unsigned char (8-битные беззнаковые целые числа)

Uint16 - 16-битные (2-байтные) беззнаковые целые числа

Uint32 - 32-битные (4-байтные) беззнаковые целые числа

Uint64 - 64-битные (8-байтные) беззнаковые целые числа

Sint8 - эквивалент типа signed char (8-битные целые числа со знаком)

Sint16 - 16-битные (2-байтные) целые числа со знаком

Sint32 - 32-битные (4-байтные) целые числа со знаком

Sint64 - 64-битные (8-байтные) целые числа со знаком

Следует также уточнить: иногда при получении ошибок во время инициализации нет необходимости полностью завершать работу. Например, если инициализация SDL_INIT_VIDEO проходит без ошибок, а SDL_INIT_AUDIO нет, то можно продолжать выполнение программы, но без звука. Для проверки (например) успешности инициализации звука используйте функцию SDL_WasInit():

Uint32 init = SDL_WasInit(SDL_INIT_AUDIO);

if(init & SDL_INIT_AUDIO)

{

sound = 1; // Аудио инициализировано, можно использовать звук

} else {

sound = 0; // Аудио не инициализировано, будем работать без звука

}

Этот кусок кода следует разместить сразу после кода инициализации:

if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) <0 )

{

printf("Инициализация SDL не возможна: %s\n", SDL_GetError());

return 1;

}

размещайте его в своих программах (в данных уроках этот код применяться не будет для упрощения).

Попиксельное рисование на первый взгляд не очень просто, но если есть функция, реализующая данную задачу, то нет ничего проще. Используемая мною функция для рисования пикселей взята из введения в SDL (находится на www.libsdl.org). Выглядит она так:

ПРИМЕЧАНИЕ: понимать как это все работает не обязательно, достаточно просто знать, что оно работает.

void DrawPixel(SDL_Surface *screen, int x, int y,


Uint8 R, Uint8 G, Uint8 B)

{

Uint32 color = SDL_MapRGB(screen->format, R, G, B);

switch (screen->format->BytesPerPixel)

{

case 1: // Используем 8-bpp (бит на пиксель)

{

Uint8 *bufp;

bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;

*bufp = color;

}

break;

case 2: // Возможно 15-bpp или 16-bpp

{

Uint16 *bufp;

bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;

*bufp = color;

}

break;

case 3: // Медленный режим 24-bpp, обычно не применяется

{

Uint8 *bufp;

bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;

if(SDL_BYTEORDER == SDL_LIL_ENDIAN)

{

bufp[0] = color;

bufp[1] = color >> 8;

bufp[2] = color >> 16;

} else {

bufp[2] = color;

bufp[1] = color >> 8;

bufp[0] = color >> 16;

}

}

break;

case 4: // Возможно 32-bpp...

{

Uint32 *bufp;

bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;

*bufp = color;

}

break;

}

}

Данной функции нужно передавать поверхность, на которой будет производиться рисование, координаты точки (х и у) и RGB цвета рисуемой точки.

Следующий абзац представляет собой ускоренный курс по RGB. Его читать необязательно.

Посмотрите на свой монитор через увеличительное стекло (примечание: вы можете испортить зрение, если будете долго это делать). Посмотрите на какую-нибудь белую часть экрана - вы увидите множество точек: красных, зеленых и синих. Теперь взгляните на любую другую часть экрана (с другим цветом) - Вы увидите, что красные, зеленые и синие точки не так ярко светятся и имеют другие тона. Применяя разные значения RGB можно отображать все имеющиеся цвета.

Нам также потребуются еще две функции. Некоторые компьютеры (видеокарты) требуют блокировки экрана перед рисования на нем. Функция SDL_MUSTLOCK(SDL_Surface *screen) позволяет определить требуется ли блокирование экрана, а функции SDL_LockSurface(SDL_Surface *screen) и SDL_UnlockSurface(SDL_Surface *screen) применяются для блокирования и разблокирования соответственно. Ниже приведены две функции, облегчающие данную задачу:

void Slock(SDL_Surface *screen)

{

if ( SDL_MUSTLOCK(screen) )

{

if ( SDL_LockSurface(screen) < 0 )

{

return;

}

}

}

void Sulock(SDL_Surface *screen)

{

if ( SDL_MUSTLOCK(screen) )

{

SDL_UnlockSurface(screen);

}

}

Теперь достаточно вызвать функцию Slock(screen) для заблокирования экрана и Sulock(screen) для его разблокирования.

Теперь Ваш код должен выглядеть примерно так:

#include <stdio.h>

#include <stdlib.h>

#include <SDL/SDL.h>

// Для экономии места функции не показаны

void DrawPixel(SDL_Surface *screen, int x, int y,

Uint8 R, Uint8 G, Uint8 B);

void Slock(SDL_Surface *screen);

void Sulock(SDL_Surface *screen);

int main(int argc, char *argv[])

{

if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )

{

printf("Инициализация SDL не удалась: %s\n", SDL_GetError());

exit(1);

}

atexit(SDL_Quit);

SDL_Surface *screen;

screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);

if ( screen == NULL )

{

printf("Не удалось установить видеорежим 640x480: %s\n", SDL_GetError());

exit(1);

}

// ЗДЕСЬ РИСУЕМ

return 0;

}

Запустите программу! На экране должно ненадолго появиться пустое окно. Если к списку флагов добавить SDL_FULLSCREEN, вы увидите как весь экран закрашивается черным на какое-то время. Теперь займемся рисованием!

Мы будем использовать простой метод: сначала рисуем в буфер, а затем размещаем рисунок на экране. Такой подход предпочтительнее рисования каждого пикселя на экране с последующим обновлением. Он быстрее и не создает эффекта мерцания.

Наша задача: сделать экран разноцветным (как показано на снимке экрана). Для этого мы проходим по всем координатам х и у и рисуем в них цветные пиксели. Перед циклом мы добавляем функцию блокировки экрана, а после него - функцию разблокировки. Drawpixel рисует цветной пиксель внутри экранной поверхности (буфера), после чего мы используем SDL_Flip для отображения буфера (экранной поверхности) на дисплее компьютера (прим. редактора - т.е. просто меняем местами буфер и отображаемую поверхность).

Slock(screen);

for(int x=0;x<640;x++)

{

for(int y=0;y<480;y++)

{

DrawPixel(screen, x,y,y/2,y/2,x/3);

}

}

Sulock(screen);

SDL_Flip(screen);

Примечание: перерисовка всего окна занимает много времени. Обычно если нужно перерисовать часть окна, только ее и перерисовывают. Подробнее в следующем уроке.

Замените комментарий // ЗДЕСЬ РИСУЕМ на блок кода рисования и запустите программу. Вы должны увидеть вместо черного окна разноцветное. Но оно отображается не долго. Чтобы продлить его отображение поместите код рисования внутрь цикла:

for(i=0;i<100;i++)

{

Slock(screen);

for(int x=0;x<640;x++)

{

for(int y=0;y<480;y++)

{

DrawPixel(screen, x,y,y/2,y/2,x/3);

}

}

Sulock(screen);

SDL_Flip(screen);

}

В результате будет выполнен тот же блок рисования 100 раз. Однако есть способ получше.

Мы размещаем код рисования внутрь новой функции:

void DrawScene(SDL_Surface *screen)

{

Slock(screen);

for(int x=0;x<640;x++)

{

for(int y=0;y<480;y++)

{

DrawPixel(screen, x,y,y/2,y/2,x/3);

}

}

Sulock(screen);

SDL_Flip(screen);

}

Теперь в функции main() вызываем так называемый игровой цикл. Игровой цикл - это цикл, который выполняется до окончания игры. В каждой итерации цикла выполняются какие-то действия. Наш игровой цикл повторяется, пока заранее объявленная переменная done равна нулю.

int done=0;

while(done == 0)

{

// КОД

}

Внутри игрового цикла проверяем не были ли нажаты клавиша ESCAPE или кнопка закрытия окна. Если были, то присваиваем переменной done значение 1 и следующая итерация цикла уже выполняться не будет.

Все события SDL размещаются в структуре SDL_Event. Нам требуется всего один её экземпляр для проверки событий.

SDL_Event event;

Далее мы проверяем список событий (до последнего события в списке).

while ( SDL_PollEvent(&event) )

{

}

В каждом while(...) {...} переменная event будет содержать некую информацию о произошедших событиях. Далее внутри этого цикла мы смотрим на тип события:

if ( event.type == SDL_QUIT ) { done = 1; }

if ( event.type == SDL_KEYDOWN )

{

// КОД

}

Если мы получили событие типа quit (пользователь нажал кнопку закрытия окна), то присваиваем переменной done значение 1. Если получено событие нажатия клавиши на клавиатуре, то делаем проверку нажатой клавиши:

if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }

Все имена клавиш начинаются с SDLK_. Если хотите узнать больше имен клавиш, просмотрите файл SDL_keysym.h. После проверки событий рисуем:

DrawScene(screen);

И вот оно! Ниже приведен полный исходный код:

#include <stdio.h>

#include <stdlib.h>

#include <SDL/SDL.h>

void Slock(SDL_Surface *screen)

{

if ( SDL_MUSTLOCK(screen) )

{

if ( SDL_LockSurface(screen) < 0 )

{

return;

}

}

}

void Sulock(SDL_Surface *screen)

{

if ( SDL_MUSTLOCK(screen) )

{

SDL_UnlockSurface(screen);

}

}

void DrawPixel(SDL_Surface *screen, int x, int y,


Uint8 R, Uint8 G, Uint8 B)

{

Uint32 color = SDL_MapRGB(screen->format, R, G, B);

switch (screen->format->BytesPerPixel)

{

case 1: // Используется 8-bpp

{

Uint8 *bufp;

bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;

*bufp = color;

}

break;

case 2: // Возможно 15-bpp или 16-bpp

{

Uint16 *bufp;

bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;

*bufp = color;

}

break;

case 3: // Медленный режим 24-bpp, обычно не применяется

{

Uint8 *bufp;

bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;

if(SDL_BYTEORDER == SDL_LIL_ENDIAN)

{

bufp[0] = color;

bufp[1] = color >> 8;

bufp[2] = color >> 16;

} else {

bufp[2] = color;

bufp[1] = color >> 8;

bufp[0] = color >> 16;

}

}

break;

case 4: // Возможно 32-bpp

{

Uint32 *bufp;

bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;

*bufp = color;

}

break;

}

}

void DrawScene(SDL_Surface *screen)

{

Slock(screen);

for(int x=0;x<640;x++)

{

for(int y=0;y<480;y++)

{

DrawPixel(screen, x,y,y/2,y/2,x/3);

}

}

Sulock(screen);

SDL_Flip(screen);

}

int main(int argc, char *argv[])

{

if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )

{

printf("Не удалось инициализировать SDL: %s\n", SDL_GetError());

exit(1);

}

atexit(SDL_Quit);

SDL_Surface *screen;

screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);

if ( screen == NULL )

{

printf("Не удалось установить видеорежим 640x480: %s\n", SDL_GetError());

exit(1);

}

int done=0;

while(done == 0)

{

SDL_Event event;

while ( SDL_PollEvent(&event) )

{

if ( event.type == SDL_QUIT ) { done = 1; }

if ( event.type == SDL_KEYDOWN )

{

if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }

}

}

DrawScene(screen);

}

return 0;

}

Pages: ← previous Ctrl next
1 2 3 4

Original (English): GFX with SDL. Lesson 1: Getting started with SDL

Translation: © Denisss, ciiccii, algor_1 .

translatedby.com crowd

Like this translation? Share it or bookmark!