Спрайтовая анимация, или Охотники за инопланетянами

Вспомните двумерные игры, в которые вы играли на телеприставке. В таких играх существует множество объектов: игрок, противники, пули, бомбы и т. д. Каждый из этих объектов представляет собой растровое изображение (изображение формата BMP), которое может перемещаться по экрану. Такие растровые изображения получили название «спрайты».

Для создания спрайтовой анимации необходимо нарисовать один или более спрайтов и запрограммировать их перемещение и отрисовку на экране. При создании компьютерных игр приходится решать ряд дополнительных задач, таких как определение пересечения (столкновения) спрайтов, их взаимодействие и т. д.

Задачи урока

  • Управление движением спрайта.
  • Локальная очистка фона.
  • Моделирование физических явлений.

Выполнение упражнения

В этом упражнении мы заставим летать по экрану «летающую тарелку» (рисунок 1). Из упражнения вы узнаете базовые принципы создания спрайтовой анимации, которые могут пригодиться вам при создании компьютерных игр.

Рисунок 1. Окно приложения

1. Дизайн приложения

Рисовать НЛО будем прямо на форме. Поместите на форму только таймер. Как вы помните из урока № 10 — Анимация, таймер позволяет «оживлять» объекты. Для этого нужно при каждом «тике» таймера менять положение объекта. Если в уроке № 10 мы двигали строку, то сейчас будем перемещать изображение летающей тарелки.

2. Объявление глобальных переменных

Для того чтобы заставить объект двигаться, необходимо хранить и изменять его положение и скорость, т. е. нужны переменные для хранения этих параметров. Также необходима переменная для хранения самого объекта (т. е. рисунка летающей тарелки).

Положение в двумерном пространстве задаётся двумя координатами: x и y. Можно так и назвать переменные. Скорость тоже можно разбить на две составляющие: горизонтальную и вертикальную. Скорость в физике обозначается буквой v, поэтому назовём переменные, которые хранят скорость, vx и vy (горизонтальная и вертикальная соответственно). Для хранения рисунка нужно описать переменную типа TBitmap. Назовём её, скажем, UFO (так НЛО называется по-английски). Эти переменные должны быть глобальными, т. к. должны быть доступны всем процедурам модуля.

Добавьте в описание глобальных переменных переменные xyvxvy и UFO:

var
    Form1: TForm1;
    x, y, vx, vy: integer;
    UFO: TBitmap
Листинг 1

Теперь можно приступать к созданию обработчиков событий.

3. Создание НЛО

1. Загляните в папку «Обеспечение». Там вы найдёте файл ufo.bmp. Этот файл содержит изображение летающей тарелки, которое будет использоваться в нашей программе. Скопируйте этот файл в ту же папку, куда вы сохранили проект.

Конечно, то, что файл ufo.bmp находится в той же папке, что и проект, ещё не означает, что он будет автоматически загружаться при запуске программы. Загрузку файла необходимо задать в программе явно. Причём загружаться файл должен в самом начале — при старте приложения. Таким образом, наиболее подходящее место для загрузки рисунка — обработчик события OnCreate формы.

2. Создайте этот обработчик события и запишите в нём следующий код:

Procedure TForm1.FormCreate(Sender: TObject);
Begin

  UFO := TBitmap.Create;
  UFO.LoadFromFile('ufo.bmp');
  // Устанавливаем начальные координаты НЛО 
  X  := 0;
  Y  := 0;
  // Устанавливаем скорость НЛО по осям x и y
  Vx := 10;
  Vy := 3

End;
Листинг 2

Поясним код:

  • создаём новый объект типа TBitmap и присваиваем его переменной UFO. После выполнения этой строчки уже можно работать с этой переменной;
  • загружаем рисунок из файла с именем ufo.bmp, который должен находиться в папке с проектом;
  • задаём начальное положение и скорость летающей тарелки. Таким образом, в начале тарелка находится в левом верхнем углу формы, её начальная скорость: на 10 вправо и на 3 вниз. Здесь скорость вычисляется, разумеется, не в километрах в час, а в пикселях за «тик», т. е. на сколько пикселей (экранных точек) сместится НЛО за один «тик» таймера.

4. Уничтожение НЛО

После закрытия программы объект UFO с изображением НЛО останется в оперативной памяти компьютера. Его нужно удалить самостоятельно с помощью метода Free.

Создайте обработчик события OnClose формы и запишите в нём следующий код:

Procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
Begin
 
  UFO.Free;

End;

Листинг 3

5. Перемещение НЛО

Теперь мы подошли к самому важному — непосредственно к рисованию и перемещению летающей тарелки. Рисовать и перемещать тарелку, как уже говорилось, нужно при каждом «тике» таймера.

1. Создайте обработчик события OnTimer объекта Timer1 и запишите в нём следующий код:

procedure TForm1.Timer1Timer(Sender: TObject);
begin

  X := x + vx;
  Y := y + vy;
  if (x+64 >= Form1.ClientWidth) and (x<0) then vx := vx*-1;
  if y+32 >= Form1.ClientHeight) and (y<0) then vy := vy*-1;
  Form1.Canvas.Draw( x, y, UFO );

End;
Листинг 4

Поясним код:

  • для того чтобы переместить НЛО, прибавляем к его текущей позиции скорость по координатам х и y;
  • рано или поздно летающая тарелка вылетит за пределы окна. Чтобы этого не случилось, необходимо проверять: достигла ли летающая тарелка границ формы, и если достигла, то заставить её лететь обратно. Размеры нашего рисунка: 64×32 пикселя. Размеры клиентского пространства внутри формы можно узнать из свойств ClientWidth и ClientHeight. Таким образом, условие «НЛО вылетел за правый край» будет выглядеть так: x+64 >= Form1.ClientWidth. Если это условие выполняется, то скорость по оси OX должна стать не 10, а –10. Аналогичным образом записываются условия для левого, верхнего и нижнего краёв формы;
  • рисуем на форме НЛО в заданных координатах. Для рисования любого графического объекта класса TBitmap используется метод Draw свойства Canvas (класса TCanvas). Этот метод имеет три параметра: первые два определяют позицию, где следует нарисовать объект, а третий — что именно требуется нарисовать.

2. Запустите программу. Вы увидите, что НЛО перемещается по форме, но при этом оставляет за собой след (рисунок 2).

Рисунок 2

6. Локальная очистка фона

Чтобы избавиться от следа, необходимо каждый раз стирать старое изображение, перед тем как нарисовать новое. Очистке подлежит только та область размером 64×32, которую занимала тарелка в предыдущем кадре.

Раз необходимо использовать предыдущие координаты тарелки, следовательно, очистку нужно делать в самом начале процедуры, ещё до того, как позиция была изменена (т. е. до строчки x := x+vx;). Для очистки фона по-прежнему будем использовать метод FillRect, но в качестве параметра вместо ClipRect, который означает всю отображаемую область, необходимо передать меньший прямоугольник, соответствующий позиции летающей тарелки.

1. Запишите в обработчик дополнительно следующие строки:

procedure TForm1.Timer1Timer(Sender: TObject);
begin

  // Стираем НЛО
  Form1.Canvas.Brush.Color := clBlack;
  Form1.Canvas.FillRect( Rect( x, y, x+64, y+32 ) );
  // Перемещаем НЛО
  X := x + vx;
  Y := y + vy;
  // Проверяем выход НЛО за границы формы
  if x+64 >= Form1.ClientWidth  then vx := -10;
  if y+32 >= Form1.ClientHeight then vy := -3;
  if x < 0 then vx := 10;
  if y < 0 then vy := 3;
  // Рисуем изображения НЛО
  Form1.Canvas.Draw( x, y, UFO );

End;
Листинг 5

Поясним код:

  • Прежде чем отобразить прямоугольник, необходимо установить цвет кисти в чёрный цвет.
  • Для того чтобы задать прямоугольник (т. е. значение класса TRect), зная его координаты, используется функция Rect. Она имеет 4 параметра: левый край, верхний край, правый край, нижний край.

2. Запустите приложение, и вы опять обнаружите неприятный сюрприз — тарелка опять оставляет след, но на этот раз — чёрный (рисунок 3).

Рисунок 3

Проблема в том, что очистка экрана, которая реализована сейчас, подразумевает, что НЛО должен летать на сплошном чёрном фоне, однако нигде этот первоначальный чёрный фон окна не был задан.

3. Измените значение свойства Color на clBlack у формы.

4. Запустите приложение. Теперь НЛО движется без проблем!

7. Неуловимая тарелка!

Наверняка однообразный полёт НЛО быстро вам надоест. Хочется, чтобы тарелка не просто летала, но и, скажем, реагировала на мышь. Давайте сделаем так, чтобы тарелка «боялась» курсора мыши, улетая от него. Для того чтобы реагировать на положение курсора мыши, нужно всегда знать, где он находится.

1. Опишите две новые глобальные переменные: mx и my типа Integer. Они будут предназначены для хранения позиции курсора.

2. Создайте обработчик события OnMouseMove. Как вы помните, это событие происходит, когда меняется положение курсора мыши. Разумеется, в этом случае необходимо сохранить новое положение курсора в переменных mx и my:

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
Begin

  mX := X;
  mY := Y;

end;
Листинг 6

Обратите внимание! В данном случае параметры X и Y заменяют глобальные переменные с теми же именами. Таким образом, именно в этой процедуре переменные X и Y — это не координаты летающей тарелки, а координаты курсора мыши.

3. Вернитесь к обработчику события OnTimer компонента Timer1. Здесь необходимо запрограммировать взаимодействие курсора мыши (координаты которого хранятся в переменных mx и my) и летающей тарелки (координаты которой хранятся в переменных x и y).

Очень красиво будет смотреться, если на НЛО будет действовать отталкивающая магнитная сила, идущая от курсора. Как известно из курса физики, магнитное воздействие обратно пропорционально квадрату расстояния. Таким образом, необходимо вычислить квадрат расстояния между курсором мыши и центром НЛО (рисунок 4).

Рисунок 4

Расстояние в данном случае будет гипотенузой треугольника. По теореме Пифагора квадрат гипотенузы равен сумме квадратов катетов. Катеты в данном случае — это расстояние вдоль осей координат между курсором мыши и центром НЛО. Размер НЛО: 64 на 32 пикселя. Таким образом, координаты центра: x+32y+16.

Отсюда следует, что квадрат расстояния можно посчитать так: d:= sqr(x+32-mx)+sqr(y+16-my). Функция sqr — это стандартная функция возведения в квадрат (от англ. square — квадрат).

Запишите эту формулу в самом начале обработчика события OnTimer (сразу же после слова Begin).

4. Опишите переменную d как локальную переменную процедуры OnTimer.

5. Теперь необходимо менять скорость НЛО в зависимости от действующей на него «магнитной» силы. Обратите внимание, что в формуле происходит деление на d. Если d станет равным нолю (это может случиться, если попасть мышью прямо в центр НЛО), то возникнет ошибка деления на ноль, поэтому необходимо обязательно выполнять проверку. Запишите в обработчике события OnTimer, сразу же после формулы d:=sqr(x+32-mx)+sqr(y+16-my); следующий код:

if d<>0 then begin
   vx := vx + round( (x+32-mx)/d );
   vy := vy + round( (y+16-my)/d );
end;
Листинг 7

Эти строчки, в принципе, можно записать в любом месте обработчика. Разница, конечно, есть, но незначительная.

6. Запустите программу и посмотрите, что получилось. Вы обнаружите, что НЛО почти никак не реагирует на курсор. В чём дело? Просто интенсивность «магнитного поля» слишком быстро иссякает с ростом расстояния, поэтому нет никакого видимого воздействия, даже если курсор навести прямо на НЛО. Необходимо усилить магнитную силу.

7. Измените две предыдущие строки так:

Vx := vx + round( (x+32-mx)*100/d );
Vy := vy + round( (y+16-my)*100/d );
Листинг 8

8. Запустите программу и попробуйте догнать НЛО курсором мыши.

8. Коротко о главном

Спрайт — растровое изображение, обозначающее какой-либо объект на экране монитора.

Основой спрайтовой анимации является таймер.

Класс TBitmap служит для хранения рисунка в оперативной памяти компьютера.

При перемещении спрайтов по экрану необходимо стирать их старое изображение.

Метод Draw класса TCanvas служит для отображения растрового изображения в текущей канве с заданными координатами.

Созданный методом Create объект перед завершением работы приложения необходимо удалить методом Free.

9. Выполнение заданий

Задания I уровня сложности

1. Создайте приложение, неподвижно отображающее НЛО в центре экрана. При наведении на НЛО курсора мыши он исчезает и появляется в другом месте формы со случайными координатами. НЛО не должен выходить за пределы формы.


2. Создайте приложение, циклически перемещающее НЛО слева направо. Причём, если НЛО полностью пропадает за правой границей формы, он вылетает с левой. Координата НЛО по оси Y задаётся в коде программы.


3. Дополните приложение так, чтобы НЛО не отталкивался от краёв формы, а исчезал за ними и после этого появлялся с другого края (т. е. пространство замыкается по горизонтали и вертикали).


4. Создайте приложение, неподвижно отображающее НЛО в центре экрана. При щелчке мыши в клиентской части формы НЛО «перелетает» в точку с координатами щелчка. Скорость полёта НЛО задаётся в программном коде.


5. Создайте приложение, циклически перемещающее НЛО по периметру формы (как показано на рисунке):

Расстояние от краёв формы до НЛО составляет 5 пикселей.


6. Создайте приложение, производящее запуск ракеты. Ракета рисуется самостоятельно в графическом редакторе. Её размер — 20х80 пикселей. Приложение содержит кнопки «Установка» и «Пуск». При нажатии на кнопку «Установка» ракета появляется снизу формы. 
При нажатии на кнопку «Пуск» ракета, постепенно набирая скорость, взлетает. Ускорение ракеты задаётся с помощью однострочного редактора в пикселях за один «тик» таймера. Результат работы приложения показан на рисунке:


7. Создайте приложение, в котором летают сразу две тарелки. Для разнообразия можете использовать файл redufo.bmp из папки «Обеспечение», содержащий изображение красной летающей тарелки. Попробуйте сделать так, чтобы тарелки отталкивались друг от друга. Результат работы приложения показан на рисунке:

Задания II уровня сложности

1. Создайте приложение так, что, если пользователь щёлкнет по НЛО левой кнопкой мыши, НЛО постепенно падает вниз. При падении НЛО касается нижнего края формы и останавливается.


2. Создайте игру, упрощённый вариант игры в теннис. По полю бегает мячик, отражаясь от стенок (верхней, левой и правой) формы. Внизу находится бита, управляемая мышью. Если мячик попадает на биту, то отскакивает от неё. Если не попадает, то игра заканчивается. Результат работы приложения показан на рисунке: