LXF103:Стрелялка
Yaleks (обсуждение | вклад) (Новая: {{Цикл/Стрелялка}} == Наш движок изнутри == : ''ЧАСТЬ 3 Пользоваться инструментом, не понимая, как он работа...) |
Текущая версия на 16:30, 1 марта 2009
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Содержание |
[править] Наш движок изнутри
- ЧАСТЬ 3 Пользоваться инструментом, не понимая, как он работает – не дело для настоящего линуксоида. Разберитесь в механике Ingame вместе с Александром Супруновым – а заодно познакомьтесь с основами SDL.
Движок Ingame, которым мы пользовались на протяжении двух последних уроков, является набором оберток над функциями библиотеки Simple DirectMedia Layer (SDL), доступной для Linux, Windows, Mac OS X и множества других систем, включая даже AmigaOS. И сегодня мы попробуем разобраться в том, что происходило все это время «за кулисами».
SDL написана на языке C, поэтому все объекты, которыми она оперирует, представлены в виде структур. Имена этих структур начинаются с префикса SDL_, а центральное место среди них занимает SDL_Surface – это так называемая «экранная поверхность», на которой можно размещать изображения. Структура SDL_Surface имеет поля w и h, задающие высоту и ширину поверхности, а также поле format. Указатель на основной игровой экран, определенный в файле ingame.h, имеет тип SDL_Surface * и имя display.
[править] Начало всех начал
Как вы уже знаете, каждая программа, использующая Ingame, начинается с вызова функции screen(). Ее прототип выглядит так:
void screen(int w, int h);
В первую очередь, screen() выполняет инициализацию SDL посредством вызова:
SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO);
где объединенные при помощи операции «логическое ИЛИ» флаги имеют следующий смысл:
- SDL_INIT_VIDEO – активировать возможность работы с графикой.
- SDL_INIT_TIMER – активировать возможность использования встроенного в SDL таймера.
- SDL_INIT_AUDIO – активировать возможность проигрывания звуковых данных.
Затем инициализируется поверхность display. Это делается следующим образом:
display = SDL_SetVideoMode (w, h, 0, SDL_SWSURFACE | SDL_ANYFORMAT);
Функция SDL_SetVideoMode() принимает в качестве параметров ширину, высоту экрана, количество бит на пиксель, а также различные ключи, в том числе:
- SDL_SWSURFACE – предписывает использовать программную отрисовку графики. Режим нагружает центральный процессор, но совместим практически со всеми компьютерами.
- Его антипод, SDL_HWSURFACE, задействует аппаратное ускорение, что может как повысить, так и понизить быстродействие конечной программы.
- SDL_DOUBLEBUF – включает режим двойной буферизации, позволяет получить более «плавную» графику.
- SDL_FULLSCREEN – активирует полноэкранный режим.
Прошу обратить внимание на третий параметр: количество бит на пиксель. Он может принимать значения 8, 16, 24, 32 или 0, что соответствует глубине цвета, установленной в системе по умолчанию. Последний вариант наиболее переносим. Однажды я наблюдал повреждение графики в SDL-игре, запущенной на Amiga Pegasos. Выяснилось, что причиной была жестко зашитая глубина цвета (16) – замена ее на 0 решила проблему.
Следующим шагом мы устанавливаем заголовок окна:
SDL_WM_SetCaption («Linux GAMES», NULL);
а затем инициализируем библиотеку SDL_ttf
TTF_Init();
и загружаем шрифт, который будет использоваться в игре для вывода различных сообщений
fnt = TTF_OpenFont(«font.ttf», 20);
Функция TTF_OpenFont() принимает два аргумента (имя файла со шрифтом и размер в пунктах) и возвращает указатель на структуру TTF_Font, который мы сохраняем в глобальной переменной fnt.
С графикой разобрались; остался звук. За него отвечает библиотека SDL_mixer, которая инициализируется вызовом:
Mix_OpenAudio (44100, MIX_DEFAULT_FORMAT, 2, 2024);
Первый аргумент – частота дискретизации звука. Второй является стандартным ключом – просто запомните его. Далее указывается количество звуковых каналов (разумеется, «стерео») и размер буфера, отводимого под звуковые данные. Если вдруг в одно непрекрасное утро вы услышите, что звук начал «спотыкаться» – увеличьте последнее число, например, в два раза.
Две заключительных строки функции screen() –
frames = 0; then = SDL_GetTicks();
имеют отношение к подсчету и ограничению FPS. Это необходимо для того, чтобы программа выполнялась с одинаковой скоростью на любых компьютерах. Мы инициализируем счетчик кадров frames и сохраняем текущее значение таймера в глобальной переменной then, имеющей тип Uint32.
[править] Добавим игроков
Итак, экран готов – настало время загрузить спрайты. Для этих целей в ingame.h используется структура WiHi, содержащая указатель на соответствующий объект SDL_Surface.
По умолчанию резервируется место под 1900 объектов WiHi, причем все номера, начиная с 1000-го, используются движком для внутренних целей. При необходимости, можно увеличить число доступных спрайтов, просто изменив размерность массива.
Для загрузки спрайтов в формате BMP в Ingame предусмотрена функция loadsprite(), принимающая в качестве аргументов номер ячейки (num), в которую будет загружена картинка и имя файла (name). loadsprite() – обертка над двумя стандартными SDL-функциями: SDL_LoadBMP() и SDL_DisplayFormat().
В принципе, здесь можно ограничиться всего одним вызовом:
pic[num].tmp=SDL_LoadBMP(name);
но с точки зрения производительности рациональнее будет сразу же преобразовать спрайт в пиксельный формат дисплея (например, если оригинальная картинка использует 24 бита на пиксель, а на экране – всего 16, глубина цвета должна быть понижена). В итоге тело функции loadsprite() примет вид:
pic[num].tmp=SDL_DisplayFormat(SDL_LoadBMP(name));
Функция sprite(num, x, y), как вы, надеюсь, помните, выводит спрайт с номером num в точке с координатами (x,y). Происходит это следующим образом: для изображения устанавливается «цветовой ключ» (значение RGB, которое SDL будет считать прозрачным), а затем спрайт просто переносится в нужную точку экрана.
За прозрачность спрайта «отвечает» функция SDL_SetColorKey():
SDL_SetColorKey(pic[num].tmp,SDL_SRCCOLORKEY | SDL_RLEACCEL,SDL_MapRGB(pic[num].tmp->format,255,0,255));
Флаг SDL_SRCCOLORKEY указывает, что «цветовой ключ» (последний аргумент функции) следует считать прозрачным, SDL_RLEACCEL включает RLE-оптимизацию (при этом группы одинаковых пикселей кодируются по принципу число_пикселей X значение, что ускоряет копирование), а вызов SDL_MapRGB() возвращает значение «ключа» в требуемом формате (напомню, что Ingame считает прозрачными пиксели цвета (255,0,255).
Собственно копирование спрайта осуществляется функцией SDL_BlitSurface():
SDL_BlitSurface(pic[num].tmp,0,display,&shadow);
Первый аргумент (pic[num]. tmp) – исходная поверхность, второй – область исходного изображения, подлежащая копированию (мы передаем здесь NULL, что означает «вся поверхность»). Остальные два параметра имеют тот же смысл для целевой поверхности. shadow – переменная типа SDL_Rect, представляющая собой прямоугольник; координаты левого верхнего угла которого как раз равны x и y.
[править] Как сказать: «Game over»?
Помимо спрайтов, на экране время от времени нужно отображать и текстовые сообщения. Для этих целей в Ingame предназанчена функция print(сообщение, координата_x, координата_y). Она начинается с определения двух переменных
SDL_Color color = {255,255,255,0}; SDL_Rect dest= {(Sint16)x, (Sint16)y,0,0};
Первая из них, типа SDL_Color, задает цвет символов (белый), а вторая определяет точку, в которой будет выводиться сообщение. Текст надписи растеризуется на служебной поверхности (помните, все спрайт-слоты выше 1000 заняты Ingame для внутренних нужд) функцией TTF_RenderText_Blended():
pic[1000].tmp = TTF_RenderText_Blended(fnt, txt, color);
Назначение ее аргументов, думаю, должно быть ясно из их имен. Можно также использовать более скоростной вариант – TTF_RenderText_Solid(), но он проигрывает _Blended() по красоте вывода.
Остается только скопировать сообщение на экран при помощи уже известной нам функции SDL_BlitSurface():
SDL_BlitSurface( pic[1000].tmp, NULL, display, &dest );
и освободить память:
SDL_FreeSurface( pic[1000].tmp );
[править] Движущая сила
Итак, библиотека инициализирована, спрайты отрисованы; настало время заставить их двигаться. За это и многое другое отвечает функция fx(), реализующая основной цикл игры. Она требует предварительного объявления ряда глобальных переменных:
SDL_Event event; Uint8* keys;
event – специальная переменная событийного типа (зачем она нужна, будет ясно ниже), а keys содержит номера нажатых клавиш.
Мы также вводим ограничение FPS 75-ю кадрами в секунду.
#define FPS_LIMIT 75
Функция fx() отслеживает нажатие клавиш, ограничивает количество кадров, выводимых на экран монитора, очищает его перед отрисовкой очередной сцены, организует механизм «велосити» для движения объектов и меняет местами основной и теневой экраны, то есть реализует двойную буферизацию изображения. Разберем эти действия по шагам.
fx() начинается с опроса SDL на предмет произошедших событий. Этим занимается функция SDL_PollEvent(), которая принимает указатель на переменную событийного типа и возвращает TRUE, если что-то произошло. После этого мы можем изучить поле event.type, чтобы понять, что именно случилось, и отреагировать надлежащим образом:
while (SDL_PollEvent(&event)) { if (event.type==SDL_QUIT) { GAME=0; SDL_Quit(); } if (event.type==SDL_KEYDOWN) { if(event.key.keysym.sym==SDLK_ESCAPE) { GAME=0; SDL_Quit(); } } }
В первую очередь обрабатываются события, сигнализирующие о необходимости выхода из игры: системное SDL_QUIT (оно генерируется, если, например, пользователь щелкнул кнопку закрытия окна) и нажатие на клавишу Escape (событие типа SDL_KEYDOWN, код клавиши при этом сохраняется SDL в поле event.key.keysym.sym). Доступные коды клавиш перечислены в файле SDL/SDL_keysym.h, там же определены и константы наподобие SDLK_ESCAPE. Кстати, можно реагировать не на нажатие, а на отпускание клавиши – такое событие будет иметь тип SDL_KEYUP.
Затем наступает черед обработки нажатия на управляющие клавиши: «вправо», «влево», «огонь» и т.п. Здесь можно было бы воспользоваться тем же приемом, что и для Escape, но есть одно «но»: пользователь должен иметь возможность нажать на несколько клавиш одновременно (скажем, «влево» и «вверх» или «вверх» и «огонь»). Для таких целей SDL предоставляет в наше распоряжение функцию SDL_GetKeyState(), которая возвращает указатель на массив значений типа Uint8, представляющий собой текущее состояние клавиатуры в целом. Исходя из того, что именно было нажато, мы устанавливаем переменные-флаги (LEFT, RIGHT и т.д.), а также изменяем значение «велосити».
keys=SDL_GetKeyState(NULL); if(keys[SDLK_LEFT]) { LEFT=1; vel=velocity; } else LEFT=0;
Аналогично поступаем и с другими управляющими клавишами.
Далее в игру вступает механизм «велосити», который был достаточно подробно рассмотрен в LXF100/101, и, наконец, отрабатывает система ограничения FPS:
++frames; now = SDL_GetTicks(); if ( now > then ) { fps= (int)((double)frames*1000)/(now-then); } if ( fps > FPS_LIMIT ) {SDL_Delay(1000/FPS_LIMIT);}
Мы увеличиваем счетчик кадров, получаем текущее значение таймера и вычисляем FPS. Если полученный результат превышает заданный предел, функция SDL_Delay() приостанавливает работу программы на необходимый промежуток времени.
В качестве завершающего аккорда функция fx() меняет местами теневой и экранный буферы:
SDL_Flip(display);
делая видимым все, что было нарисовано на данной итерации цикла, и очищает вспомогательный экран, заливая его черным цветом при помощи непрямого вызова функции SDL_FillRect().
[править] Музыка и звук
Работа со звуком в Ingame по своей сути похожа на работу со спрайтами. Здесь также определяются структуры SiHi и MiHi, являющиеся обертками для указателей на Mix_Chunk (звуковой эффект) и Mix_Music (музыкальная композиция), соответственно. Структуры объединяются в массивы, насчитывающие 500 и 100 элементов. Для загрузки эффекта из файла формата WAV функция loadsound() вызывает Mix_LoadWAV(), которая, в свою очередь, принимает имя WAV-файла в качестве единственного параметра. Для воспроизведения эффекта используется функция sound(), эквивалентная вызову:
Mix_PlayChannel (-1, sn[num].tmp, 0);
-1 является указанием использовать первый доступный канал, 0 – число повторений, означающее, что звук будет проигран один раз.
С музыкой все обстоит ненамного сложнее. Соответствующие функции являются обертками над Mix_LoadMUS() и Mix_PlayMusiс(). Интерес представляет лишь функция stopmusic(), вызывающая
Mix_FadeOutMusic(1000);
для плавного затухания звука в течение 1 секунды.
[править] Что дальше?
Наш краткий экскурс в SDL подошел к концу. Конечно, мы не рассмотрели и десятой части возможностей этой замечательной библиотеки, но сделали главное – разобрались в том, как работает движок Ingame. Теперь, когда на нашей карте не осталось белых пятен, вы можете сами решать, куда двигаться дальше. Хотите – развивайте ingame.h: исходный код распространяется по лицензии GPL; хотите – напишите с его помощью собственную игру. Возможен так-же третий вариант – разберитесь в деталях с SDL и создайте что-нибудь с нуля. Главное, если у вас получится что-то стоящее – не забудьте сообщить нам об этом!