Игра «НЛО», или Стрельба по тарелочкам

Рынок компьютерных игр — самый большой в компьютерной индустрии. Но, тем не менее, каждый уважающий себя программист стремится написать свою игру, хоть и похожую на то, что уже было ранее.

Задача урока

  • Основы создания игрового процесса.

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

В этом уроке мы напишем небольшую игру, целью которой будет стрельба по движущемуся НЛО с помощью мыши (рисунок 1). Если игрок попадает в НЛО, он взрывается.

 а)  б)

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

1. Концепция игры

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

Что же нам необходимо для реализации такой задумки:

  • нарисовать пейзаж, на котором будет перемещаться НЛО. Этот пейзаж будет хранить компонент Image;
  • нарисовать НЛО и перемещать его по игровому полю. НЛО будет нарисован в отдельном компоненте Image, и перемещать будем сам этот компонент;
  • обработать событие щелчка по НЛО. Если игрок мышью попал по НЛО (т. е. по компоненту Image, на котором он нарисован), то происходит его взрыв. Различные стадии взрыва реализуются как последовательное отображение картинок (спрайтов).

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

1. Расположите компоненты, как показано на рисунке 2а.

 а)  б)

Рисунок 2

Заметьте, что в левый верхний угол формы мы поместили два компонента Image.

Компонент Timer1 необходим для перемещения НЛО по форме, а Timer2 — для реализации стадий взрыва НЛО, если в него попали.

2. Поменяйте заголовок формы на слово «Игра».

3. У компонента Image1 установите значение свойства Align в alClient. В результате компонент растянется на всю клиентскую часть формы (рисунок 2б).

4. У компонента Image2 установите значения свойств Transparent (прозрачность) и AutoSize (авторазмер) в True. В этом компоненте будет отображаться картинка с НЛО, и необходимо, чтобы фон НЛО был прозрачным, а размер компонента совпадал с размером картинки.

5. Переименуйте компоненты следующим образом:

Имя, которое было

Имя, на которое необходимо изменить

Image1

ImageFon
Image2 ImageUFO
Timer1 TimerMove
Timer2 TimerDestroy

 

3. Объявление переменных

Объявите следующие глобальные переменные:

p,                      // Счётчик этапов уничтожения НЛО
  x,y,                    // Координаты НЛО
  vx,vy:Integer;          // Скорость по осям X и Y НЛО
  Ufo1,                   // Изображение изначального вида НЛО
  Ufo2,Ufo3,Ufo4,Ufo5:TBitmap; 	// Изображения этапов уничтожения НЛО
Листинг 1

Назначение каждой из переменных понятно из комментариев.

4. Загрузка и уничтожение картинок

1. Картинка самого НЛО (UFO1.bmp) и четыре стадии его взрыва (UFO2.bmpUFO3.bmpUFO4.bmpUFO5.bmp) находятся в папке «Обеспечение». Скопируйте их в папку с проектом. Изображения этих картинок показаны на рисунке 3.

Рисунок 3

2. Теперь мы должны загрузить эти картинки в память компьютера после запуска приложения. Для этого создайте обработчик события OnCreate для формы и напишите в нём следующий код:

procedure TForm1.FormCreate(Sender: TObject);
begin

  Ufo1 := TBitmap.Create;
  Ufo2 := TBitmap.Create;
  Ufo3 := TBitmap.Create;
  Ufo4 := TBitmap.Create;
  Ufo5 := TBitmap.Create;
  Ufo1.LoadFromFile( 'UFO1.bmp' );
  Ufo2.LoadFromFile( 'UFO2.bmp' );
  Ufo3.LoadFromFile( 'UFO3.bmp' );
  Ufo4.LoadFromFile( 'UFO4.bmp' );
  Ufo5.LoadFromFile( 'UFO5.bmp' );
  ImageUFO.Picture.Bitmap := Ufo1;
  vx := 3;
  vy := 1;
end;
Листинг 2

Поясним код:

  • динамически создаём объекты типа TBitmap (картинка) для хранения исходного изображения НЛО и четырёх стадий его уничтожения;
  • загружаем в созданные объекты изображения с диска. В переменной UFO1 будет храниться изображение начального вида НЛО, а в переменных UFO2UFO3UFO4 и UFO5 — изображения различных стадий его взрыва;
  • отображаем начальный вид НЛО в компоненте ImageUFO. Для этого в свойстве Picture класса TPicture (обозначает картинку вообще) обращаемся к свойству Bitmap класса TBitmap (растровая картинка формата BMP) и присваиваем ему значение переменной UFO1;
  • в момент запуска приложения также устанавливаем скорость движения НЛО по осям Х и Y.

3. Теперь в момент запуска приложения картинки НЛО будут загружены в память. После закрытия приложения они там останутся. Необходимо самим позаботиться об освобождении памяти. Создайте обработчик события OnClose для формы и напишите в нём следующий код:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin

  UFO1.Free;
  UFO2.Free;
  UFO3.Free;
  UFO4.Free;
  UFO5.Free;

end;
Листинг 3

С помощью метода Free мы удаляем объекты.

4. Запустите приложение. На форме появится изображение НЛО, но оно пока никуда не движется и никак не реагирует на курсор мыши.

5. Закройте приложение.

5. Создание игрового фона

Для придания колорита игровому процессу необходимо нарисовать фон (пейзаж), на котором будет летать тарелка. Этот фон будет находиться на компоненте Image с именем ImageFon. Т. к. летать будет НЛО, то логично этот фон сделать космическим. Изобразим звёздное небо и планету. Фон должен отображаться непосредственно в момент запуска приложения.

1. Перейдите к обработчику события OnCreate для формы и дополните его следующим кодом:

procedure TForm1.FormCreate(Sender: TObject);
Var f,Color:Integer;
begin

  // Активация генератора случайных чисел
  randomize;
 
  // Закрашиваем фон чёрным цветом
  ImageFON.Canvas.Brush.Color := clblack;
  ImageFON.Canvas.FillRect( rect(0,0,form1.Width,form1.Height) );

  // Рисуем звёзды со случайными координатами и случайным цветом
  For f:= 1 To 5000 Do Begin
    Color := random(255);
    ImageFON.Canvas.Pixels[ random(form1.Width), random(form1.Height) ] := rgb(Color, Color, Color);
  End;

  // Отображаем планенту
  ImageFON.Canvas.Brush.Style := bsSolid;
  ImageFON.Canvas.Pen.Style   := psClear;
  ImageFON.Canvas.Brush.Color := clsilver;
  ImageFON.Canvas.Ellipse( 20, 20, 120, 120 );


  // Создание объектов для хранения изображений НЛО
  Ufo1 := TBitmap.Create;
  Ufo2 := TBitmap.Create;
  Ufo3 := TBitmap.Create;
  Ufo4 := TBitmap.Create;
  Ufo5 := TBitmap.Create;
  // Загрузка начального вида НЛО
  Ufo1.LoadFromFile( 'UFO1.bmp' );
  // Загрузка этапов уничтожения НЛО
  Ufo2.LoadFromFile( 'UFO2.bmp' );
  Ufo3.LoadFromFile( 'UFO3.bmp' );
  Ufo4.LoadFromFile( 'UFO4.bmp' );
  Ufo5.LoadFromFile( 'UFO5.bmp' );
  // Отображаем начальный вид НЛО
  ImageUFO.Picture.Bitmap := Ufo1;
  // Установка скорости движения НЛО
  vx := 3;
  vy := 1;

end;
Листинг 4

Поясним код:

  • объявляем локальные (т. е. видимые только в данной процедуре) переменные, которые будут использованы при рисовании космического фона: f — переменная цикла, Color — переменная для задания цвета звёздам;
  • активируем генератор случайных чисел. Он необходим, т. к. координаты и цвет звёзд будут задаваться случайно;
  • закрашиваем компонент ImageFon чёрным цветом;
  • в цикле рисуем 5 тысяч звёзд со случайными координатами и случайным цветом. Причём красная, зелёная и синяя составляющие цвета имеют одинаковое значение Color. В результате звёзды будут нарисованы в оттенках серого цвета;
  • отображаем планету в левом верхнем углу игрового пространства, диаметром 100 пикселей.

2. Запустите приложение. Теперь вместе с НЛО появится симпатичное звёздное небо с планетой (рисунок 4).

Рисунок 4

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

Теперь будем перемещать НЛО по игровому пространству аналогично тому, как мы перемещали НЛО в уроке № 17 — Спрайтовая анимация.

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

procedure TForm1.TimerMoveTimer(Sender: TObject);
begin

  // Изменение координат НЛО
  x := x + vx;
  y := y + vy;
  // Предотвращение выхода НЛО за границы формы
  if (x+ImageUFO.Width+vx  >= Form1.ClientWidth)  or (x < 0) then vx := vx*-1;
  if (y+ImageUFO.Height+vy >= Form1.ClientHeight) or (y < 0) then vy := vx*-1;
  // Перемещение НЛО
  imageUFO.Left := x;
  imageUFO.Top  := y;
end;
Листинг 5

Код во многом похож на тот, что мы писали в уроке № 17. Однако есть некоторые отличия:

  • объединены условия предотвращения выхода НЛО за границы формы. Теперь их не четыре, а два. Дело в том, что необходимо выполнять один и тот же оператор при выходе НЛО за правую и левую границу формы и за верхнюю и нижнюю;
  • перемещению подвергается компонент ImageUFO на фоне компонента ImageFon.

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

7. Точно в цель!

Если игрок попал в НЛО (т. е. щёлкнул мышью по компоненту ImageUFO, на котором нарисован НЛО), то происходит его взрыв. Создайте обработчик события OnClick для компонента ImageUFO и напишите в нём следующий код:

procedure TForm1.ImageUFOClick(Sender: TObject);
begin

  p := 0;
  TimerDestroy.Enabled := true;

end;
Листинг 6

Поясним код:

• обнуляем счётчик этапов уничтожения НЛО. Для чего он нужен, рассказано в следующем пункте;

• запускаем таймер TimerDestroy, который будет создавать эффект взрыва НЛО.

8. Взрыв НЛО

Различные стадии взрыва реализуются как последовательное отображение картинок (спрайтов). Отметим, что чем больше стадий, тем более «реалистичный» получится взрыв. Задержку между этими картинками реализует таймер TimeDestroy. Всего картинок взрыва четыре (рисунок 3).

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

procedure TForm1.TimerDestroyTimer(Sender: TObject);
begin

  // Увеличение счётчика этапов уничтожения НЛО
  inc( p );
  // Этапы уничтожения НЛО
  case p of
    1: ImageUFO.Picture.Bitmap := UFO2;
    2: ImageUFO.Picture.Bitmap := UFO3;
    3: ImageUFO.Picture.Bitmap := UFO4;
    4: ImageUFO.Picture.Bitmap := UFO5;
    5: begin
         // Отключение таймера уничтожения НЛО
         timerDestroy.Enabled := false;
         // НЛО начального вида
         imageUFO.Picture.Bitmap := UFO1;
         // Установка случайных координат НЛО
         x := Random(form1.ClientWidth-1)  - ImageUFO.Width;
         y := Random(form1.ClientHeight-1) - ImageUFO.Height;
       end;

  end;

end;
Листинг 7

Поясним код:

  • увеличиваем счётчик этапов уничтожения НЛО при каждом «тике» таймера;
  • в теле оператора Case, в зависимости от значения переменной p, компоненту ImageUFO присваиваем картинки с различными стадиями взрыва;
  • на последней стадии взрыва НЛО отключаем таймер TimerDestroy, возвращаем НЛО первоначальный вид, помещаем его в игровое поле со случайными координатами и… игра продолжается.

2. ВСЁ! Игра написана! Запустите приложение и ловите инопланетян на мушку!

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

Перед разработкой игры необходимо написать её концепцию (идею). Продумать поведение всех объектов игры и реализацию их событий.

Главный элемент анимации объектов игры — это компонент Timer.

Рабочим пространством игры, использующим графику, обычно являются компоненты FormImage либо PaintBox.

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

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

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


2. Модифицируйте созданное приложение так, чтобы в заголовке формы отображались расстояние до НЛО в пикселях и поясняющий текст: «Огонь!» (когда курсор находится непосредственно над НЛО), «Вы близко к НЛО» (когда курсор находится на расстоянии не менее 50 пикселей от НЛО) и «Вы далеко от НЛО» (когда курсор находится на расстоянии более 50 пикселей от НЛО).


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


4. Модифицируйте созданное приложение так, чтобы снизу, на фоне игрового пространства, рисовался рельеф гор. Результат задания показан на рисунке:


5. Модифицируйте созданное приложение так, чтобы НЛО при полёте изменял свою скорость на 45% в большую и меньшую сторону от исходной. Изменение скорости на 1% должно происходить каждую секунду.


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


7. Модифицируйте созданное приложение так, чтобы НЛО при полёте пропорционально изменял свои размеры на 40 пикселей в большую и меньшую сторону от исходного размера. Изменение размеров должно происходить плавно от максимального до минимального и наоборот. Как будто НЛО «дышит».

Подсказка:

  • используйте свойство Stretch компонента ImageUFO;
  • введите дополнительный таймер для задания скорости изменения размеров НЛО.

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

1. Модифицируйте приложение, созданное в уроке № 17 — Спрайтовая анимация, так, чтобы в НЛО можно было стрелять. Т. е., когда по НЛО игрок щёлкает мышью, он взрывается. Эффект взрыва необходимо нарисовать самим, например, в приложении Photoshop. Количество стадий взрыва должно быть не менее четырёх.