От переводчика: вдохновившись циклом статей, я решил попробовать свои силы в SDL. Да вот незадача, каких-либо толковых уроков по SDL версии 2.0 на русском языке найти мне не удалось. Это и подтолкнуло меня к переводу замечательной серии туториалов Twinklebear, в оригинале доступных тут.
Добро пожаловать!
Цель данных уроков — познакомить вас с основами SDL 2.0 и гейм-дева на С++. Подразумевается, что у читателя есть некоторый опыт программирования на С++ и минимальные знания массивов, векторов, управляющих структур, функций и указателей.
Если вы испытываете трудности при разборе кода в примерах, воспользуйтесь одной из книг, представленных в этом чудесном списке на StackOverflow.
Если вы хотите увидеть полный исходник или же скачать ресурсы для уроков, то все это можно получить на GitHub’е. Но не копируйте!
Также документация по SDL 2.0 доступна для чтения в этой вики.
Урок 1: Hello World!
В этом уроке мы научимся открывать окно, создавать контекст рендеринга и рисовать загруженное изображение на экране. Ниже вы можете забрать BMP картинку, которую мы будем рисовать. Сохраните ее где-нибудь в своем проекте. Так давайте уже начнем!
Запуск SDL
Первый шаг обычно заключается в подключении заголовочного файла SDL.
#include <SDL2/SDL.h>
Для того, чтобы начать работать с 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 под различные платформы доступна тут.
До скорой встречи в уроке 2: не запихивайте все в main.