Лабораторная работа №24. Игра




Цель работы - создать программу - игру. Игрок управляет пушкой зенитки, его боевое задание - справиться с нашествием воздушных шаров. Воздушные шары несут бомбы, которые они сбросят, как только окажутся над пушкой. Необходимо не допустить этого и уничтожить их все на подлете. Снаряды не ограничены, но следующий выстрел можно делать только после того, как выпущенный снаряд поразит цель, упадет на землю или уйдет из зоны видимости.

 

В игре участвуют:

• воздушные шары,

• знитная пушка,

• пушечный снаряд,

• бомба,

• внешняя среда.

Для шаров введен специальный тип TBalloon., в котором содержатся данные о координатах шара, его скорости, состоянии и цвете. Массив переменных Balloons типа TBalloon будет содержать полную информацию обо всех шарах. Индексирование идет, начиная с нуля. Общее число шаров в массиве задается соответствующей константой.

Кроме того, к шарам относятся константы, определяющие:

• количество шаров,

• их возможные цвета,

• возможную высоту над землей (всего предполагается четыре уровня),

• интервал между шарами и их радиус.

TBalloon = record x, y, v, Explosion: integer;

Color: TColor;

end;

const

BallCount = 10;

BallColors: array [0.. 9] of TColor = (clRed, clGreen, clNavy, clMaroon, clPurple, clOlive, clLime, clYellow, clFuchsia, clSilver);

BallAltitude: array [0.. 3] of integer = (240, 160, 200, 120);

BallInterval = 40;

BallRadius = 15;

V = 150;

g = -9.8 * 3;

dt = 0.1;

var

Form1: TForm1;

x, y, Vx, Vy, BombY, BombV: double;

Angle, GunPosition, GunExplosion: integer;

Balloons: array [0.. BallCount - 1] of TBalloon;

Зенитная пушка описывается переменными:

• угол наклона пушки,

• ее положение

• ее состояние.

Пушечный снаряд описывается переменными:

• координатами,

• текущей скоростью,

• начальной скоростью при выстреле. Бомба, сбрасываемая с воздушного шара, имеет:

• координату,

• скорость.

Для расчета положений объектов нужно знать ускорение падения и шаг по времени.

В качестве состояния шара и пушки мы используем переменные TBalloon. В определенный момент времени каждый элемент должен находиться в каком-то состоянии, определяющем то, как элемент выглядит на экране. Шары и пушка могут быть боеспособными, взрывающимися и уничтоженными.

Если Explosion = 0- шар боеспособен, если Explosion = 10, то он уничтожен, а значения от 1 до 9 обозначают фазы взрыва. Чтобы инициировать взрыв боеспособного шара, нужно присвоить Explosion:= 1. То же самое касается и пушки.

Состояние, в котором скорость снаряда равна нулю: Vx = 0 и Vy = 0. В этом случае считают, что можно сделать новый выстрел. Пока же снаряд летит (то есть скорость его не нулевая), новый выстрел сделать нельзя.

Бомба будет сбрасываться шаром, когда он поравняется с пушкой. Таким образом, X -координата бомбы всегда равна X - координате пушки. Следовательно, необходимо хранить только Y-координату бомбы. Бомба не сброшена, если BombY = 1000. Если BombY < 1000, то она считается сброшенной и начинает падать.

Поместите на форму TrackBar, Button, Image и Timer и настройте их параметры.

Timer: Interval = 100; Image: Width = 440, Heigth = 260; Button: Caption = 'Новая игра'; TrackBar: Min = -90, Max = 90, Frequency = 10.

Image - сражение в реальном времени. Timer - реальное время. Button - запуск новой игры. TrackBar - управление зенитной пушкой.

В обработчике нажатия на кнопку "Новая игра" необходимо обнулить параметры, расставить пушку и шары. Для каждого шара устанавливается случайное направление движения: v = 1 или -1. В зависимости от направления, определяется высота, на которой находится шар (на одном и том же уровне шары летят в одну сторону). По x-координате шары расставляются так, чтобы они шли с примерно равным интервалом в направлении пушки из-за границ экрана.

Каждый шар Balloons[i], элемент массива Balloons, имеет тип записи TBalloon, и для того, чтобы получить доступ к его элементам, нужно писать: Balloons[i].Explosion:= 0, Color:= BallColors[random(10)] и т.д. Конструкция with Balloons[i] do begin.. end позволяет избежать повторения.

procedure TForm1.Button1Click(Sender: TObject); var i: integer;

begin

Caption:= 'Нашествие'; TrackBar1.Position:= 0;

GunPosition:= 170 + random(100);

Vx:= 0;

Vy:= 0;

GunExplosion:= 0;

for i:= 0 to BallCount - 1 do with Balloons[i] do begin

if random < 0.5 then v:= -1 else v:= 1;

y:= BallAltitude[1 + v + random(2)] + random(10); x:= 220 - v * (220 + i * Balllnterval + random(20));

Explosion:= 0;

Color:= BallColors[random(10)];

end;

BombY:= 1000;

end;

 

В обработчик события формы OnCreate вводится датчик случайных чисел, устанавливается клиентский размер формы (ClientWidth:= 455; ClientHeight:= 315;). Размер клиентской области формы в приложениях определяется автоматически как Width и Height формы минус ширина заголовка и бордюров.

 

procedure TForm1.FormCreate(Sender: TObject);

begin

Randomize;

ClientWidth:= 455;

ClientHeight:= 315;

Button1.Click;

end;

 

Пушка танка будет управляться с помощью компонента TrackBar. Выстрел производится нажатием пробела на клавиатуре. При этом фокус ввода должен быть у TrackBar. В обработчике OnKeyPress реализуется выстрел, задавая начальную скорость и положение снаряду, а также издавая звук выстрела.

Процедура, проигрывающая этот звук из wav-файла, описана в модуле, который необходимо подключить в разделе uses. В модуле MMSystem имеется функция PlaySound, с помощью которой можно проигрывать файлы wav. Для асинхронного воспроизведения (приложение не приостанавливает работу на время воспроизведения, а проигрывает его в фоновом режиме) вызов выглядит так:

 

 

PlaySound(pChar('имяфайла.wav'), 0, SNDASYNC).

В стандартных звуках Microsoft Office можно подобрать звуки для выстрела и взрыва. В зависимости от версии программы, эти файлы хранятся в C:\Windows\Media\ Microsoft Office 2000, C:\Program Files\Microsoft Office\Office\Media или в какой-то подобной папке. Найдите звуки для выстрела (условно назовем GunShot.wav) и взрыва (условно назовем Explode.wav) и скопируйте их в папку, в которой сохранен проект.

procedure TForm1.TrackBar1KeyPress(Sender: TObject;

var Key: Char); var

i: integer;

begin

if (Key = ' ') and (Vx = 0) and (Vy = 0) and (GunExplosion = 0) then begin try

PlaySound(PChar('GunShot.wav'), 0, SND_ASYNC);

except end;

Vx:= V * Sin(Angle * pi / 180);

Vy:= V * Cos(Angle * pi / 180);

x:= GunPosition + 15 * Sin(Angle * pi / 180);

y:= 20 + 15 * Cos(Angle * pi / 180);

end;

if (Key = 'Q') then begin

try

PlaySound(PChar('Explode.wav'), 0, SND_ASYNC);

except end;

for i:= 0 to BallCount - 1 do if Balloons[i].Explosion = 0 then Balloons[i].Explosion:= 1;

Caption:= 'Уничтожен';

end;

end;

В обработчике изменения положения бегунка TrackBar меняем наклон пушки.

procedure TForm1.TrackBar1Change(Sender: TObject);

begin

Angle:= TrackBar1.Position;

end;

Выстрел происходит, если:

1) нажат пробел;

2) снаряд готов (Vx = 0) и (Vy = 0);

3) пушка боеспособна (GunExploion = 0).

Процедура MoveAll, вызываемая каждый такт таймера, отвечает за то, чтобы все процессы шли своим чередом. Вставьте ее сразу после implementation.

procedure MoveAll;

var i: integer;

begin

for i:= 0 to BallCount - 1 do with Balloons[i] do if Explosion = 0 then x:= x + v else begin

if Explosion < 10 then

inc(Explosion);

end;

if (Vx <> 0) or (Vy <> 0) then begin

x:= x + Vx * dt;

y:= y + Vy * dt;

Vy:= Vy + g * dt;

end;

if BombY < 1000 then

begin

BombY:= BombY + BombV * dt;

BombV:= BombV + g * dt end;

if (GunExplosion > 0) and (GunExplosion < 10) then inc(GunExplosion);

end;

Процедура пододвигает все шары, которые живы (у них Explosion = 0), в направлении вектора их скорости. Остальные шары, если они находятся в фазе взрыва (0 < Explosion < 10), переходят в следующую фазу.

Если снаряд выпущен (Vx <> 0) или (Vy <> 0), то его координата и скорость изменяются согласно законам природы.

Если бомба сброшена (BombY < 1000), то она также подчиняется закону всемирного тяготения.

Если танк находится в фазе взрыва (0 < GunExplosion < 10), он переходит в следующую фазу.

Ошибки, возникающие в обработчике таймера или процедурах, которые вызываются из обработчика, не останавливают таймер и, значит, через секунду возникают опять. Чтобы их остановить, приходится использовать Ctrl-Alt-Del...

В этой процедуре мы работаем с массивом. Одной из самых частых ошибок, которые не так-то просто найти, является выход за границы массива. Для предотвращения подобных ситуаций включите проверку на выход из диапазона допустимых значений (Project => Options => Compiler => Runtime errors => Range checking), которая по умолчанию отключена.

Перемещения сами по себе могли бы продолжаться бесконечно. Необходимо проконтролировать:

• не попал ли снаряд в шар, следовательно, взорвать его;

• не уничтожены ли все шары, следовательно, выдать надпись о победе;

• не поравнялся ли шар с пушкой, следовательно, сбросить бомбу;

• не упал ли снаряд и не вышел ли он из зоны контроля, следовательно, перезарядить пушку;

• не упала ли бомба на пушку, следовательно, закончить игру. Для контроля за передвижениями предусмотрена процедура:

procedure CheckCollisions;

var

i, j: integer;

HappyEnd: boolean;

begin

for i:= 0 to BallCount - 1 do with Balloons[i] do begin

if (Explosion = 0) then begin

if (sqr(x - Unit1.x) + sqr(y - Unit1.y) <

sqr(BallRadius)) then begin try

PlaySound(PChar('Explode.wav'), 0, SND_ASYNC);

except end;

Explosion:= 1;

Unit1.x:= 0;

Unit1.y:= 0;

Unit1.Vx:= 0;

Unit1.Vy:= 0;

HappyEnd:= (GunExplosion = 0);

for j:= 0 to BallCount - 1 do

HappyEnd:= HappyEnd and (Balloons[j].Explosion > 0);

if HappyEnd then Form1.Caption:= 'Победа!';

end;

if (x = GunPosition) and (BombY = 1000) and (GunExplosion = 0) then begin

BombY:= y - BallRadius - 5;

BombV:= 0;

end;

end;

end;

if (y < 0) or (x < 0) or (x > 440) then begin

x:= 0;

y:= 0;

Vx:= 0;

Vy:= 0;

end;

if BombY < 10 then

begin

BombY:= 1000;

GunExplosion:= 1;

try

PlaySound(PChar('Explode.wav'), 0, SND_ASYNC);

except end;

Form1.Caption:= 'Увы...';

end;

end;

В этом блоке возможная ошибка связана с использованием with... do begin... end.

X-координата снаряда хранится в глобальной переменной x, X-координата воздушного шара - в переменной Balloons[i].x. Однако когда пишут код внутри with Balloons[i] do begin... end, то именно к координате шара обращаются просто как к x. Если нужно добраться до глобальной переменной, то теперь уже перед ней нужно ставить уточнение: Unit1.x, где Unit1 - имя модуля.

Рисование отдельных элементов баталии также разумно поместить в специализированные для этого процедуры.

Художественные образы шаров, пушки и взрыва передаются с помощью процедур:

procedure DrawBalloon(x, y: integer;

Color: TColor);

begin

with Form1.Image1.Canvas do begin

Pen.Color:= clBlack;

Brush.Color:= Color;

Ellipse(x - BallRadius, y - BallRadius, x + BallRadius, y + BallRadius);

Pen.Color:= clWhite;

Brush.Color:= clWhite;

Ellipse(x - BallRadius div 2 - 3, y - BallRadius div 2 - 3, x - BallRadius div 2 + 3, y - BallRadius div 2 + 3);

Pen.Color:= clBlack;

Brush.Color:= clOlive;

Rectangle(x - 5, y + BallRadius + 5, x + 5, y + BallRadius + 10);

MoveTo(x - 5, y + BallRadius + 5);

LineTo(x - BallRadius, y);

MoveTo(x, y + BallRadius + 5);

LineTo(x, y);

MoveTo(x + 5, y + BallRadius + 5);

LineTo(x + BallRadius, y);

end;

end;

procedure DrawGun;

begin

with Form1.Image1.Canvas do begin

if (Vx = 0) and (Vy = 0) then

Pen.Color:= RGB(0, 70, 0) else Pen.Color:= clBlack;

Brush.Color:= Pen.Color;

Pen.Width:= 5;

MoveTo(GunPosition, 240);

LineTo(GunPosition + round(15 * sin(Angle * pi / 180)), 240 - round(15 * cos(Angle * pi / 180)));

Pen.Width:= 1;

Ellipse(GunPosition - 8, 232, GunPosition + 8, 248);

Rectangle(GunPosition - 10, 240, GunPosition + 10, 260);

end;

end;

Процедура DrawExplosion обеспечивает доступ к координатам снаряда.

procedure DrawExplosion(x, y, Phase: integer); var

i, xx, yy, Size: integer;

a, b: double;

begin

with Form1.Image1.Canvas do for i:= 0 to Phase * 10 do begin

a:= random * 2 * pi;

b:= random * sqr(Phase) / 3 + 5;

xx:= x + round(l * sin(a));

yy:= y + round(l * cos(a));

Size:= round(sqr(10 - Phase) / 8 + 2);

Pen.Color:= RGB(random(100), 0, 0);

Brush.Color:= Pen.Color;

Ellipse(xx - random(Size) - 1, yy - random(Size) - 1, xx + random(Size), yy + random(Size));

end;

end;

Кроме того, пушка меняет цвет, когда готов очередной снаряд. В довершение, нужно соединить все написанное воедино.

Процедуры рисования объединяются в процедуре, рисующей все поле боя:

• небо,

• шары,

• пушка,

• ядро,

• бомба.

 

Объединять все вместе обработчик таймера.

procedure DrawBattleField;

var

i: integer;

begin

with Forml.Imagel.Canvas do

for i:= 0 to 259 do

begin

Pen.Color:= RGB(i div 2, i div 2, 255);

MoveTo(0, i);

LineTo(440, i);

end;

for i:= 0 to BallCount - 1 do with Balloons[i] do

if Explosion = 0 then DrawBalloon(x, 260 - y, Color) else if Explosion < 10 then DrawExplosion(x, 260 - y, Explosion);

if GunExplosion = 0 then DrawGun else if GunExplosion < 10 then DrawExplosion(GunPosition, 240, GunExplosion);

with Forml.Imagel.Canvas do

begin

Pen.Color:= clMaroon;

Brush.Color:= clRed;

if (Vx <> 0) or (Vy <> 0) then Ellipse(round(x) - 2, 260 - round(y) - 2, round(x) + 3, 260 -round(y) + 3);

if (BombY <> 1000) then Ellipse(GunPosition - 2, 260 - round(BombY) - 2, GunPosition + 3, 260 - round(BombY) + 3);

end;

end;

procedure TForm1.Timer1Timer(Sender: TObject);

begin

MoveAll;

CheckCollisions;

DrawBattleField;

end;

Рис. 24.1

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-12-05 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: