Статистика |
|
Онлайн всего: 1 Гостей: 1 Пользователей: 0 | |
|
|
| | |
|
Уроки SDL - система координат и blitting
Представляю вашему вниманию второй переведенный тутор Тима Джонса. Система координат и Blitting. Оригинал статьи здесь.
Используя первый урок, как базу, мы будем углубляться дальше в мир поверхностей SDL. Как я пытался объяснить в последнем уроке, поверхности SDL представляют собой изображения, сохраненные в памяти. Представьте, у нас есть пустая поверхность 320 x 240 пикселей. Иллюстрация системы координат SDL:
Обратите внимание, что координата увеличивается Y вниз, а координата X увеличивается право. Понимание системы координат SDL важно, чтобы правильно нарисовать изображения на экране. Поскольку у нас уже есть основная поверхности (Surf_Display), нам нужен способ наложения на нее другой поверхности. Этот процесс называется Blitting, где мы в основном накладываем одно изображение на другое. Но прежде чем мы сможем это сделать, мы должны загрузить эти изображения в память. SDL предлагает для этого простую функцию SDL_LoadBMP(). Общий вариант ее использования может выглядеть следующим образом:
SDL_Surface* Surf_Temp; if((Surf_Temp = SDL_LoadBMP("mypicture.bmp")) == NULL) { //Error! }
Это довольно просто, SDL_LoadBMP принимает один аргумент - файл, который вы хотите загрузить, и возвращает SDL поверхность. Если функция возвращает NULL, то файл прочесть не удалось: либо файл не найден, поврежден или имели место какие-то другие ошибки. К сожалению, для эффективности этого метода недостаточно. Часто изображение, загруженное будет в другом формате, чем формат пикселей на дисплее. Таким образом, когда мы будем выводить такое изображение на дисплей, мы можем получить потерю производительности, потерять цвета изображения, и тому подобное. SDL предлагает быстрое решение этой проблемы при помощи функции SDL_DisplayFormat. Эта функция принимает уже загруженную поверхность, и возвращает новую поверхность, использующую уже нужный нам формат отображения.
Давайте создадим новый класс – класс поверхности SDL. Используйте код Урока 1 в качестве основы для вашего кода и добавьте два следующих новых файла: CSurface.h, CSurface.cpp. Откройте CSurface.h и добавить туда: #ifndef _CSURFACE_H_ #define _CSURFACE_H_ #include <SDL.h> class CSurface { public: CSurface(); public: static SDL_Surface* OnLoad(char* File); }; #endif
Мы описали простую статическую функцию OnLoad которая будет загружать и оптимизировать поверхности для нас. Теперь откройте CSurface.cpp:
#include "CSurface.h" CSurface::CSurface() { } SDL_Surface* CSurface::OnLoad(char* File) { SDL_Surface* Surf_Temp = NULL; SDL_Surface* Surf_Return = NULL; if((Surf_Temp = SDL_LoadBMP(File)) == NULL) { return NULL; } Surf_Return = SDL_DisplayFormat(Surf_Temp); SDL_FreeSurface(Surf_Temp); return Surf_Return; }
Есть несколько важных вещей, которые неплохо иметь ввиду. Во-первых, всегда помните, что когда вы делаете указатель, установите его в NULL или 0. Многие проблемы могут возникнуть позже, если вы этого не сделаете. Во-вторых, обратите внимание, что SDL_DisplayFormat возвращает новую поверхность, а не перезаписывает оригинал. Это важно помнить, потому что так как он создает новую поверхность, мы должны освободить старую. В противном случае, у нас появится лишняя поверхность, «висящая» в памяти.
Теперь у нас есть способ загрузки поверхностей в память, нам понадобится способ блиттинга их на другие поверхности. В SDL есть функция блиттинга изображения: SDL_BlitSurface. К сожалению, эта функция не так проста для использования как SDL_LoadBMP, но тем не менее, это достаточно просто. Откройте резервную копию CSurface.h и добавьте следующий прототип функции:
#ifndef _CSURFACE_H_ #define _CSURFACE_H_ #include <SDL.h> class CSurface { public: CSurface(); public: static SDL_Surface* OnLoad(char* File); static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y); }; #endif Теперь снова откройте CSurface.cpp, и приведите к такому виду:
#include "CSurface.h" CSurface::CSurface() { } SDL_Surface* CSurface::OnLoad(char* File) { SDL_Surface* Surf_Temp = NULL; SDL_Surface* Surf_Return = NULL; if((Surf_Temp = SDL_LoadBMP(File)) == NULL) { return NULL; } Surf_Return = SDL_DisplayFormat(Surf_Temp); SDL_FreeSurface(Surf_Temp); return Surf_Return; } bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y) { if(Surf_Dest == NULL || Surf_Src == NULL) { return false; } SDL_Rect DestR; DestR.x = X; DestR.y = Y; SDL_BlitSurface(Surf_Src, NULL, Surf_Dest, &DestR); return true; } Прежде всего, посмотрите на аргументы, которые передаются в функцию OnDraw. У нас есть две поверхности, и две переменные Int. Первая поверхность – поверхность назначения (куда будет производиться блиттинг). Вторая поверхность - поверхность-источник (ее будем блиттировать). В общем, мы отображаем Surf_Src поверх Surf_Dest. Переменные X, Y задают позицию в Surf_Dest, куда мы блиттируем поверхность Surf_Src.
Сначала функция делает проверку указателей на поверхости (существуют ли эти поверхности), если хотя бы одна из них не существует (указатель = 0), то наша функция вернет falce и закончит свою работу. Далее, мы видим SDL_Rect. Это SDL структуру, которая состоит из четырех членов: x, y, w, h. Она, конечно, задает размеры прямоугольника. Если мы будем блиттировать Surf_Src за пределы этого отсекающего прямоугольника, то отсекаемая часть его не отобразится. В нашем случае мы беспокоимся только о том, где мы будем рисовать, размеры h и w не задаем. Таким образом задаем x, y координаты в поверхности назначения. Вам, наверное интересно, что за параметр имеет значение NULL в вызове функции SDL_BlitSurface. это еще один параметр для SDL_Rect. Мы вернемся к нему в этом уроке, но позже. Наконец, вызываем саму функцию блиттинга и возвращаем true по ее завершению.
Теперь, чтобы убедиться, что все это работает, давайте создадим тестовую поверхность. Откройте CApp.h, и создите указатель новой поверхности, при этом не забываем подключить CSurface.h: #ifndef _CAPP_H_ #define _CAPP_H_ #include <SDL.h> #include "CSurface.h" class CApp { private: bool Running; SDL_Surface* Surf_Display; SDL_Surface* Surf_Test; public: CApp(); int OnExecute(); public: bool OnInit(); void OnEvent(SDL_Event* Event); void OnLoop(); void OnRender(); void OnCleanup(); }; #endif Так же инициализируем новую поверхность NULLем в конструкторе:
CApp::CApp() { Surf_Test = NULL; Surf_Display = NULL; Running = true; } И не забываем про очистку:
#include "CApp.h" void CApp::OnCleanup() { SDL_FreeSurface(Surf_Test); SDL_FreeSurface(Surf_Display); SDL_Quit(); } Теперь, фактически позволяет загружать изображения. Откройте CApp_OnInit.cpp, и добавить код для загрузки поверхности:
#include "CApp.h" bool CApp::OnInit() { if(SDL_Init(SDL_INIT_EVERYTHING) < 0) { return false; } if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) { return false; } if((Surf_Test = CSurface::OnLoad("myimage.bmp")) == NULL) { return false; } return true; } Не забудьте поместить какой-либо битмап с именем "myimage.bmp" в ту же папку, где находится ваш исполняемый файл.
Теперь у нас есть загруженное изображение, можно его блиттировать. Откройте CApp_OnRender.cpp и добавить следующее:
#include "CApp.h" void CApp::OnRender() { CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0); SDL_Flip(Surf_Display); }
Обратите внимание на новую функцию SDL_Flip. Она «освежает» буфер и таким образом отображает Surf_Display на экране. Это называется двойной буферизацией. То есть при двойной буферизации все рисование происходит в памяти и только после вызова SDL_Flip(Surf_Display) содержимое Surf_Display отображается на экране. Если бы мы этого не сделали, то увидели бы мерцание изображения на экране. Помните флаг SDL_DOUBLEBUF? Как раз он и задает двойную буферизацию.
Скомпилируйте код, и убедитесь, что все работает правильно. Вы должны увидеть изображение в верхнем левом углу экрана. Если это так, поздравляем, вы на один шаг ближе к реальной игре. Если нет, убедитесь, что вы поместили myimage.bmp в той же папке, что и исполняемый файл. Также убедитесь, что это действительно годный файл растрового изображения.
Идем дальше. Хорошо, конечно, что мы умеем выводить изображение на экран, но часто нам нужно выводить лишь часть какого-либо изображения (набора тайлов или спрайтов). Возьмем, к примеру такой тайлсет (tileset):
Он представляет собой одно изображение, а мы только хотим вывести его часть. Откроем резервную копию CSurface.h, и добавим следующий код:
#ifndef _CSURFACE_H_ #define _CSURFACE_H_ #include <SDL.h> class CSurface { public: CSurface(); public: static SDL_Surface* OnLoad(char* File); static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y); static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H); }; #endif
Затем откройте резервную копию CSurface.cpp и добавьте следующию функцию:
bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H) { if(Surf_Dest == NULL || Surf_Src == NULL) { return false; } SDL_Rect DestR; DestR.x = X; DestR.y = Y; SDL_Rect SrcR; SrcR.x = X2; SrcR.y = Y2; SrcR.w = W; SrcR.h = H; SDL_BlitSurface(Surf_Src, &SrcR, Surf_Dest, &DestR); return true; }
Обратите внимание, что это в основном та же наша первая функция, за исключением того, что мы добавили еще один SDL_Rect. Этот прямоугольник источника позволяет использовать для указания, какие пиксели поверхности-источника Surf_Src будут браться для копирования в Surf_Dest. Если мы указали 0, 0, 50, 50 в качестве параметров для X2 ... H, будет копироваться только левая верхняя часть поверхности (квадрат 50?50 пикселей).
Теперь проверим эту новую функцию в деле, откроем резервную копию СApp_OnRender.cpp, и добавим следующее:
#include "CApp.h" void CApp::OnRender() { CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0); CSurface::OnDraw(Surf_Display, Surf_Test, 100, 100, 0, 0, 50, 50); SDL_Flip(Surf_Display); }
Вы видите, что изображение рисуется с координатами 100, 100 и отрисована лишь часть тайлсета 50х50 пикселей. Вы обязательно должны разобраться, как работают эти функции, и как организована система координат SDL.
Перейдем к следующему уроку SDL, где мы обратим внимание на события SDL (SDL events), и попробуем сделать работу с ними проще.
Все права на статью и исходный код в ней принадлежат Tim Jones by sdltutorials.com.
|
Категория: SDL и C/C++ | Добавил: J3d1 (30.11.2010)
|
Просмотров: 4360
| Рейтинг: 0.0/0 |
Добавлять комментарии могут только зарегистрированные пользователи. [ Регистрация | Вход ]
| |
| | |
|
|