Урок 1 | Содержание | Урок 3 |
Приветствую вас во втором уроке. Если вы внимательно изучили первый, то это очень хорошо. Сегодня мы
научимся отображать картинки в ваших программах. Этот урок очень простой и я постараюсь объяснить каждую
строчку нашей следующей программы. Мы нарисуем приятный бэкграунд, нарисуем забавное существо и будем
передвигать его с помощью клавиатуры.
Начнем с включения заголовочных файлов. Их должно быть три: stdio.h, stdlib.h и SDL.h
. Файл
stdlib.h нужен для функции atexit()
.
#include <stdio.h> #include <stdlib.h> #include "SDL.h"
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 поддерживается только формат BMP. Вся эта поддержка состоит только из двух функций
SDL_LoadBMP, которую мы использовали и SDL_SaveBMP, которая позволяет сохранять поверхность в файл BMP.
Для этой функции передавайте в качестве параметра поверхность, которую надо сохранить и имя файла, куда
сохранять. Вот ее прототип: |
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, который мы выводим в итоге на экран). С этого места подробнее. Стандартный
алгоритм для анимации движения таков:
/* ------------------------------------------- */ 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(); |
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 |
©opyleft PLG, 2003.