Уроки по SDL 2: Урок 5 — Текстуры +9


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

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

У текстур есть собственный тип данных — SDL_Texture. При работе с текстурами нам потребуется рендер для отрисовки. Сейчас мы всё это объявим.

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>

using namespace std;

int SCREEN_WIDTH = 640;
int SCREEN_HEIGHT = 480;

SDL_Window *win = NULL;
SDL_Renderer *ren = NULL;
SDL_Texture *flower = NULL;

Здесь мы объявили окно, рендер и этот цветок.
Так же подключили библиотеки и задали размеры окна.

Функцию Init тоже придется изменить.

    bool ok = true;

    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        cout << "Can't init: " << SDL_GetError() << endl;
        ok = false;
    }

    win = SDL_CreateWindow("Текстуры", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    if (win == NULL) {
        cout << "Can't create window: " << SDL_GetError() << endl;
        ok = false;
    }

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

Пришло время объявить рендер.

    ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (ren == NULL) {
        cout << "Can't create renderer: " << SDL_GetError() << endl;
        ok = false;
    }
    SDL_SetRenderDrawColor(ren, 0xFF, 0xFF, 0xFF, 0xFF);

Рендер объявляется функцией SDL_CreateRenderer. Она принимает 3 значения: окно, для которого будет использоваться этот рендер, индекс драйвера(или -1, если мы хотим найти первый подходящий) и флаги. Я использовал флаги SDL_RENDERER_ACCELERATED и SDL_RENDERER_PRESENTVSYNC. Вот список всех флагов:

Флаги
SDL_RENDERER_SOFTWARE — запасной вариант по;
SDL_RENDERER_ACCELERATED — использование аппаратного ускорения;
SDL_RENDERER_PRESENTVSYNC — вертикальная синхронизация;
SDL_RENDERER_TARGETTEXTURE — поддержка рендеринга в текстуру.

Дальше мы используем функцию SDL_SetRenderDrawColor, которая устанавливает цвет рисования примитивов.

После этого инициализируем IMG и возвращаем ok.

    int flags = IMG_INIT_PNG;
    if (!(IMG_Init(flags) & flags)) {
        cout << "Can't init image: " << IMG_GetError() << endl;
        ok = false;
    }

    return ok;
}

Теперь напишем функцию Load.

bool load() {
    bool ok = true;
    SDL_Surface * temp_surf = NULL;
    temp_surf = IMG_Load("flower.png");
    if (temp_surf == NULL) {
        cout << "Can't load image: " << IMG_GetError() << endl;
        ok = false;
    }

Здесь мы так же создали переменную ok, для тех же нужд. Создали временную поверхность и загрузили в нее изображения цветка.

Далее нам нужно сделать из поверхности текстуру.

    flower = SDL_CreateTextureFromSurface(ren, temp_surf);
    if (flower == NULL) {
        cout << "Can't create texture from surface: " << SDL_GetError() << endl;
        ok = false;
    }

В создании текстуры нам поможет функция SDL_CreateTextureFromSurface. Она принимает значения рендера и поверхности. Возвращяет уже экземпляр класса SDL_Texture.

Чтобы не засорять память, нам нужно очистить временную поверхность. После этого возвращяем ok и функция готова.

Функция Quit обрела еще несколько действий.

void quit() {
    SDL_DestroyWindow(win);
    win = NULL;

    SDL_DestroyRenderer(ren);
    ren = NULL;

    SDL_DestroyTexture(flower);
    flower = NULL;

    SDL_Quit();
    IMG_Quit();
}

Здесь мы удалили окно, рендер, текстуру и вышли из SDL и IMG. Также я присвоил всем удаленным объектам NULL для еще лучшей очистки памяти.

Начинаем писать Main функцию.

int main (int argc, char ** argv) {

    if (!init()) {
        quit();
        return 1;
    }
    if (!load()) {
        quit();
        return 1;
    }

Написали инициализацию и загрузку, здесь не останавливаемся.

Следующим шагом создадим 2 переменные.

    bool run = true;
    SDL_Event e;

Эта программа будет с главным циклом, чтобы освежить память, так что начнем его неаписание.

while (run) {
        while(SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                run = false;
            }
        }

Обработали событие нажатия крестика и всё.

Пришло время рисования. При использовании текстур и рендера для отрисовки используются отличные функции от тех, что мы знаем. Для того, чтобы очистить экран от нарисованных текстур, используется функция SDL_RenderClear. Она закрашивает экран цветом, который мы указали функцией SDL_SetRenderDrawColor. Для отрисовки текстуры исрользуется функция SDL_RenderCopy, а для обновления экрана — SDL_RenderPresent. Сечас их напишем.

        SDL_RenderClear(ren);
        SDL_RenderCopy(ren, flower, NULL, NULL);
        SDL_RenderPresent(ren);
    }

Разберем только функцию SDL_RenderCopy. Она принимает 4 значения. Первое — рендер, второе — текстура, третье — прямоугольник, область которого мы хотим вырезать для отрисовки, четвертое — прямоугольник, координаты которого используются для отрисовки, а ширина и высота — для изменения размеров текстуры.

Далее вызываем функцию Quit и возвращаем 0.

    quit();
    return 0;
}

На этом я урок окончю, если что-то было непонятно — пишите, а я прощаюсь, всем пока!

Кстати вот полный код:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>

using namespace std;

int SCREEN_WIDTH = 640;
int SCREEN_HEIGHT = 480;

SDL_Window *win = NULL;
SDL_Renderer *ren = NULL;
SDL_Texture *flower = NULL;

bool init() {
    bool ok = true;

    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        cout << "Can't init: " << SDL_GetError() << endl;
        ok = false;
    }

    win = SDL_CreateWindow("Текстуры", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    if (win == NULL) {
        cout << "Can't create window: " << SDL_GetError() << endl;
        ok = false;
    }

    ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (ren == NULL) {
        cout << "Can't create renderer: " << SDL_GetError() << endl;
        ok = false;
    }
    SDL_SetRenderDrawColor(ren, 0xFF, 0xFF, 0xFF, 0xFF);

    int flags = IMG_INIT_PNG;
    if (!(IMG_Init(flags) & flags)) {
        cout << "Can't init image: " << IMG_GetError() << endl;
        ok = false;
    }

    return ok;
}

bool load() {
    bool ok = true;
    SDL_Surface * temp_surf = NULL;
    temp_surf = IMG_Load("flower.png");
    if (temp_surf == NULL) {
        cout << "Can't load image: " << IMG_GetError() << endl;
        ok = false;
    }

    flower = SDL_CreateTextureFromSurface(ren, temp_surf);
    if (flower == NULL) {
        cout << "Can't create texture from surface: " << SDL_GetError() << endl;
        ok = false;
    }

    SDL_FreeSurface(temp_surf);

    return ok;
}

void quit() {
    SDL_DestroyWindow(win);
    win = NULL;

    SDL_DestroyRenderer(ren);
    ren = NULL;

    SDL_DestroyTexture(flower);
    flower = NULL;

    SDL_Quit();
    IMG_Quit();
}

int main (int argc, char ** argv) {

    if (!init()) {
        quit();
        return 1;
    }
    if (!load()) {
        quit();
        return 1;
    }

    bool run = true;
    SDL_Event e;

    while (run) {
        while(SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                run = false;
            }
        }

        SDL_RenderClear(ren);
        SDL_RenderCopy(ren, flower, NULL, NULL);
        SDL_RenderPresent(ren);
    }

    quit();
    return 0;
}

< Предыдущий урок | Следующий урок >




К сожалению, не доступен сервер mySQL