Изучаем DelphiX (Часть 2)

== Изучаем DelphiX. Часть 2: Усложняем нашу игру. ==
 "Хех! загружу:"
Прежде чем начать, объясню решение Д/З:
=== 1. Сделайте так, чтобы при уходе патрона из зоны видимости он уничтожался: ===
Это очень легко сделать достаточно в процедуру DoMove патрона вставить:
if y <= 0 then Dead;
if y >= 600 then Dead;
Нужно это для того, чтобы повысилось качество игры.
=== 2. Сделайте, чтобы патроны стреляли очередями, а не кучами как у меня: ===
Вот эта задача была посложнее первой. Во-первых, как я сделал в первой части этого туториала, делать нельзя из-за причины небольшой "кривости" DelphiX, он может держать только определённое количество спрайтов, а если их больше он или выходит или подвисает. Так что, делаем вот так:
В класс с плеером добавляем две переменные, он теперь выглядит так (я добавил комментарии к тем строчкам которые изменились):
 TPlayerSprite = class(TImageSprite)
 private
 LngPolet: Integer; // расстояние которое пролетела пуля
 OldLngPolet: Integer; // расстояние которое пролетела предыдущая пуля
 protected
 procedure DoMove(MoveCount: Integer); override;
 end;
Процедура DoMove класса PlayerSprite изменяется следующим образом:
procedure TPlayerSprite.DoMove(MoveCount: Integer);
begin
 inherited DoMove(MoveCount);
 if isLeft in Form1.DXInput1.States then x := x-5;
 if isRight in Form1.DXInput1.States then x := x+5;
 if isUp in Form1.DXInput1.States then
 begin
 // Если расстояние между пулями меньше 250, то пуля не создаётся
 if (LngPolet - OldLngPolet) >= 250 then
 begin
 Inc(LngPolet);
 with TPlayerFa.Create(Engine) do
 begin
 PixelCheck := True;
 Image := Form1.dxImageList1.Items.Find('Pula');
 X := Self.X + Self.Width - 40;
 Y := Self.Y + Self.Height - 80;
 Width := Image.Width;
 Height := Image.Height;
 end;
 OldLngPolet := LngPolet; // после создания пули - последняя становится первой
 end;
 end;
 if y >= Form1.DXDraw1.SurfaceHeight - Image.Height then
 y := Form1.DXDraw1.SurfaceHeight - Image.Height;
 if x >= Form1.DXDraw1.SurfaceWidth - Image.Width then
 x := Form1.DXDraw1.SurfaceWidth - Image.Width;
 if y <= 0 then
 y := 1;
 if x <= 0 then
 x := 1;
 LngPolet := LngPolet + MoveCount; // расстояние увеличиваем, когда пуля движется
end;
Вот и всё, что требовалось сделать.
В этой части вы узнаете:
# Создание анимации. 
# Создание примитивного AI. 
# Создадим простенькие взрывы.
=== Создание анимации. ===
Анимация в DelphiX строится следующим образом:
Это пример покадровой анимации. У каждого кадра должен быть одинаковый размер.
В данной ситуации: 
* Ширина: 40 
* Высота: 38
Давайте теперь, применим ее к нашей игре. 
Идём в DXImageList, находим наш спрайт Pula и загружаем картинку с анимацией. Далее устанавливаем свойства спрайта следующим образом:
и в процедуре DoMove класса PlayerSprite изменяем код следующим образом:
procedure TPlayerSprite.DoMove(MoveCount: Integer);
begin
 inherited DoMove(MoveCount);
 if isLeft in Form1.DXInput1.States then x := x-5;
 if isRight in Form1.DXInput1.States then x := x+5;
 if isUp in Form1.DXInput1.States then
 begin
 if LngPolet - OldLngPolet >= 250 then
 begin
 Inc(LngPolet);
 with TPlayerFa.Create(Engine) do
 begin
 PixelCheck := True;
 Image := Form1.dxImageList1.Items.Find('Pula');
 X := Self.X + Self.Width - 40;
 Y := Self.Y + Self.Height - 80;
 Width := Image.Width;
 Height := Image.Height;
 AnimCount := Image.PatternCount; // число кадров
 AnimLooped := True; // повторять ли анимацию
 AnimSpeed := 10 / 1000; // скорость анимации
 end;
 OldLngPolet := LngPolet;
 end;
 end;
 if y >= Form1.DXDraw1.SurfaceHeight - Image.Height then
 y := Form1.DXDraw1.SurfaceHeight - Image.Height;
 if x >= Form1.DXDraw1.SurfaceWidth - Image.Width then
 x := Form1.DXDraw1.SurfaceWidth - Image.Width;
 if y <= 0 then
 y := 1;
 if x <= 0 then
 x := 1;
 LngPolet := LngPolet + MoveCount;
end;
Можно проделать тоже самое с другими классами для придания реалистичности игры.
=== Cоздание примитивного AI. ===
Для начала маленько улучшим наш класс плеера, добавим в него столкновение, и он теперь выглядит следующим образом:
TPlayerSprite = class(TImageSprite)
 private
 LngPolet: Integer;
 OldLngPolet: Integer;
 protected
 procedure DoMove(MoveCount: Integer); override;
 // добавляем
 procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
 end;
Далее создаём процедуру DoCollision и вставляем в неё следующее:
procedure TPlayerSprite.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
 if Sprite is TPlayerFa then Dead;
 Collision;
end;
Дальше пред implementation добавляем 1 переменную:
 plx: double; // X плеера
Далее обновим класс TPlayerFa и он станет таким:
 TPlayerFa = class(TImageSprite)
 private
 Stril: Integer; // Как вы заметили, добавилась переменная
 protected
 procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
 procedure DoMove(MoveCount: Integer); override;
 public
 constructor Create(AParent: TSprite); override;
 destructor Destroy; override;
 end;
А в TPlayerFa добавляем строчку:
procedure TPlayerFa.DoMove(MoveCount: Integer);
begin
 y := y - Stril; // когда стреляет игрок, с этой скоростью будет двигаться пуля
 y := y + Stril; // когда стреляет AI, с этой скоростью будет двигаться пуля
 if y <= 0 then Dead;
 if y >= 600 then Dead;
end;
А процедура TBoSS.DoMove становиться такой:
procedure TBoSS.DoMove(MoveCount: Integer);
begin
 inherited DoMove(MoveCount);
 if x >= 600 then I := False;
 if x <= 0 then I := True;
 if i = True then X := X+5;
 if i = False then X := X-5;
 if plx = x then // если равна х плеера стреляем
 begin
 with TPlayerFa.Create(Engine) do
 begin
 PixelCheck := True;
 Image := Form1.dxImageList1.Items.Find('Pula');
 X := Self.X + Self.Width - 70;
 Y := Self.Y + Self.Height + 10;
 Width := Image.Width;
 Height := Image.Height;
 Stril := 5; // скорость патрона
 end;
 end;
 Collision;
end;
И естественно полнеет и TPlayerSprite:
procedure TPlayerSprite.DoMove(MoveCount: Integer);
begin
 inherited DoMove(MoveCount);
 plx := x; // чтобы наш комп видел нашего плеера
 if isLeft in Form1.DXInput1.States then
 begin
 x := x-5;
 plx := x;
 end;
 if isRight in Form1.DXInput1.States then
 begin
 x := x+5;
 plx := x;
 end;
 if isUp in Form1.DXInput1.States then
 begin
 if LngPolet - OldLngPolet >= 250 then
 begin
 Inc(LngPolet);
 with TPlayerFa.Create(Engine) do
 begin
 PixelCheck := True;
 Image := Form1.dxImageList1.Items.Find('Pula');
 X := Self.X + Self.Width - 40;
 Y := Self.Y + Self.Height - 80;
 Width := Image.Width;
 Height := Image.Height;
 Stril := -4;
 end;
 OldLngPolet := LngPolet;
 end;
 end;
 if y >= Form1.DXDraw1.SurfaceHeight - Image.Height then
 y := Form1.DXDraw1.SurfaceHeight - Image.Height;
 if x >= Form1.DXDraw1.SurfaceWidth - Image.Width then
 x := Form1.DXDraw1.SurfaceWidth - Image.Width;
 if y <= 0 then
 y := 1;
 if x <= 0 then
 x := 1;
 LngPolet := LngPolet + MoveCount;
 Collision;
end;
И поменялся конструктор PlayerFa:
constructor TPlayerFa.Create(AParent: TSprite);
begin
 inherited Create(AParent);
 Image := Form1.DXImageList1.Items.Find('Pula');
 Width := Image.Width;
 Height := Image.Height;
 AnimCount := Image.PatternCount; // число кадров
 AnimLooped := True; // повторять ли анимацию
 AnimSpeed:= 10 / 1000;
 Collision;
end;
=== Создадим простенькие взрывы. ===
Для начала загрузим спрайт взрыва в DXImageList вот он:
Назовём его "ex" и установим размер кадра на 32Х48 (PatternHeight - PatternWidth)
Дальше, в класс TPlayerSprite в private добавляем procedure Hit; и создаём обработчик:
procedure TplayerSprite.Hit;
begin
 Collisioned := False;
 Image := Form1.dxImageList1.Items.Find('Ex');
 Width := Image.Width;
 Height := Image.Height;
 AnimCount := Image.PatternCount;
 AnimLooped := False;
 AnimSpeed := 15/1000;
 AnimPos := 0;
end;
и в процедуре TPlayerSprite.DoCollision вставляем следующий код:
procedure TPlayerSprite.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
 if Sprite is TPlayerFa then
 begin
 TPlayerSprite(Sprite).Hit;
 Dead;
 end;
 Done := False;
end;
Этот код надо разместить и в Tboss. DoCollision.
И под конец маленький бонус: 
Можно создать бэкграунд, без создания нового класса достаточно в FormCreate написать:
 with TBackgroundSprite.Create(DXSpriteEngine1.Engine) do
 begin
 SetMapSize(1, 1);
 Image := dxImageList1.Items.Find('bgr');
 Z := -2;
 Tile := True;
 end;
Где bgr спрайт-бэкграунд. 
Вот мой БГ:
Нравиться? Здесь можно скачать исподники всей этой части. part2.rar (77,5 kB)
Author: Влад Энгельгардт
ID: 03930
Category:Графика
Category:DirectX
Category:Учебники