Введение. Ага, окошко мы сделали. (Да! мы сделали это! ведь Вы его сделали?) И надо сказать, что это было совсем не сложно. Теперь нам надо сделать нечто полезное в нем. Я пробежался по многим туторам СДЛ и нашел в них, что в большинстве своем вторая попытка сделать что-то на СДЛ, сводилась к рисованию спрайта или пикселя. Я же решил поступить несколько иначе. О загрузке графики и рисовании спрайтов мы поговорим в третьем уроке, это от нас никуда не убежит. А рисование пикселя я и вовсе решил опустить, мне кажется что начинать изучение какого-либо движка или библиотеки с такого старомодного метода, как рисование пикселя это знаете ли, моветон. Ничего полезно это вам не даст, а голову порядком замутит. Да и не припомню я чтобы были какие-нить игры с пикселями. Впрочем, если вам эта тема всеже интересна, то в конце статьи есть ссылки на подобные туторы. Задачи. Тем временем мы приступим к нашему второму уроку. В нем я расскажу как выводить текст на экран. Ранее я говорил, что сама библиотека имеет ряд функций облегчающих работу с графикой, устройствами ввода, таймерами и др. причем эти вещи совершенно не привязаны к какой-то конкретной системе, что в принципе ведет к легкой переносимости программ под разные платформы. Тем не менее, некоторые полезные функции в СДЛ не представлены, такие как звук, загрузка картинок, сеть... Но не стоит расстраиваться, потому как сама библиотека задумывалась таким образом, что ее без труда можно расширить под ваши нужды и для этого создан целый ряд дополнительных модулей. Например, для загрузки картинок разных форматов можно подключить SDL_Image, для музыки SDL_Mixer, сеть SDL_net , а шрифты SDL_Font.
Подключаем. Давайте подключим в нашу программу шрифты и напечатаем на экране, что-нибудь такое, ммм эдакое: чтоб ласкало слух и радовало глаз. Например, всем известную фразу: "To be or not to be." Ну вот, мне нравится, блин. Вы можете написать что-нить другое. Но сначала займемся выводом классики. Для этого нам необходимо
скачать эту библиотеку. Она как и все находится на офф.сайте СДЛ в разделе projetcts вот ссылка:
http://www.libsdl.org/projects/SDL_ttf/
Переходим и скачиваем этот вот файл:
devel - в названии файла говорит о том, что этот архив для тех кто будет, что-то с этим разрабатывать(development). И в дальнейшем, если вам будут нужны какие-нибудь библиотеки для подключения к проекту, надо будет качать их devel-версии. В этих архивах находятся 4 вещи: - папка include - хидеры (заголовки)
- папка lib - где лежит .lib
- там же dll - необходимые динамические библиотеки
- лицензионные соглашения этой библиотеки
Надо разобраться что с этим добром делать. Если вы уже знаете это, то пропустите абзац, остальные - за мной. ОК, мы создали новый СДЛ проект, теперь нам надо "приютить" файлы, которые находились в архиве SDL_ttf. Для этого кидаем SDL_ttf.h в папку include нашей SDL. А файл SDL_ttf.lib в папку lib. Дальше я буду приводить скриншоты, как это выглядит у меня :
Потом первым делом включаем заголовочный файл нашей библиотеки вслед за основным хидером SDL (SDL.h) .....
#include <SDL.h> #include <SDL_TTF.h> ..... Затем идем в опции линковщика проекта(Project->Build options...->Linker Settings) и прицепляем наш lib-файл. У меня так:
И последним делом нам надо разместить dll-ки из нашего архива(zlib1.dll, SDL_ttf.dll, libfreetype-6.dll) . Я уже говорил о том куда их класть, но повторюсь(в последний раз), что их можно положить в: - папку вашего проекта, это хороший вариант, не забывайте все нужные dll-ки таскать с exe-шником
- папку bin в Code::Blocks, в том случае если у вас много СДЛ-проектов, а dll-ки таскать из папки в папку жутко ломает.
- папку system32 в windows-е
Скажу вам честно, что я поступил самым тривиальным третьим путем, скинул все в windows\system32 знаю что засирание виндуса ни к чему хорошему не приводит, но тем не менее, я сделал именно так. В принципе так подключается любая библиотека SDL. Ну подключить-то мы ее подключили, а как ею пользоваться? Об этом дальше. Пробуем. Ага потираем ручки. Как и в случае с инициализацией самой SDL и ее подсистем, TTF - надо сначала инициализировать. Вы мне не поверите, но тысяча чертей, это проще пареной репы: TTF_Init(); А деинциализация: TTF_Quit(); Де жа вю... очень похожие названия. Но ведь в этом и суть, что для выполнения одной и той же функции для однотипных заданий, использовать функции сходные в названиях, а самое главное "говорящие" названия функций - это есть самый правильный подход к оформлению программы. Не забывайте, что вашу программу, возможно будут читать другие люди, и сколько бессонных ночей они потеряют пытаясь понять что делает функция sdfgljkgk(), одному Богу известно. Да и вам самим будет проще потом разобраться :) А теперь возвращаясь к теме нашего повествования, хочу отметить, что TTF_Init() при верной инициализации выдает 0, а при неверной -1. За этим как всегда надо следить, поэтому аккуратно оборачиваем её в if с проверкой: if (TTF_Init() == -1) { printf("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); }
как видите функция возвращающая значение ошибки, так же имеет "говорящее" название TTF_GetError(). О том инициалицирована ли библиотека, можно узнать функцией TTF_WasInit(); Теперь не мешало бы поговорить о фонтах(font-шрифт). Эта библиотека работает со шрифтами ttf( TrueTypeFont) рекомендую нагуглить парочку таких бесплатных шрифтов для наших эксперементов, можно конечно воспользоваться стандартными виндовсовскими, но они залицензированы и использовать их в коммерческом проекте нельзя, только если вы не купите право их использовать у МС. В интернете есть тонны бесплатных шрифтов, надо тока поискать любой на ваш вкус. Теперь нам необходимо загрузить шрифт и указать его размер, чтобы использовать в дальнейшем для написания нашего предложения. Делает это функция TTF_OpenFont, вот как надо ее использовать: TTF_Font* TTF_OpenFont( font_name, font_size ); Она принимает 2 параметра, название шрифта и его размер, а возвращает указатель на объект типа TTF_Font. Итак чтобы инициализировать конкретный шрифт, нам надо создать указатель и "заполнить" его загруженным шрифтом. Сам шрифт должен лежать в папке проекта, думаю это очевидно. TTF_Font *my_font = NULL; // указатель на шрифт my_font = TTF_OpenFont("Diablo Heavy.ttf", 36); // грузим шрифт if (my_font == NULL) { // если он не загрузился и остался NULL printf("Unable to load font: %s \n", TTF_GetError()); return false; } И после не забываем проверить, что он загружен. Таков хороший стиль программирования. Я скачал и использовал шрифт, отображающий текст в стиле игры Diablo - он называется "Diablo Heavy.ttf", скачать его можно вот по этой ссылке http://www.creamundo.com/index.php?lang=en&letra=d&fuente=Diablo+Heavy+TTF или еще где-нибудь. Мне кажется что он наиболее подходит для этой фразы, но вы можете использовать любой другой шрифт, тока не забудьте поменять название в функции загрузки TTF_OpenFont ;) Итак, шрифт загружен и теперь его надо бы отобразить, а иначе зачем мы это все затевали? Так вот чтобы нам отобразить текст, надо знать как минимум 3 вещи, это координаты в окне, где мы будем выводить наш текст, цвет текста и цвет фона под текстом. Начнем с цвета. Цвет. Абсолютно любой цвет образуется путем смешивания трех основных цветов красного(red), зеленого(green) и голубого(blue). Подобно тому как художник на мольберте смешивает цвета красок для того чтобы отбразить нужный цвет, мы можем сделать это и в компьютере. Нам необходимо будет лишь задать контраст этих цветов от 0 до 255 и мы получим нужный нам цвет. Вы наверное замечали в различных редакторах, что выбирая цвет показывается так же уровни этих трех цветовых компонентов.
Из рисунка видно, что этот цвет... эмм (блин, во выбрал-то, и названия такого не знаю, будет считать серо-бур-малиновый) можно представить как три цвета красный, зеленый и синий в такой вот пропорции( 54,154,56 ). В SDL цвет задается структурой SDL_Color: typedef struct{ Uint8 r; // Величина красной составляющей Uint8 g; // Величина зеленой составляющей Uint8 b; // Величина синей составляющей Uint8 unused; // неиспользуемый параметр } SDL_Color; Создав объект этого типа и заполнив цветовые компоненты, значениями - мы в итоге получим нужный цвет в представлении SDL. Заполнить структуру можно двумя способами: 1) задать значения поэлементно SDL_Color back_color; back_color.r = 255; back_color.g = 0; back_color.b = 128; 2) а можно задать более простым способом, в одну строку SDL_Color back_color = { 255, 0, 128 }
Второй вариант предпочтительнее, по той причине, что мы можем дефайнами (#define) предопределить цвета и более удобно использовать их. Например: #define BLACK_COLOR 0,0,0 #define WHITE_COLOR 255,255,255 #define RED_COLOR 255,0,0 #define BLUE_COLOR 0,0,255
Создание SDL-цвета сокартится, до: SDL_Color fore_color = { RED_COLOR }; Теперь поговорим о координатах для отображения нашего текста в окне SDL. Координаты. Для того чтобы указать где отображать текст нам достаточно двух цифр, координат по оси X и по оси Y от левого верхнего края окна SDL-приложения. Но в функции, которая нам будет необходима, мы должны передавать тип SDL_Rect. Так, я ничегошеньки про эту функцию не сказал. Что же надо восполнить упущение. int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect); Эта функция копирует одну поверхность(сурфейс) на другую. Я уже говорил, что в SDL очень много работы с сурфейсами. Пока мы знали только сурфейс окна, созданную функцией SDL_SetVideoMode, для текста мы тоже должны создать свой сурфейс, заполнить его текстом и положить на сурфейс окна(ну то есть проще, говоря, отобразить надпись в окне, а технически получается - скопировать сурфейс на сурфейс). И как раз для этого нужна эта функция. 1)Первый параметр SDL_Surface *src сурфейс источника, 2)второй: SDL_Rect *srcrect прямоугольник сурфейса источника, если второй параметр NULL, то копируется вся сурфейс 3)третий: SDL_Surface *dst сурфейс получателя(у нас это экран(screen)), 4)четвертый: SDL_Rect *dstrect прямоугольный участок сурфейса получателя , если 4 параметр равен NULL - то копируется в координаты (0,0) - левый-верхний угол получателя. Так что если быть совсем точным, то эта функция копирует заданный прямоугольник одного сурфейса в прямоугольник другого. Зная это, можно уже прикинуть как будет выглядеть наша функция при вызове: SDL_BlitSurface(textSurface, NULL, screen, &textLocation); ,где textSurface - сурфейс текста, screen - сурфейс окна, textLocation - координаты прямоугольника отрисовки (текста в окне). Так выглядит структура прямоугольного участка SDL_Rect: typedef struct{ Sint16 x, y; // Позиция верхнего левого угла прямоугольной области Uint16 w, h; // Ширина и высота прямоугольной области } SDL_Rect;
Заполнять объект этого типа можно так же простым способом как и SDL_Color, что сократит время и усилия: SDL_Rect textLocation = { 100, 200, 0, 0 }; TextSurface. Остался один нерешенный вопрос, как создать сурфейс с нашим текстом? В библиотеке SDL_ttf для этого есть 3 основые режима: 1) Solid - рисует быстро, но не очень качественно и потому используется в тех случаях, когда важнее скорость 2) Blended - обратная Solid, то есть медленная, но за качество "отвечает" 3) Shaded - отличная от первых двух, прежде всего тем, что тескто можно рендерить с бэк-колором, то есть помимо цвета текста надо задавать цвет фона на котором этот текст будет отображаться. Быстрый как Solid, и качественный как Blended. Отвечают за это 3 функции: TTF_RenderText_Solid, TTF_RenderText_Blended и TTF_RenderText_Shaded. Мы воспользуемся здесь третьим вариантом. Объявим сурфейс для текста: SDL_Surface* textSurface = NULL; Заполним сурфейс классической фразой, в Shaded режиме: первый параметр -шрифт, второй - текст, третий цвет текста, четвертый - цвет фона под текстом. textSurface = TTF_RenderText_Shaded(my_font, "To be or not to be.", fore_color, back_color);
проверим обязательно, что сурфейс создана, иначе вываливаемся в панике: if(textSurface == NULL) { printf("Unable to load textsurface: %s \n", SDL_GetError()); return false; }
Ну а дальше, когда мы получили сурфейс текста, можно уже копировать ее на сурфейс экрана. Об этом я уже рассказывал ранее, какой функцией это делается. Но надо учесть и еще один факт, что когда мы создавали сурфейс окна SDL мы разрешили делать двойную буфферизацию экрана флагом SDL_DOUBLEBUF. И чтобы отобразить нашу поверхность и увидеть текст, надо флипнуть буффер экрана SDL_Flip(screen); по завершении мы должны освободить все используемые нами сурфейсы. У нас их 2: сурфейс окна и сурфейс текста. И если сурфейс окна освобождается командой SDL_Quit(), то остальные сурфейсы мы должны удалить вручную специально предназначенной для этого функцией SDL_FreeSurface(сурфейс). Следите за этим внимательно, это не только хороший стиль программирования, но и забота о пользователе вашей программы. Не забудьте, что как бы ваша программа ни была хороша, но если она не освободит ресурсы, то по ее завершении пользователю, возможно, надо будет рестартануть систему, а пару раз рестартанув, он уже наврятли захочет еще раз запустить ваше творение. Это всё, а вот текст программы, которая у нас получилась. #include <SDL.h> #include <SDL_TTF.h>
#define BLACK_COLOR 0,0,0 #define WHITE_COLOR 255,255,255 #define RED_COLOR 255,0,0 #define BLUE_COLOR 0,0,255
const int WINDOW_WIDTH = 640; const int WINDOW_HEIGHT = 480;
int main ( int argc, char** argv ) {
if(SDL_Init( SDL_INIT_EVERYTHING ) == -1) // инициализируем видео-подсистему { printf("SDL_Init failed: %s\n", SDL_GetError() ); return false; } if (TTF_Init() == -1) { printf("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); } TTF_Font* my_font = NULL; my_font = TTF_OpenFont("Diablo Heavy.ttf", 36); //ARIAL.TTF", 24);
if(my_font == NULL) { // если он не загрузился и остался NULL printf("Unable to load font: %s \n", TTF_GetError()); return false; }
SDL_Surface* screen = NULL; screen = SDL_SetVideoMode( WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_HWSURFACE | SDL_DOUBLEBUF ); if(screen == NULL) { printf("Unable to load screen: %s \n", SDL_GetError()); return false; }
SDL_Color fore_color = { RED_COLOR }; //255, 255 }; SDL_Color back_color = { BLUE_COLOR };
SDL_Surface* textSurface = NULL; textSurface = TTF_RenderText_Shaded(my_font, "To be or not to be.", fore_color, back_color); if(textSurface == NULL) { printf("Unable to load textsurface: %s \n", SDL_GetError()); return false; }
// Pass zero for width and height to draw the whole surface SDL_Rect textLocation = { 100, 200, 0, 0 };
SDL_BlitSurface(textSurface, NULL, screen, &textLocation); // скопируем тектовую сурфейс на экранную screen
SDL_Flip(screen); // отобразим сарфейс screen
SDL_Delay(3000); // подождем
SDL_FreeSurface(textSurface); // освободим поверхность для текста
TTF_CloseFont(my_font); // закроем используемый шрифт
TTF_Quit(); // закроем библиотеку шрифтов
SDL_Quit(); // закроем SDL
return 0;
}
А вот что вы должны получить в итоге выполнения этой программы.
Выведение. Отлично! Вы справились и с этим! И теперь, когда вы совладали с выводом текста, подумаем насчет этого предложения. Я склоняюсь к варианту "быть", нежели чем "не быть" :) Думаю и вы тоже. На очереди отрисовка спрайтов. Но это уже в следующий раз. Чао.
|