Как я и обещал, сегодня мы создадим первую спрайтовую анимацию. Спрайты долгое время были основой в игровой графике, да и демомейкеры их не обходили стороной. Поэтому их понимание необходимо и обязательно для любого кодера компьютерной графики.
Спрайтовая анимация - это просто копия мультипликации. Как создается мультик? Сначала рисуется персонаж. Чтобы заставить его двигать рукой, нужно просто нарисовать много картинок, где рука персонажа будет медленно изменять свое положение. Теперь если быстро пролистать все картинки, то получается плавное движение рукой.
Компьютерная анимация похожа на мультипликацию. Посмотри на рисунок 1. На нем нарисован самолет постепенно меняющий угол наклона. Если быстро пролистать каждый из кадров да еще и заставить кадры двигаться по оси Х, то получиться ощущение, что самолет летит.
Рисунок 3. Анимация самолёта
Именно таким способом создавались первые компьютерные игры. Ты думаешь, что в первом Doom монстры действительно были трехмерными? Нет, тогда мощности компьютеров на это не хватало. Просто дизайнеры подготовили большое количество картинок монстров в разных ракурсах, а программеры заставили нас поверить в трехмерность этих картинок. Ловкость рук и никакого обмана.
Итак, чтобы заставить нашу анимацию двигаться, нам понадобятся следующие дополнительные переменные:
X и Y - в этих переменных будет храниться текущее положение вертолета.
State - состояние самолета. Я подготовил картинку из 7 положений самолета и эта переменная будет указывать, какое именно положение сейчас должно отображаться.
LastXState - это для хранения времени, когда последний раз менялось состояние самолета. Так как самолет должен двигаться по оси Х (т.е. лететь слева на право), то здесь будет храниться Х позиция. Состояние будет меняться, когда он пролетит по оси Х на очередные 10 пикселей.
incparam - эту переменную я буду использовать для хранения значения приращения состояния самолета. Ну и сказанул. Короче, в начале здесь будет храниться 1. Когда надо изменить состояние, то я просто прибавляю это приращение к переменной State (State:=State+incparam). Это значит, что переменная State будет увеличиваться. Когда самолет получает состояние 7 (отображается 7 картинка), мне надо будет сделать так, чтобы переменная State стала уменьшаться, чтобы состояние самолета отражалось в обратном порядке. Для этого я меняю incparam на -1 и теперь State:=State+incparam будет уменьшать значение State.
Загружай пример из предыдущего номера Х, сейчас мы его подкорректируем. Для начала надо приготовиться к взлету и всем новым переменным присвоить начальные значения. Для этого где-нибудь в начале старта приложения (лучше даже до кода создающего окно) написать:
incparam:=1;
x:=0;
LastXState:=0;
y:=random(400);
Переменной Y я присваиваю значение функции random, которая возвращает случайное число в диапазоне от нуля до числа переданного в качестве единственного параметра. Конечно же, высоту полета можно было бы задать и постоянным числом, но я решил сделать его случайным. Пускай самолет летает где хочет, лишь бы не разбился :).
Теперь надо подправить наш цикл обработки сообщений. Раньше он был неэффективен, потому что выглядел так:
while (GetMessage(msg, 0, 0, 0)) do
begin
end;
Здесь мы получали очередное сообщение от ОС с помощью GetMessage и обрабатывали его. А что если сейчас нет для нас сообщений? Программа просто застывала на вызове функции GetMessage и могла ждать бесконечно.
Для компьютерной графики более эффективно будет использовать функцию PeekMessage, которая проверяет, есть ли для нас сообщение. Если есть, то она возвращает true, а если нет, то результатом будет false. С использованием этой функции логика обработки сообщений изменяется так:
1. Запускаем бесконечный цикл (while true do).
2. На каждом этапе цикла проверяем с помощью PeekMessage есть ли для нас сообщения.
3. Если нет, то можем заниматься своими делами, если да, то обрабатываем их.
4. Возвращаемся на пункт 2 и снова проверяем наличие сообщений.
while true do
begin
if PeekMessage(msg,0,0,0,PM_REMOVE)=false then
begin
// Для нас нет сообщений и здесь мы можем
// заниматься анимацией
end
else
begin
translatemessage(msg);
dispatchmessage (msg);
end;
end;
Теперь нужно написать логику движения самолета там, где я поставил комментарии в нашем новом обработчике событий. Напиши вместо комментариев следующее:
x:=x+1; //Увеличиваю позицию Х самолета
if X-LastXState>10 then
begin
State:=State+incparam; // Изменяю состояние
if State=6 then // Если уже 6-е состояние, надо уменьшать
incparam:=-1; // Приращение отрицательное
if State=0 then // Если 0-е состояние, надо увеличивать
incparam:=1; // Приращение положительное
LastXState:=x; // Запомнить текущую позицию самолета
end;
SendMessage(Handle, WM_PAINT, 0, 0); // Перерисовать
Здесь в первой строке кода я увеличиваю значение переменной Х на 1. После этого я проверяю, если переменная Х - LastXState больше 10, значит самолет уже пролетел 10 пикселей и можно менять его состояние (выводить следующий спрайт). Для этого я увеличиваю значение переменной State на значение записанное в incparam.
После этого проверяю состояние. Если переменная State равно 6, значит мы отображаем последний спрайт и дальше некуда. Поэтому я изменяю приращение incparam на отрицательное и в следующий раз переменная State будет уменьшаться. Ну а если State равна нулю, то приращение изменяю на положительное.
После изменения позиции самолета, я отсылаю сообщение системе с помощью SendMessage чтобы перерисовать экран. В качестве первого параметра я указываю указатель на мое окно, а второй - показывает тип сообщения (WM_PAINT - заставляет окно обновить свое содержимое).
Математику анимации мы уже сделали, теперь нужно вывести на экран сам самолет в уже рассчитанную позицию. Функция вывода не изменилась и мы будем использовать все ту же BltFast, но небольшая косметическая операция все же произойдет:
srcrect.Left:=0;
srcrect.Top:=State*90;
srcrect.Right:=180;
srcrect.Bottom:=State*90+90;
FBackgroundSurface.BltFast (X, Y, FTransImageSurface, @srcrect,
DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);
Вначале я заполняю структуру srcrect, которая будет указывать область, которую мы хотим скопировать. Так как у нас спрайты самолета выстроены в столбик, то левая и правая позиции неизменяемы - левая равна нулю, а правая равна 180.
Верх и низ области нужно рассчитывать, чтобы правильно вывести нужный спрайт в зависимости от состояния самолета. Высота каждого спрайта равна 90, значит верхняя позиция области копирования будет равна State*90, а нижняя еще на 90 пикселей больше.
Все готово к отображению самолета и можно вызывать функцию BltFast. В качестве первых двух параметров нужно только указать текущую позицию самолета - Х и Y.
Как видишь, компьютерная анимация не так уж и сложна. Это только на первый взгляд требуются глубокие знания математики. Для реализации анимации на основе спрайтов мы обошлись только простыми операциями сложения, вычитания и умножения (даже деления кажется не было). Лично я пока не встречал в компьютерной графике ничего такого, что не укладывалось бы в школьный курс алгебры и геометрии. Конечно же бывают сложные проекты, но большинство обходятся школьным курсом.