Я решил попробовать свои силы в SDL. Да вот незадача, каких-либо толковых уроков по SDL версии 2.0 на русском языке найти мне не удалось. Это и подтолкнуло меня к переводу замечательной серии туториалов Twinklebear, в оригинале доступных .

Добро пожаловать!
Цель данных уроков - познакомить вас с основами SDL 2.0 и гейм-дева на С++. Подразумевается, что у читателя есть некоторый опыт программирования на С++ и минимальные знания массивов, векторов, управляющих структур, функций и указателей.

Если вы испытываете трудности при разборе кода в примерах, воспользуйтесь одной из книг, представленных в этом чудесном списке на StackOverflow .

Если вы хотите увидеть полный исходник или же скачать ресурсы для уроков, то все это можно получить на GitHub ’е. Но не копируйте!

Также документация по SDL 2.0 доступна для чтения в этой вики .

Урок 1: Hello World!

В этом уроке мы научимся открывать окно, создавать контекст рендеринга и рисовать загруженное изображение на экране. Ниже вы можете забрать BMP картинку, которую мы будем рисовать. Сохраните ее где-нибудь в своем проекте. Так давайте уже начнем!
Запуск SDL
Первый шаг обычно заключается в подключении заголовочного файла SDL.

#include
Для того, чтобы начать работать с SDL, нам необходимо инициализировать различные SDL подсистемы, которые мы хотим использовать. Это можно сделать с помощью функции SDL_Init , которая принимает набор флагов , указывающих, какие подсистемы мы хотим инициализировать. Сейчас мы просто скажем, что хотим инициализировать все, но если хотите, вы можете это изменить. Минимум, что необходимо для нашего урока - это SDL_INIT_VIDEO. Если все пройдет успешно, SDL_Init вернет 0, в противном случае мы напечатаем ошибку и выйдем.

Заметка для пользователей Visual Studio: если вы установили значение системы как Windows в настройках компоновщика, то вы не получите стандартный вывод в консоль. Чтобы этого не произошло, вам необходимо изменить значение системы на Console.

If (SDL_Init(SDL_INIT_EVERYTHING) != 0){ std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl; return 1; }

Открытие окна
Нам нужно окно, чтобы можно было в нем отображать на рендер. Мы можем создать его, используя функцию SDL_CreateWindow , которая принимает название окна, его координаты, высоту, ширину и некоторые флаги , чтобы задать параметры окна. Данная функция возвращает SDL_Window*. Этот указатель будет NULL, если что-нибудь пойдет не так при создании окна.

SDL_Window *win = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN); if (win == nullptr){ std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; return 1; }

Создание рендерера
Теперь мы можем создать рендерер, с помощью SDL_CreateRenderer . Это необходимо для того, чтобы получить возможность рисовать в окне. Данная функция принимает указатель на окно, с которым необходимо ассоциировать рендерер, индекс драйвера, который будет использоваться для рендеринга (или -1, чтобы выбрать первый подходящий под наши требования) и различные флаги , использующие для указания типа рендерера, который нам нужен. В данном случае мы запрашиваем рендерер с аппаратным ускорением и включенной вертикальной синхронизацией. Мы получим SDL_Renderer*, который будет NULL, если что-то пошло не так.

SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (ren == nullptr){ std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl; return 1; }

Загрузка BMP изображения
Для того, чтобы отрендерить BMP картинку, нам необходимо сначала загрузить ее в память, а затем в устройство рендеринга, которое мы используем (в данном случае GPU). Мы можем загрузить изображение с помощью SDL_LoadBMP . Эта функция возвращает SDL_Surface* , которую мы можем загрузить в SDL_Texture , чтобы ей мог воспользоваться рендерер.

SDL_LoadBMP принимает путь к нашему изображению, который вы должны изменить, чтобы он соответствовал структуре вашего проекта, и возвращает SDL_Surface* или NULL, в случае ошибки.

SDL_Surface *bmp = SDL_LoadBMP("../res/Lesson1/hello.bmp"); if (bmp == nullptr){ std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl; return 1; }
Теперь мы можем загрузить изображение в рендерер, используя SDL_CreateTextureFromSurface . Мы передаем контекст рендеринга и картинку в памяти (SDL_Surface), а получаем загруженную текстуру. В случае, если что-то пошло не так, мы получим NULL. Также SDL_Surface нам больше не потребуется, поэтому мы освободим занимаемую ей память.

SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, bmp); SDL_FreeSurface(bmp); if (tex == nullptr){ std::cout << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl; return 1; }

Отрисовка текстуры
Все, что осталось сделать, это получить нашу текстуру на экране! Сперва мы очистим рендерер, после отрендерим текстуру , а затем покажем обновленный экран, чтобы увидеть результат. Так как мы хотим отрендерить изображение целиком и заставить его растянутся по размеру экрана, мы передадим NULL как исходный и целевой прямоугольники для SDL_RenderCopy. Также мы хотим сохранить окно открытым на некоторое время, чтобы увидеть результат до завершения программы, поэтому мы добавим вызов SDL_Delay .

SDL_RenderClear(ren); SDL_RenderCopy(ren, tex, NULL, NULL); SDL_RenderPresent(ren); SDL_Delay(2000);

Уборка мусора
Перед тем, как выйти, мы должны уничтожить все созданные нами объекты с помощью различных SDL_DestroyX функций и завершить SDL.

SDL_DestroyTexture(tex); SDL_DestroyRenderer(ren); SDL_DestroyWindow(win); SDL_Quit();

Конец урока
В случае успеха, при запуске вы должны увидеть изображение во все окно, а через две секунды программа завершится. В случае проблем убедитесь, что SDL установлена и настроена правильно. Подробная инструкция по установке и настройке SDL под различные платформы доступна .

Сегодня, как уже понятно из названия статьи, мы поговорим об использовании джойстиков, об их подключении и обработке приходящих с джойстика сигналов. Для чего это вообще надо? Ну вот, например, хочется нам собрать робота. А как им управлять? Вот как раз для этого можно прикрутить к компьютеру джойстик, написать небольшую программку, а затем передавать сигналы управления микроконтроллеру, установленному в нашем роботе. И это только одно применение, сразу пришедшее в голову) Короче, штука полезная.

Итак, для работы с джойстиком мы будем использовать библиотеку SDL (Simple DirectMedia Layer) . Помимо доступа к джойстику эта библиотека может помочь реализовать работу с графическими подсистемами и звуковыми устройствами. Но мы пока остановимся на подключении джойстика)

В качестве среды разработки мы, как и обычно, будем использовать QT Creator . И теперь, для начала, нам нужно скачать библиотеку SDL . Заходим и в самом низу находим раздел Development Libraries . Нас интересует Win32 и Mingw32 . Качаем!

Распаковываем куда-нибудь скачанный архив и можно переходить к написанию программы. НО! Для начала нужно зайти в архиве с библиотекой SDL в папку bin и скопировать оттуда файл SDL.dll . А запихать этот файл нужно в папку куда установлен QT Creator , вот по такому пути:

QtSDK/Desktop/Qt/4.8.1/mingw/bin

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

INCLUDEPATH += C:/ SDL- 1.2.15/ SDL- 1.2.15/ include/ SDL LIBS += - lSDL

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

Сделаем так, чтобы приложение считывало данные о текущем положении “рычага” джойстика, ну и наглядно выводило эти данные.

// Подключаем SDL #include // main определена и в SDL и в QT, поэтому #undef #undef main #include QT_USE_NAMESPACE int main(int argc, char * argv ) { // Переменная для нашего джойстика SDL_Joystick * joy; SDL_Event event; // Инициализация SDL для использования джойстика SDL_Init(SDL_INIT_JOYSTICK) ; // Включаем SDL_JoystickEventState(SDL_ENABLE) ; // Открываем;) joy = SDL_JoystickOpen(0 ) ; while (1 ) { // Примитивнейшая задержка unsigned int j = 0 ; for (j = 0 ; j < 60000 ; j++ ) ; SDL_PollEvent(& event) ; // Получаем значения, соответствующие смещению джойстика // по оси Х int xAxis = SDL_JoystickGetAxis(joy, 0 ) ; // по оси Y int yAxis = SDL_JoystickGetAxis(joy, 1 ) ; // Выводим qDebug() << xAxis << yAxis; } }

В зависимости от положения “рычага” джойстика возвращаемые значения могут изменяться от -32768 до 32767 для каждой оси. Это и видим с помощью нашего приложения. При перемещении рычага видим как меняются в реальном времени значения, возвращаемые приложением)


Вот так вот получилось кратко, но я надеюсь, информативно) Немного разобрались с джойстиками, наверное, в будущем еще не раз к этому вернемся, а на сегодня это все, до новых встреч!

Среда программирования:

Добрый день и добро пожаловать. Это серия статей будет посвящена библиотеке SDL2. В них я дам базовые знания, необходимые для написания графических приложений с помощью этой библиотеки. SDL2 - низкоуровневая библиотека, написанная для получения "почти" прямого доступа ко всему оборудованию компьютера. Что это значит? Больше скорости работы, меньше памяти, но и требования по знаниям у нее чуть больше. Но ничего бояться не нужно. Самый большой ее плюс в том, что она кроссплатформена, хотите писать код с минимальным отличием, который будет работать как в Windows так и в Linux/Mac? Тогда вам сюда.

Сразу же скачаем библиотеку. . Распакуйте этот архив куда угодно. Я положил в корень диска С (С:\SDL2)

Открываем студию и создаем новый проект -> Пустой проект.
Создаем в нем любой.cpp файл. Это важный шаг.
Нажимаем правой кнопкой мыши (ПКМ) в обозревателе решений по проекту -> свойства.

Если все хорошо то видим такое окно:

Выбираем С/С++ -> Общие -> Дополнительные каталоги включаемых файлов
Находим папку SDL2 и заходим в папку Includes. Нажимаем ОК. В строке получится что-то вроде C:\SDL2\include;%(AdditionalIncludeDirectories)

Компоновщик -> Ввод -> Дополнительные зависимости
Здесь необходимо нажать изменить и в Дополнительные зависимости прописать это:

SDL2.lib SDL2main.lib SDL2test.lib

Заходим в Компоновщик -> Система -> Подсистема
Выбираем (если не стоит) консоль.

А теперь последний штрих. Соберите проект как он есть. Это создаст ехе-файл.
Перейдите в папку "мои документы" и найдите свой проект (у меня мои документы/Visual studio 2015/projects/project1/debug) и помеcтите туда файл SDL2.DLL. Ваша программа НЕ БУДЕТ работать без этого файла.

Теперь небольшой код для проверки всего, что мы настроили:

//подключим SDL и stdio #include #include //Некоторые константы нашего окна const int SCREEN_WIDTH = 640 ; const int SCREEN_HEIGHT = 480 ; int main(int argc, char * args ) { //Какое окно будет рендерится SDL_Window* window = NULL ; //Поверхность окна SDL_Surface* screenSurface = NULL ; //Включим SDL if (SDL_Init(SDL_INIT_VIDEO) < 0 ) { printf ("SDL не смог запуститься! SDL_Error: %s\n " , SDL_GetError() ) ; } else { //Создаем окно window = SDL_CreateWindow("Урок1" , SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN) ; if (window == NULL ) { printf ("Окно не может быть создано! SDL_Error: %s\n " , SDL_GetError() ) ; } else { //Получаем поверхность screenSurface = SDL_GetWindowSurface(window) ; //Заполняем ее белым цветом SDL_FillRect(screenSurface, NULL , SDL_MapRGB(screenSurface- > format, 0xFF , 0xFF , 0xFF ) ) ; //Обновляем поверхность SDL_UpdateWindowSurface(window) ; //Ждем две секунды SDL_Delay(2000 ) ; } } //И удаляем из памяти окно SDL_DestroyWindow(window) ; //Выход из SDL SDL_Quit() ; return 0 ; }

Если в результате его выполнения вы увидели консоль и окно (Которое через 2 секунды закрылось), то вы все сделали правильно! До встречи на следующем уроке.

Draw_FillEllipse(screen, 320, 240, 300, 200, SDL_MapRGB(screen->format, 128, 128, 128));

SDL_Flip(screen); while(SDL_WaitEvent(&event)){

if(event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))

SDL_Quit(); return 0;

SDL_Quit(); return 2;

4. Из архива SDL-1.2.15-devel-1.2.15-mingw32.tar.gz копируем файл SDL-1.2.15-devel-1.2.15- mingw32.tar\SDL-1.2.15\bin\SDL.dll в каталог с созданным проектом (C:\SDLdrawtest).

5. Из каталога C:\SDL_draw-1.2.13\Dev-Cpp\ копируем файл SDL_draw.dll в каталог с созданным проектом (C:\SDLdrawtest).

6. В параметрах проекта (меню «Проект/Параметры проекта», комбинация клавиш Alt+P), на вкладке «Параметры», добавляем в столбец «Компоновщик» строку:

Lmingw32 -lSDLmain –lSDL –lSDL_draw

7. Собираем и запускаем проект по клавише F9, и он успешно запускается: на черном фоне отображается серый закрашенный эллипс. Выход из программы – по нажатию клавиши Esc или щелчку по системной кнопке закрытия окна в его заголовке.

Создание проектов, использующих библиотеки SDL-1.2.15, SDL_draw-1.2.13 и SDL_ttf-

2.0.11 с динамическим связыванием:

1. Сам проект создается аналогично вышеприведенным примерам, в папку с проектом

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

2. В параметрах проекта (меню «Проект/Параметры проекта», комбинация клавиш Alt+P), на вкладке «Параметры», в столбец «Компоновщик» добавляется строка, подключающая все три библиотеки:

Lmingw32 -lSDLmain –lSDL –lSDL_draw –lSDL_ttf

3. Подключение и инициализация библиотеки SDL

Для возможности вызова в программе функций из библиотеки SDL необходимо подключить заголовочный файл SDL.h (обращаем внимание, что путь к каталогу с ним уже включен в список дополнительных путей поиска заголовочных файлов согласно разд. 2):

#include " SDL.h"

Данный заголовочный файл включает в себя все остальные заголовочные файлы библиотеки SDL, поэтому подключения других не требуется. Заголовочные файлы всех рассматриваемых библиотек содержат для большинства функций достаточно подробные комментарии на английском языке, в том числе явное указание на то, какие функции являются «публичными» – для использования пользователями библиотеки, а какие лишь для ее реализации.

При использовании среды разработки Dev-Cpp 4.9.9.2 следует также обратить внимание на то, что использование библиотеки SDL приводит к переопределению стандартного потока вывода stdout и стандартного потока ошибок stderr на одноименные файлы stdout и stderr в каталоге с исполняемым файлом программы. Также обращаем внимание на необходимость определения функ-ции main в соответствии с сигнатурой, используемой в библиотеке SDL, а именно следующим образом:

int main(int argc, char *argv)

/* тело функции main*/

Непосредственно перед использованием функций библиотеки SDL необходимо инициализировать соответствующие ее подсистемы с помощью функции SDL_Init:

extern DECLSPEC int SDLCALL SDL_Init(Uint32 flags);

Важным является тип аргумента flags – целое число без знака, представленное 32 битами (4 байта). Для переносимости между платформами и различными компиляторами он объявлен как самостоятельный тип с помощью директив препроцессора, обес-печивающих условную трансляцию. С помощью данного парамет-ра указывается, какие именно подсистемы библиотеки требуется инициализировать и какие глобальные режимы использовать. Для этого объявлен ряд флагов, которые можно комбинировать с помощью операции побитового ИЛИ. Например, для ини-

циализации подсистемы работы с дисплеем служит флаг SDL_INIT_VIDEO, для работы с

SDL_INIT_EVERYTHING объявлено как 0x0000FFFF, то его можно рассматривать как указание на инициализацию всех доступных подсистем SDL.

Результат, возвращаемый функцией SDL_Init, указывает на успешность инициализации, если он равен нулю, или на ошибку инициализации в противном случае. При ошибке можно получить описание ошибки (также на английском языке) с помощью функции SDL_GetError:

extern DECLSPEC char * SDLCALL SDL_GetError(void);

Такое объявление позволяет использовать ее для формиро-вания сообщений о произошедших ошибках. Например, для вывода в поток stderr с помощью функции fprintf:

/* объявление переменных */

if (SDL_Init(SDL_INIT_VIDEO)) /* инициализация SDL */

{ /* При ошибке формируем сообщение и выходим */ fprintf(stderr,"Ошибка в SDL_Init: %s\n",SDL_GetError()); return 1;

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

/* Cразу после вышеприведенного фрагмента. Оператор выполняется при безошибочной инициализации SDL*/ atexit(SDL_Quit);

Отображение графической информации в библиотеке SDL основано на понятии «поверхность». Поверхность логически представляет собой прямоугольную матрицу, состоящую из пик-селей определенного формата, на которой можно рисовать, изме-няя состояние пикселей. Набор возможных состояний пикселя определяется его форматом. Все пиксели одной поверхности име-ют одинаковый формат. Поэтому его также можно считать «фор-матом поверхности». Возможно и более сложное использование поверхностей, например для формирования изображения нало-жением изображений разных поверхностей. В программе каждая поверхность представляется указателем на структуру SDL_Surface. Данный указатель возвращают функции, создающие поверхности как объекты программы, и впоследствии они используются для указания поверхности при всех операциях с ними. Основное окно программы, возможно полноэкранное,

также является поверх-ностью, однако создающейся специальной функцией установки видеорежима SDL_SetVideoMode:

extern DECLSPEC SDL_Surface * SDLCALL SDL_SetVideoMode (int width, int height, int bpp, Uint32 flags);

Она возвращает либо корректный указатель на структуру SDL_Surface, соответствующую поверхности окна программы (или всего экрана), либо NULL в случае ошибки. Это позволяет контролировать возникновение ошибок при установке видео-режима в простейшем случае опять же с помощью функции SDL_GetError:

/* в соответствующем месте объявляем указатель на поверхность: */

SDL_Surface *screen; /* ... */

/* После инициализации собственно SDL и установки atexit(SDL_Quit): */ screen=SDL_SetVideoMode(800,600,32,SDL_ANYFORMAT);

fprintf(stderr,"SDL mode failed: %s\n",SDL_GetError()); return 1;

Приведенный фрагмент также иллюстрирует параметры функции SDL_SetVideoMode. Первый параметр задает ширину окна или разрешение по горизонтали для полноэкранного режима, второй

– высоту окна или разрешение по вертикали. Третий пара-метр задает глубину цвета – количество бит для представления цвета одного пикселя (BPP – bit-per-pixel). Если в качестве значе-ния третьего параметра указать ноль, то будет выбрана оптималь-ная глубина цвета для указанного разрешения (т.е. обеспечи-вающая наибольшее количество цветов при наименьших затратах на дополнительные программные преобразования, что определя-ется возможностями сочетания используемой в конкретном устройстве аппаратной части и ее драйверов). Четвертый параметр предназначен для указания специальных флагов задания видео-режима (часть из них применима и к другим поверхностям для рисования), в частности для указания на полноэкранный видео-режим, возможность изменения размера окна пользователем (по умолчанию отключено), определение расположения поверхности в системной памяти или видеопамяти, возможности асинхронного обновления. В простых случаях при использовании современных видеоадаптеров достаточно указать флаг SDL_ANYFORMAT, заставляющий инициализировать поверхность, даже если запрошенная глубина цвета не поддерживается: практически любой современный видеоадаптер для персональных компьютеров, включая встраиваемые или интегрированные с процессором, поддерживают разрешение до 1920 на 1080 пикселей с глубиной цвета 32 бита на пиксель.

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

Библиотека SDL предусматривает также ряд функций, позво-ляющих проверить поддерживаемые аппаратной частью конкрет-ного компьютера видеорежимы, список видеорежимов или полу-чить параметры для «оптимального» видеорежима. Это функции

SDL_GetVideoInfo, SDL_VideoModeOK, SDL_ListModes, которые рекомендуется рассмотреть самостоятельно.

После инициализации видеорежима имеет смысл установить заголовок окна, соответствующий создаваемой программе. Для этого служит функция SDL_WM_SetCaption:

extern DECLSPEC void SDLCALL SDL_WM_SetCaption (const char *title, const char *icon);

Первый параметр – указатель на начало последовательности байтов, заканчивающейся нулем и представляющей текст нового заголовка окна в кодировке utf-8, второй параметр – аналогичный указатель для используемого в некоторых операционных системах текста пиктограммы окна. Если второй параметр равен NULL, то текст пиктограммы не изменяется. Для поддерживаемых ОС семейства Microsoft Windows второй параметр игнорируется. Однако для предотвращения нежелательных эффектов его следует задавать равным NULL. Существует обратная функция SDL_WM_GetCaption, позволяющая получить текущий заголовок окна и текст пиктограммы окна. Фактическое поведение данных функций может зависеть от конкретной операционной системы и режима работы приложения. Например, понятие «заголовок окна» отсутствует в полноэкранном режиме или при задании соответ-ствующих параметров функции SDL_SetVideoMode.

Непосредственное рисование на поверхности путем изменения состояния пикселей – достаточно трудоемкий процесс: требуется вычислить представление каждого компонента цвета для соответ-ствующей данной поверхности глубины цвета и положение битовых последовательностей, отвечающих за соответствующие компоненты конкретного пикселя для их изменения. Приведем лишь пример функции, обеспечивающей установку на поверх-ности screen для пикселя с координатами (x ,y ) цвета, заданного своими компонентами (R, G, B). Данная функция основана на примерах из введения в SDL (www.libsdl.org). По аналогии с Uint32, типы Uint8 и Uint16 соответствуют целым числам без знака, представляемым одним или двумя байтами. Функция SDL_MapRGB возвращает число, соответствующее ближайшему представимому в формате данной поверхности цвету, заданному своими компонентами. Формат поверхности (формат пикселей поверхности) можно получить с помощью компонента format структуры SDL_Surface. Этот компонент представляет собой указатель на структуру типа SDL_PixelFormat, описывающую формат пикселя поверхности, объем памяти, занимаемой одним пикселем, используемую для преобразования цветов (при необходимости) палитру. Возвращенное функцией SDL_MapRGB число может быть непосредственно записано в область памяти, отвечающую за цвет конкретного пикселя. Система координат в окне или на экране – осьX слева направо, осьY сверху вниз, верхний левый угол имеет координаты (0,0), нижний правый – на единицу меньше ширины и высоты окна (разрешения экрана). Определение функции DrawPixel будет выглядеть следующим образом:

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 = (Uint8 *)screen->pixels +

/* pixels – указатель на начало области данных, описывающей состояние пикселей поверхности screen */

y*screen->pitch + x; /* pitch – количество байтов, занимаемых данными

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

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

/* Поскольку (Uint16 *)screen->pixels возвращает указатель на целое число, занимающее в памяти 2 байта, при вычислении смещения делим длину строки в байтах

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

Uint8 *bufp = (Uint8 *)screen->pixels +

Я решил попробовать свои силы в SDL. Да вот незадача, каких-либо толковых уроков по SDL версии 2.0 на русском языке найти мне не удалось. Это и подтолкнуло меня к переводу замечательной серии туториалов Twinklebear, в оригинале доступных .

Добро пожаловать!
Цель данных уроков - познакомить вас с основами SDL 2.0 и гейм-дева на С++. Подразумевается, что у читателя есть некоторый опыт программирования на С++ и минимальные знания массивов, векторов, управляющих структур, функций и указателей.

Если вы испытываете трудности при разборе кода в примерах, воспользуйтесь одной из книг, представленных в этом чудесном списке на StackOverflow .

Если вы хотите увидеть полный исходник или же скачать ресурсы для уроков, то все это можно получить на GitHub ’е. Но не копируйте!

Также документация по SDL 2.0 доступна для чтения в этой вики .

Урок 1: Hello World!

В этом уроке мы научимся открывать окно, создавать контекст рендеринга и рисовать загруженное изображение на экране. Ниже вы можете забрать BMP картинку, которую мы будем рисовать. Сохраните ее где-нибудь в своем проекте. Так давайте уже начнем!
Запуск SDL
Первый шаг обычно заключается в подключении заголовочного файла SDL.

#include
Для того, чтобы начать работать с SDL, нам необходимо инициализировать различные SDL подсистемы, которые мы хотим использовать. Это можно сделать с помощью функции SDL_Init , которая принимает набор флагов , указывающих, какие подсистемы мы хотим инициализировать. Сейчас мы просто скажем, что хотим инициализировать все, но если хотите, вы можете это изменить. Минимум, что необходимо для нашего урока - это SDL_INIT_VIDEO. Если все пройдет успешно, SDL_Init вернет 0, в противном случае мы напечатаем ошибку и выйдем.

Заметка для пользователей Visual Studio: если вы установили значение системы как Windows в настройках компоновщика, то вы не получите стандартный вывод в консоль. Чтобы этого не произошло, вам необходимо изменить значение системы на Console.

If (SDL_Init(SDL_INIT_EVERYTHING) != 0){ std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl; return 1; }

Открытие окна
Нам нужно окно, чтобы можно было в нем отображать на рендер. Мы можем создать его, используя функцию SDL_CreateWindow , которая принимает название окна, его координаты, высоту, ширину и некоторые флаги , чтобы задать параметры окна. Данная функция возвращает SDL_Window*. Этот указатель будет NULL, если что-нибудь пойдет не так при создании окна.

SDL_Window *win = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN); if (win == nullptr){ std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; return 1; }

Создание рендерера
Теперь мы можем создать рендерер, с помощью SDL_CreateRenderer . Это необходимо для того, чтобы получить возможность рисовать в окне. Данная функция принимает указатель на окно, с которым необходимо ассоциировать рендерер, индекс драйвера, который будет использоваться для рендеринга (или -1, чтобы выбрать первый подходящий под наши требования) и различные флаги , использующие для указания типа рендерера, который нам нужен. В данном случае мы запрашиваем рендерер с аппаратным ускорением и включенной вертикальной синхронизацией. Мы получим SDL_Renderer*, который будет NULL, если что-то пошло не так.

SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (ren == nullptr){ std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl; return 1; }

Загрузка BMP изображения
Для того, чтобы отрендерить BMP картинку, нам необходимо сначала загрузить ее в память, а затем в устройство рендеринга, которое мы используем (в данном случае GPU). Мы можем загрузить изображение с помощью SDL_LoadBMP . Эта функция возвращает SDL_Surface* , которую мы можем загрузить в SDL_Texture , чтобы ей мог воспользоваться рендерер.

SDL_LoadBMP принимает путь к нашему изображению, который вы должны изменить, чтобы он соответствовал структуре вашего проекта, и возвращает SDL_Surface* или NULL, в случае ошибки.

SDL_Surface *bmp = SDL_LoadBMP("../res/Lesson1/hello.bmp"); if (bmp == nullptr){ std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl; return 1; }
Теперь мы можем загрузить изображение в рендерер, используя SDL_CreateTextureFromSurface . Мы передаем контекст рендеринга и картинку в памяти (SDL_Surface), а получаем загруженную текстуру. В случае, если что-то пошло не так, мы получим NULL. Также SDL_Surface нам больше не потребуется, поэтому мы освободим занимаемую ей память.

SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, bmp); SDL_FreeSurface(bmp); if (tex == nullptr){ std::cout << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl; return 1; }

Отрисовка текстуры
Все, что осталось сделать, это получить нашу текстуру на экране! Сперва мы очистим рендерер, после отрендерим текстуру , а затем покажем обновленный экран, чтобы увидеть результат. Так как мы хотим отрендерить изображение целиком и заставить его растянутся по размеру экрана, мы передадим NULL как исходный и целевой прямоугольники для SDL_RenderCopy. Также мы хотим сохранить окно открытым на некоторое время, чтобы увидеть результат до завершения программы, поэтому мы добавим вызов SDL_Delay .

SDL_RenderClear(ren); SDL_RenderCopy(ren, tex, NULL, NULL); SDL_RenderPresent(ren); SDL_Delay(2000);

Уборка мусора
Перед тем, как выйти, мы должны уничтожить все созданные нами объекты с помощью различных SDL_DestroyX функций и завершить SDL.

SDL_DestroyTexture(tex); SDL_DestroyRenderer(ren); SDL_DestroyWindow(win); SDL_Quit();

Конец урока
В случае успеха, при запуске вы должны увидеть изображение во все окно, а через две секунды программа завершится. В случае проблем убедитесь, что SDL установлена и настроена правильно. Подробная инструкция по установке и настройке SDL под различные платформы доступна .