Цикл уроков по SDL 2.0: урок 1

Автор оригинала: Twinklebear

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

Добро пожаловать!

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

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

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

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

Урок 1: Hello World!


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

image

Запуск 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.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 16

    +6
    Пожалуйста, не повторяйте за другими всякую бяку.
    Подключать заголовки нужно всегда с указанием пути. Т.е. для SDL 1.x:
    #include <SDL/SDL.h>
    
    Для SDL 2.x:
    #include <SDL2/SDL.h>
    

    Если по какой-то причине это не работает, значит в среде неправильно настроены пути к библиотекам.
      0
      Отчего-же? Скажем, pkg-config --cflags sdl2 на моей системе подключает путь .../include/SDL2, то есть, с таким флагом, надо будет подключать #include <SDL.h>.
        0
        Смотрите, оно делает -I/usr/include/SDL2, т.е. добавляет его в «несистемные» инклуды. Т.е. через подключать надо либо через "SDL.h", либо через <SDL2/SDL.h>.
          +1
          Моё мнение, что через "" стоит подключать только «внутрипроектные» инклуды, а инклуды библиотек надо всегда через <>, причём указывать путь всегда, чтобы быть уверенным в том, какая именно библиотека подключается.
          +1
          Цитируя FAQ wiki, касающийся разработки:

          The most portable way to include SDL headers is to use quotes around the header name:
          #include «SDL.h»
            +1
            Процитируя мой ответ Вам в private messages,
            я с ними несогласен. Из-за такой позиции все и суют SDL куда попало вместо того, чтобы поставить куда надо.
              0
              Обновил пост.
                0
                Глянул, что в SDL_Image, например, инклудится «SDL.h»… вновь изменил свою точку зрения и поправил пост.
                  0
                  SDL_Image это SDL_Image. Остальным нужно именно так:
                  gcc `pkg-config --cflags sdl2` <...> `pkg-config --libs sdl2`
                  

                  #include <SDL2/SDL.h>
                  


                  И никак более. Так не нужно будет в случае чего менять исходники, просто либо через pkg-config, либо вручную -I указать. В том-же Microsoft Visual Studio C++ исходники менять не придется, просто пути (или как оно там называется) прописать в проекте.
                    0
                    У меня вижуалка начинает ругаться, что не может найти библиотеку, т.к. в SDL_Image именно такой инклуд. («SDL.h»)
          –4
          Не надо людей учить плохому. BMP — проприетарный формат, и к тому же без сжатия. За его использование MS может попросить денюжку. Лучше сразу разрабатывать игры с использованием PNG для графики и OGG для звука, чтобы никаких претензий ни у кого не возникало.
            0
            В чём выражается проприетарность? Википедия говорит «patent free», документация есть в msdn — по всему выходит что формат открытый. А сжатие — это уже зависит от потребностей в каждом конкретном случае. В конкретно данном случае куда важнее простота, выражающаяся в отсутствии зависимости от SDL2_image.
              0
              > Википедия говорит «patent free», документация есть в msdn — по всему выходит что формат открытый.

              Уж сколько раз натыкались на одни и те же грабли — сегодня это free, а завтра давай плати. Учитывая, что авторство формата остается за Microsoft, сделать они могут что угодно.
              0
              Это перевод цикла уроков, причём пока что первый из них. В третьем уроке будет разобрана библиотека SDL_Image, позволяющая работать с различными форматами изображений.
              0
              Возможно кому-то пригодится моя C++ обёртка над (пока весьма небольшим подмножеством) SDL2. Позволяет не думать о проверке ошибок и уборке мусора.

              github.com/AMDmi3/libSDL2pp
                0
                Судя по статистике переходов с хабра, библиотека многих интересует. Сделал туториал: github.com/AMDmi3/libSDL2pp-tutorial и постараюсь написать по нему статью.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое