Пятница, 26.04.2024, 18:59
PIXELCODEПриветствую Вас Гость | RSS
Главная | Каталог статей | Регистрация | Вход
Меню сайта

Категории раздела
SDL и C/C++ [6]

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Форма входа

Главная » Статьи » SDL и C/C++

Уроки 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)
Просмотров: 4359 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск

Друзья сайта
  • Все для веб-мастера
  • Программы для всех
  • Мир развлечений
  • Лучшие сайты Рунета

  • Copyright MyCorp © 2024 Создать бесплатный сайт с uCoz