Урок 1. Начинаем программировать с SDL

      Комментарии к записи Урок 1. Начинаем программировать с SDL отключены

Главная Форумы Программирование Программирование на С++ Библиотека SDL Урок 1. Начинаем программировать с SDL

Помечено: , ,

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

  • Автор
    Сообщения
  • #3134
    Как компилировать Содержание Урок 2

    lesson1
    И так, будем считать, что вы установили и настроили библиотеку SDL. Теперь просто подключаем файл заголовков SDL к нашему первому проекту и начинаем работать. Делаем это вот таким вот образом:

    #include "SDL.h"

    Инициализация SDL делается при помощи функции SDL_Init(). Эта функция возвращает значение, меньшее нуля при неудачной инициализации и принимает всего один параметр: что инициализировать. Например, чтобы инициализировать видео подсистему, передайте в качестве аргумента константу SDL_INIT_VIDEO. Чтобы инициализировать аудио подсистему, соответственно, SDL_INIT_AUDIO. А чтобы инициализировать сразу обе подсистемы, передавайте SDL_INIT_VIDEO | SDL_INIT_AUDIO. На самом деле, есть еще несколько значений аргумента функции SDL_Init(). Чтобы передать несколько констант, разделяйте их оператором |. Вот несколько констант, которые можно передавать:

     SDL_INIT_TIMER
     SDL_INIT_AUDIO
     SDL_INIT_VIDEO
     SDL_INIT_CDROM
     SDL_INIT_JOYSTICK
     SDL_INIT_EVERYTHING
     SDL_INIT_NOPARACHUTE
     SDL_INIT_EVENTTHREAD

    И так, если нам надо инициализировать видео и аудио подсистемы, то делаем так:

     if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ){ 
       printf("Unable to init SDL: %s\n", SDL_GetError()); 
       exit(1); 
     } 

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

    atexit(SDL_Quit());

    Работая с SDL, вы имеете дело с понятием surface (поверхность). Вы можете рисовать на поверхностях, а также, можете накладывать одну поверхность на другую. Экран также является поверхностью. Поверхность – это ни что иное, как указатель на структуру SDL_Surface. Чтобы получить доступ к экрану, можно сделать например так:

    SDL_Surface *screen;

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

    screen = SDL_SetVideoMode(640,480,32, SDL_HWSURFACE|SDL_DOUBLEBUF);

    Первые три параметра задают ширину, высоту и глубину цвета (bits per pixel) экрана. Если в параметр глубины цвета задать равным нулю, то библиотека SDL автоматически выберет наилучшее возможное значение. Четвертый параметр служит для передачи некоторых флагов. Практически всегда вы должны передавать флаг SDL_HWSURFACE (SDL_SWSURFACE). Вот флаги которые можно передать функции:

    • SDL_SWSURFACE – создает видео поверхность в оперативной памяти.
    • SDL_HWSURFACE – создает видео поверхность в памяти видеокарты.
    • SDL_ASYNCBLIT – использование асинхронного обновления экрана. Обычно это замедляет работу на обычных компьютерах, но значительно ускоряет на многопроцессорных системах.
    • SDL_ANYFORMAT – обычно, когда указанная глубина цвета невозможна на видеокарте, SDL эмулирует ее с помощью специальной теневой поверхности. Это замедляет работу программы. Указывая этот флаг, вы запрещаете библиотеке делать такие преобразования и заставляете использовать “неправильную” поверхность в любом случае.
    • SDL_HWPALETTE – предоставляет программе эксклюзивный доступ к палитре. Без этого флага вы не всегда сможете получить доступ к цветам функциями SDL_SetColors и SDL_SetPalette.
    • SDL_DOUBLEBUF – включает двойной буфер. Это возможно только вместе с флагом SDL_HWSURFACE. Функция SDL_Flip переключает буферы, обновляя экран. То есть, когда вы рисуете что-либо, то все это отображается на невидимом буфере, а потом буферы переключаются и невидимый становится видимым. Когда двойной буфер недоступен, функция SDL_Flip вызывает функцию SDL_UpdataRects.
    • SDL_FULLSCREENSDL пытается переключиться в полноэкранный режим. Если по каким-либо причинам запрошенный размер экрана не может установиться, то будет выбран следующий больший режим, а изображение будет отцентрировано на черном экране.
    • SDL_OPENGL – создает контекст OpenGL. Перед этим необходимо установить видео атрибуты OpenGL с помощью функции SDL_GL_SetAttribute.
    • SDL_OPENGLBLIT – также создает контекст OpenGL, но позволяет blitting-операции. Экранная поверхность (2D) может иметь альфа-канал (прозрачность) и должна обновляться только при помощи SDL_UpdateRects.
    • SDL_RESIZABLE – создает изменяемое окно. Когда пользователь изменяет размер окна, возникает событие SDL_VIDEORESIZE и можно опять вызвать функцию SDL_SetVideoMode, чтобы установить новые размеры окна.
    • SDL_NOFRAME – если возможно, создает окно без рамки и заголовка. Полноэкранный режим автоматически устанавливает этот флаг.

    Рекомендую использовать флаги: SDL_HWSURFACE|SDL_DOUBLEBUF или в случае ошибки SDL_SWSURFACE.
    SDL_SetVideoMode() возвращает указатель на структуру SDL_Surface, а в случае ошибки возвращает 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); 
     } 

    Еще одна небольшая деталь: когда вы запускаете приложение SDL в окне, можно установить заголовок этого окна и иконку. Естественно, это бесполезно, когда приложение работает в полноэкранном режиме.

     SDL_WM_SetCaption("SDL Gfx Example #1","ex1");
     SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL);

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

    Uint8 – эквивалентно unsigned char
    Uint16 – 16 бит (2 байта) беззнаковое целое (unsigned int)
    Uint32 – 32 бита (4 байта) unsigned int
    Uint64 – 64 бита (8 байт) unsigned int
    Sint8 – эквивалентно signed char
    Sint16 – 16 бит (2 байта) целое со знаком (signed int)
    Sint32 – 32 бита (4 байта) signed int
    Sint64 – 64 бита (8 байт) signed int

    И еще одно замечание: иногда, получая ошибку инициализации какой либо подсистемы, вам не нужно завершать работу программы! Например, SDL_INIT_VIDEO передалась, а SDL_INIT_AUDIO нет, но вы можете продолжать работу программы не используя звук. С помощью функции SDL_WasInit() можно проверить произошла ли инициализация той или иной подсистемы. Вот пример кода на проверку инициализации аудио подсистемы:

    Uint32 init = SDL_WasInit(SDL_INIT_AUDIO); 
    if(init & SDL_INIT_AUDIO){
      sound = 1; // Audio init successful, use sound
    }else{   
      sound = 0; // Audio init unsuccessful, don't use sound
    }

    Нарисовать точку при помощи SDL не так уж и просто. Мы будем использовать для этого функцию, взятую из SDL Intro. Обратите внимание, что вам не нужно полностью понимать как это работает. Просто используйте этот код:

    /* ------------------------------------------------------ */
    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:  // Assuming 8-bpp 
       { 
         Uint8 *bufp; 
         bufp = (Uint8 *)screen->pixels + y*screen->pitch + x; *bufp = color; 
       } break; 
       case 2: // Probably 15-bpp or 16-bpp 
       { 
         Uint16 *bufp; 
         bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; *bufp = color; 
       } break; 
       case 3: // Slow 24-bpp mode, usually not used 
       { 
         Uint8 *bufp; 
         bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3; 
         if(SDL_BYTEORDER == SDL_LIL_ENDIAN){ 
           bufp[0] = color; 
           bufp[1] = color >> 8; 
           bufp[2] = color >> 16; 
         }else{ 
           bufp[2] = color; 
           bufp[1] = color >> 8; 
           bufp[0] = color >> 16; 
         } 
       } break; 
       case 4: // Probably 32-bpp 
       { 
         Uint32 *bufp; 
         bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x; 
         *bufp = color; 
       } break; 
     } 
    
    } 

    Вы просто передаете поверхность, на которой хотите нарисовать точку, координаты и составляющие цвета (RGB). Далее следует экспресс-курс в RGB. Если хотите, можете его спокойно пропустить.

    Посмотрите на экран своего монитора очень близко (Внимание! Если делать это долго, то можно повредить зрение!). Посмотрите на белый участок изображения. Вы видите много мелких точек: красные, зеленые и синие. А теперь посмотрите на участок изображения другого цвета: вы видете все те же точки, но светящие с другой яркостью. С помощью всего трех цветов (красный, зеленый, синий) вы можете составить практически любой цвет! Эти основные цвета известны как RGB триплет. Смешивая их в разных пропорциях, получаем какой угодно цвет, например комбинация RGB(255,255,0) кодирует желтый цвет, а RGB(255,255,255) – белый.

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

    /* ------------------------------------------------------ */
    void Slock(SDL_Surface *screen){ 
    
     if ( SDL_MUSTLOCK(screen) ){ 
       if ( SDL_LockSurface(screen) < 0 ){ 
         return; 
       } 
     } 
    
    } 
    
    /* ------------------------------------------------------ */
    void Sulock(SDL_Surface *screen){
    
     if ( SDL_MUSTLOCK(screen) ){ 
       SDL_UnlockSurface(screen); 
     } 
    
    } 

    Просто вызывайте функции Slock(screen), чтобы заблокировать и Sulock(screen), чтобы разблокировать экран.
    Итак, подведем небольшой итог. До этого момента у вас должно получиться что то вроде этого:

    #include <stdio.h> 
    #include <stdlib.h>  
    #include "SDL.h"  
    
    //Код функций не показан для экономии места
    
    void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B);
    void Slock(SDL_Surface *screen);
    void Sulock(SDL_Surface *screen);
    
    /* ------------------------------------------- */
    int main(int argc, char *argv[]){ 
     
     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_Surface *screen; 
     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); 
     } 
    
     //ТУТ КОД РИСОВАНИЯ 
    
     return 0;  
    }

    Если скомпилировать этот код, то получим черное окно, которое появится и быстро исчезнет. Попробуйте добавить флаг SDL_FULLSCREEN и посмотреть что произойдет. А теперь давайте рисовать.
    Мы будем использовать простой метод: нарисуем все в буфер, а когда все изображение нарисуется, поместим этот буфер на экран. Этот метод работает гораздо быстрее, чем рисовать точку и тут же обновлять экран.
    Давайте сделаем изображение разноцветным, чтобы не было скучно (такое, как на скриншоте). Для этого мы в цикле будем рисовать точки разными цветами. Перед циклом добавим функцию блокировки экрана Slock(screen), а после него функцию Sulock(screen). DrawPixel() рисует цветные точки на поверхности буфера и мы будем использовать функцию SDL_Flip чтобы вывести буфер на экран монитора.

     Slock(screen);
     for(int x=0;x<640;x++){ 
       for(int y=0;y<480;y++){ 
         DrawPixel(screen, x,y,y/2,y/2,x/3); 
       } 
     } 
     Sulock(screen); 
     SDL_Flip(screen);

    Мы поместим этот код в новую функцию DrawScene(). В функции main() создадим главный игровой цикл. Игровой цикл (game loop) – это цикл, который работает все время, пока выполняется программа. Как только мы выходим из этого цикла, программа завершает работу. В нашей программе главный цикл будет выполняться, пока специальная переменная равна нулю:

     int done=0; 
     while(done == 0){
     
       //Здесь код
     
     }

    Внутри нашего цикла будем проверять не нажата ли клавиша ESCAPE или закрыто окно программы. Если это произошло, то присваиваем переменной done единицу и цикл больше не выполнится. Будем называть это событиями.
    В SDL все события описаны структурой SDL_Event. Нам всего лишь нужно создать экземпляр этой структуры, чтобы обрабатывать события.

    SDL_Event event;

    Затем мы ждем наступления события, проверяя нашу структуру:

     while ( SDL_PollEvent(&event) ){
    
     }

    В течение каждого выполнения цикла while, SDL_Event возвращает нам информацию о произошедших событиях. Внутри while-цикла для SDL_PollEvent мы проверяем тип события:

     while ( SDL_PollEvent(&event) ){ 
         if ( event.type == SDL_QUIT ){ done = 1; } 
         if ( event.type == SDL_KEYDOWN )
         {
           //Код проверки клавиш
         }
     }

    При наступлении события keydown (нажатие клавиши), мы должны проверить какая из них была все-таки нажата:

     if ( event.key.keysym.sym == SDLK_ESCAPE ){ 
         done = 1; 
     } 

    Константы клавиш в SDL всегда начинаются с префикса SDLK_. Загляните в файл SDL_keysym.h, чтобы посмотреть все значения. После проверки событий мы вызываем нашу функцию рисования DrawScene(screen). Вот собственно и все! Загрузите исходный текст этого урока и тренируйтесь.

    Как компилировать Содержание Урок 2

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