Урок 2. Загружаем и отображаем картинки

      Комментарии к записи Урок 2. Загружаем и отображаем картинки отключены

Главная Форумы Программирование Программирование на С++ Библиотека SDL Урок 2. Загружаем и отображаем картинки

Помечено: , ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 7 мес., 3 нед. назад.

  • Автор
    Сообщения
  • #3137
    Урок 1СодержаниеУрок 3

    Сегодня мы научимся отображать картинки в ваших программах.
    lesson2
    Этот урок очень простой и я постараюсь объяснить каждую строчку нашей следующей программы. Мы нарисуем приятный бэкграунд, нарисуем забавное существо и будем передвигать его с помощью клавиатуры.
    Начнем с включения заголовочных файлов. Их должно быть три: stdio.h, stdlib.h и SDL.h. Файл stdlib.h нужен для функции atexit().

    #include <stdio.h> 
    #include <stdlib.h>  
    #include "SDL.h" 

    После этого мы объявим три глобальных поверхности SDL_Surface. Как вы знаете, глобальные означают, что они могут использоваться всеми функциями внутри файла. Чтобы объявить что-либо глобально, просто поместите код объявления в верху файла, перед кодом функций. Также нам понадобятся две переменных типа int: xpos и ypos. Они будут хранить координаты положения нашего существа.

    SDL_Surface *back;
    SDL_Surface *image;
    SDL_Surface *screen;
    int xpos=0, ypos=0;

    Функция InitImages() нужна, чтобы загружать изображения из файлов (BMP) на наши поверхности SDL_Surface. Позже мы вызовем эту функцию из функции main(). Внутри нашей функции мы используем SDL_LoadBMP. Эта функция принимает имя файла изображения в качестве параметра и возвращает указатель на структуру SDL_Surface. Мы загрузим две картинки: одну в SDL_Surface back, которая будет отображаться в качестве фона, а другую в SDL_Surface image, которая будет нашим существом.

    void InitImages(){
     back=SDL_LoadBMP("bg.bmp");
     image=SDL_LoadBMP("image.bmp"); 
    }

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

    Библиотекой SDL поддерживается только формат BMP. Вся эта поддержка состоит только из двух функций SDL_LoadBMP, которую мы использовали и SDL_SaveBMP, которая позволяет сохранять поверхность в файл BMP. Для этой функции передавайте в качестве параметра поверхность, которую надо сохранить и имя файла, куда сохранять. Вот ее прототип:
    int SDL_SaveBMP(SDL_Surface *surface, const char *filename);
    Функция возвращает 0 при успехе и -1 при ошибке. Но иногда неудобно использовать формат BMP. Такие файлы занимают много места. Существует библиотека SDL Image, которая позволяет очень просто работать с изображениями очень многих форматов. Загрузить эту библиотеку можно на официальном сайте.

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

    int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);

    Здесь src это поверхность, которую мы хотим отобразить, а dst это поверхность на которой отобразится src. Еще два параметра — это указатели на структуру SDL_Rect: srcrect и dstrect. Эта структура содержит 4 16-битных целых числа: x, y, w(ширина) и h(высота). Параметр srcrect указывает какую часть исходной поверхности отобразить, а dstrect задает координаты принимающей поверхности, в которых отобразится исходная. Если в качестве второго параметра NULL, то все исходное изображение будет отображено целиком. В структуре dstrect элементы ширина (w) и высота (h) не обрабатываются функцией и не имеют никакого значения. Вот код первой функции DrawIMG:

    void DrawIMG(SDL_Surface *img, int x, int y){
     SDL_Rect dest;
     dest.x = x;
     dest.y = y;
    
     SDL_BlitSurface(img, NULL, screen, &dest);
    }

    Вторая функция использует параметр srcrect. Элементы x и y структуры srcrect задают координату откуда начинать копирование изображения, а ширина и высота определяют размеры исходного копируемого изображения. То есть, мы рисуем только часть изображения. Взгляните на код функции и постарайтесь понять, что происходит:

    void DrawIMG(SDL_Surface *img, int x, int y, int w, int h, int sx, int sy){
     SDL_Rect dest;
     dest.x = x;
     dest.y = y;
    
     SDL_Rect src;
     src.x = sx;
     src.y = sy;
     src.w = w;
     src.h = h;
     
     SDL_BlitSurface(img, &src, screen, &dest);
    }

    Теперь разберем функцию DrawBG(), с помощью которой будем рисовать бэкграунд (задний фон). Впоследствии, в функции main перед началом главного цикла мы вызовем ее. Как вы помните с предыдущего урока, нам не надо блокировать экран для отображения картинок. В этой функции мы просто копируем фоновое изображение на экранную поверхность SDL_Surface *screen. Нам не понадобится функция обновления экрана вроде SDL_Flip(), потому что бэкграунд нужно нарисовать только один раз. Заметьте, что копировать изображение поверхности можно на любую поверхность, а не только на экран. Вот код для рисования заднего фона:

    void DrawBG(){
     DrawIMG(back, 0, 0);
    }

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

    • Затираем изображение участком фона, на котором это изображение расположено
    • Рисуем изображение в новом месте (с новыми координатами)
    • Опять затираем изображение участком фона, на котором находится изображение
    • Рисуем изображение на новом месте

    И так продолжается пока не предусмотрено иное действие. Мы будем придерживаться этого алгоритма. Изображение нашего существа имеет размеры 128х128. Координаты текущего положения будут изменяться только на 1 точку, т.е. при одном нажатии на клавишу изменяем на 1 точку. Участок фона, которым мы будем затирать изображение будет немного больше в размерах и это здорово облегчит нашу задачу. То есть если наше изображение размерами 128х128, то участок фона должен быть (2+128+2)х(2+128+2) или 132х132. Вот что должно в итоге получиться:

    void DrawScene(){
     DrawIMG(back, xpos-2, ypos-2, 132, 132, xpos-2, ypos-2);
     DrawIMG(image, xpos, ypos);
     
     SDL_Flip(screen);
    }

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

    int main(int argc, char *argv[]){ 
     
     Uint8* keys;
     
     if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ){ 
       printf("Unable to init SDL: %s\n", SDL_GetError()); 
       exit(1); 
     } 
     
     atexit(SDL_Quit); 
    
     SDL_WM_SetCaption("SDL Gfx Example #2","ex2");
     SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL);
     
     screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF); 
     if ( screen == NULL ){ 
       printf("Unable to set 640x480 video: %s\n", SDL_GetError()); 
       exit(1); 
     }

    Затем загружаем изображения и рисуем фон:

    InitImages();
     DrawBG();
    

    Начинаем главный цикл и проверяем на нажатие клавишу ESCAPE. Все как в первом уроке:

     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; } 
         } 
       }
    

    Теперь надо получить состояние клавиш клавиатуры. Помните нашу переменную Uint8 *keys? Так вот, функция SDL_GetKeyState() возвращает указатель на массив Uint8. Каждый элемент массива содержит состояние определенной клавиши клавиатуры (нажата или нет). Мы не проверяем все клавиши в event-цикле (там где проверка на нажатие ESCAPE), потому что SDL_PollEvent реагирует только на события (например KEYDOWN), но не будет реагировать на удерживание клавиши. После этого рисуем сцену:

       keys = SDL_GetKeyState(NULL);
       if(keys[SDLK_UP]){ ypos -= 1; }
       if(keys[SDLK_DOWN]){ ypos += 1; }
       if(keys[SDLK_LEFT]){ xpos -= 1; }
       if(keys[SDLK_RIGHT]){ xpos += 1; }
       
       DrawScene();
     }

    Когда выходим из цикла, то программа завершает работу:

     return 0;
      
    }

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

    Урок 1СодержаниеУрок 3

Для ответа в этой теме необходимо авторизоваться.