Теперь у нас есть полный набор функций для наше игры, и мы можем приступать к написанию функции вывода.
Наиболее распространенный способ организации функции вывода это привязать эту функцию к обработчику события перерисовки. Но не всегда бывает удобно использовать стандартный обработчик OnPaint . Это связано с тем что большинство событий формы и компонентов являются аналогами соответствующих сообщений операционной системы.
Конечно, не все сообщения имеют такие аналоги, поскольку их (сообщений) очень много, несколько сотен. Но одно известно точно что иногда требуется доступ к событиям напрямую без посредников, в роли которых выступаю функции написанные разработчиками среды программирования, которые вносят ненужные задержки и уменьшают производительность.
У программистов всегда будет возникать потребность обрабатывать сообщения, не имеющие аналогов в списке событий, либо самостоятельно перехватывать сообщения, для которых есть аналоги среди событий формы и компонентов. Заделать это не сложно тем более это дает неплохой прирост производительности для нашей программы.
В Delphi для реализации собственного обработчика необходимо написать всего лишь следующую строку:
procedure TMainWindow.WMPaint(var Msg: TWMPaint);
А далее осуществлять вывод. Рассмотрев по внимательнее код обработчика мы можем заметить что в нашей игре используется только одна форма и весь вывод, как игрового процесса, так и меню игры, происходить на одну форму. С помощь двух логических переменных мы осуществляем выборку того, что нам выводить
procedure TMainWindow.WMPaint(var Msg: TWMPaint);
var
ps : TPaintStruct;
begin
BeginPaint(Canvas.Handle, ps);
glViewPort (0, 0, ScreenWidth, ScreenHeight); // область вывода
glClear(GL_COLOR_BUFFER_BIT);
if(isGame=False) then begin
…
end
else begin
if( not isStoped ) then begin
…
end
end;
SwapBuffers(Canvas.Handle);
EndPaint(Canvas.Handle, ps);
end;
Как уже говорилось ранее для реализации анимации необходимо изменять изображение перед каждым выводом, но это приемлемо только в мультипликации, но не в коем случае для игры так как программист никогда не сможет предугадать что будет делать пользователь в следующий момент времени. Для этого процесс вывода и редактирования оделяют и обрабатываются параллельно дуг от друга. Если в выводом изображения все понятно, ведь она будет осуществляться каждый раз в момент появлении сообщения о перерисовки. То что же нам делать с обработчиком преобразований который тоже должен обрабатываться периодически.
Конечно, нам нужен таймер, но какой? Для простаты можно воспользоваться стандартным компонентом. Но я предлагаю воспользоваться системным таймером основанном на функциях API.
Для хранения информации для идентификации таймера нам понадобится переменная целого типа.
var
TimerId : uint;
Идентифицировать таймер необходимо потому, что у приложения их может быть несколько. Для включения таймера вызывается функция API SetTimer, где задается требуемый интервал таймера, а также указывается тип таймера, будет ли работать как таймер или одноразовая задержка.
UINT SetTimer(HWND hWnd, UINT nIDEvent,UINT uElapse, TIMERPROC lpTimerFunc);
Функция должна обрабатывать сообщение WM_Timer.
Как всегда в Windows, созданные объекты должны по окончании работы удаляться, дабы не поглощали ресурсы. Для удаления таймера вызываем Функцию KillTimer .
BOOL KillTimer(HWND hWnd, UINT uIDEvent );
В третьей главе мы упоминали, что для спрайтовой анимации необходимо много картинок. Картинки хорошего качество и больших размеров могут занимать много места на жестком диске. Для решения этих проблем существует множество типов сжатия изображения. Некоторый из них способны уменьшить размер файла в несколько сотен, а то и тысяч, раз, но при этом теряя часть данных. Другие сохраняют данные полностью но плотность сжатия не такая высокая.
Для простого ознакомления попробуем придумать собственный формат изображений сжатием данных. Простейшим алгоритмом сжатия является алгоритм RLE. Алгоритм RLE действуют следующим образом. В файле ищется последовательность из одинаковых байтов и заменяется специальным кодом, в котором описано повторяющийся байт и количество повторений.
Еще одним из методов уменьшения размера файла изображения является применение индексации цветов т.е. присвоение определенной комбинации битов пикселя отвечающих за его цвет уникального номер. Причина эффективности данного метода заключается в том, что в большинстве на картинки присутствует от 1 до 25 процентов всех возможных цветов.
После все индексированные цвета записываются в начале файла и образуют таблицу индексов. Затем каждому пикселю присваивается индекс соответствующего цвета, и записываются в файл.
Попробуем реализовать следующий формат файла для хранения графических данных и использованием индексации и последующим сжатием по методу RLE. Для этого нам необходимо описать где и как будут хранится данные о размере изображения, размер палитры, и как будет осуществляется RLE сжатие.
В нашей игре реализован этот формат файла со следующее реализацией. На Рисунке 1 представлена общий вид файла и пример реализации метода сжатия изображения RLE.
Рисунок 1. структурная изображение файла (а). Пиксель без повторения (б.). Пиксель с повторением (в.).
В схеме указаны следующие блоки .
1. Размер изображения по вертикали.
2. Размер изображения по горизонтали.
3. Размер палитры.
4. Палитра. Под каждый цвет отводится по четыре биты и описываются форматом RGBA.
5. Данные
В данном формате применяется следующие ограничения. Максимальный размер палитры 128. Каждый пиксель в области данных описывается одним или тремя байтами. Сначала читается первый байт первые семь бит указывают на номер в палитре. Восьмой бит указывает на повторение. 0 – повторять бит не надо читаем следующий пиксель, 1 – необходимо прочитать следующие два байта и повторит этот пиксель это количество раз.
Формат прост, но достаточно эффективен так как смог упаковать 12 Мб в 700Кб. Самый распространенный форматом с использованием индексации и сжатием без потери качества стал формат GIF.