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

== Изучаем DelphiX. Часть 4: Разбираем классы. ==
 "Классы - это хорошо :)"
В этой части, дорогие мои читатели, мы разберем классы. Здесь будет рассказано, как создать грамотный, читаемый, и не тормозной код (на сколько это возможно).
Для начала реализуем наше Д/З:
:1. Сделай анимированные патроны. 
Для начала я заменил в DXImageList спрайт "pul" другой картинкой вот такой:
а размер кадра 26X40.
В конструктор патрона нужно вставить анимацию:
constructor TPlayerFa.Create(AParent: TSprite);
begin
 inherited Create(AParent);
 Image := Form1.DXImageList1.Items.Find('pul');
 Width := Image.Width;
 Height := Image.Height;
 AnimCount := Image.PatternCount; // вот эти
 AnimLooped := True; // три строчки
 AnimSpeed := 10 / 1000; // с которыми вы уже знакомы
end;
Вот и всё с этим вопросом.
:2. Реализуй, чтобы вторым игроком управлял не человек, а созданный тобой интеллект. 
Я не хочу потом повторяться, об этом ты прочитаешь в 5 части, и она будет посвящена "AI".
: 3. Сделай так, чтобы вёлся счёт фрагов. 
Добавляем перед implementation в Var две переменные: 
var
 FragPl, FragPl2: Integer;
И для отображения наших фрагов вставляем следующие строчки и он изменяется следующим образом:
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
 if not DXDraw1.CanDraw then Exit;
 DXInput1.Update;
 DXSpriteEngine1.Move(LagCount);
 DXSpriteEngine1.Dead;
 DXDraw1.Surface.Fill(0);
 DXSpriteEngine1.Draw;
 with DXDraw1.Surface.Canvas do
 begin
 Brush.Style := bsClear;
 Font.Color := clWhite;
 Font.Size := 12;
 // это первого плеера фраги
 TextOut(30, 35, 'Фраги первого игрока: ' + IntToStr(FragPl));
 // это второго плеера
 TextOut(240, 35, 'Фраги второго игрока: ' + IntToStr(FragPl2));
 TextOut(0, 0, 'FPS: ' + IntToStr(DXTimer1.FrameRate));
 TextOut(0, 24, 'Спрайтов: ' + IntToStr(DXSpriteEngine1.Engine.AllCount));
 Release;
 end;
 DXDraw1.Flip;
end;
И ещё маленький штрих, без которого некуда:
procedure TPlayerOne.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
 if Sprite is TPlayerFa then Dead;
 FragPl2 := FragPl2 + 1;
end;
procedure TPlayerTwo.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
 if Sprite is TPlayerFa then Dead;
 FragPl := FragPl + 1;
end;
:4. После смерти любого игрока, чтобы через 5 сек. происходил респаун. 
Для этого на форму добавляем обыкновенный таймер. Находится он в закладки System, и выглядит также как DXTimer. И устанавливаем свойства следующим образом: 
 Enabled = False 
 Interval = 5000
А в событие OnTimer:
 if PlRes = True then
 begin
 with TPlayerOne.Create(dxSpriteEngine1.Engine) do
 begin
 PixelCheck := True;
 Image := Form1.dxImageList1.Items.Find('krut');
 x := Random(450);
 y := Random(450);
 Width := Image.Width;
 Height := Image.Height;
 end;
 end;
 if PlRes = False then
 begin
 with TPlayerTwo.Create(dxSpriteEngine1.Engine) do
 begin
 PixelCheck := True;
 Image := Form1.dxImageList1.Items.Find('krut');
 x := Random(450);
 y := Random(450);
 Width := Image.Width;
 Height := Image.Height;
 end;
 end;
 Timer1.Enabled := False;
end;
Теперь перед implementation в var добавляем переменную типа Boolean:
var
 PlRes: Boolean; // переменная респавна True = погиб 1 игрок False 2 игрок.
В событие FormCreate добавляем одну единственную команду: 
 Randomize();
И финальный штрих, процедуру DoCollision у каждого игрока изменяем следующим образом:
procedure TPlayerOne.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
 if Sprite is TPlayerFa then
 begin
 Dead;
 FragPl2 := FragPl2 + 1;
 PlRes := True;
 Form1.Timer1.Enabled := True;
 end;
end;
procedure TPlayertwo.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
 if Sprite is TPlayerFa then
 begin
 Dead;
 FragPl := FragPl + 1;
 PlRes := False;
 Form1.Timer1.Enabled := True;
 end;
end;
Вот и всё, что нужно было сделать дома :).
Ну что, приступим, для начала я расскажу о разных способах создания спрайтов: 
:1. Это наш способ, мы использовали его в этом туториале. При создании формы, создавался объект, т.е. это выглядит так:
procedure TFormMy.FormCreate(Sender: TObject);
begin
 with TPlayer.Create(dxSpriteEngine1.Engine) do
 begin
 PixelCheck := True;
 Image := dxImageList.Items.Find(skinspl1);
 x := 10;
 y := 12;
 Width := Image.Width;
 Height := Image.Height;
 end;
end;
Здесь, при создании спрайта, мы определяем его свойства. Значит, обрабатывать конструктор TPlayer нам ненужно.
Следующий способ, это способ конструктора. В конструкторе мы заранее задаём свойства будущего спрайта, но Create мы обрабатываем в FormCreate.Вот пример:
constructor TPlayerFa.Create(AParent: TSprite);
begin
 inherited Create(AParent);
 Image := FormMy.DXImageList.Items.Find(pus2);
 Width := Image.Width;
 Height := Image.Height;
 AnimCount := Image.PatternCount;
 AnimLooped := True;
 AnimSpeed := SpeedAnimPul / 1000;
end;
Обычно этот способ используют для патронов.
И последний способ - это создания типов. Например, мы создали класс:
 TPlayer = class(TImageSpriteEx)
 private
 Speed: Single;
 public
 procedure DoMove(MoveCount: Integer); override;
end;
Теперь перед implementation в var обозначаем наш класс как тип:
var
 Form1: TForm1;
 Player: TPlayer;
implementation
Теперь в свойствах DxDraw находим событие DXDrawInitialize и вставляем следующее:
 Player := TPlayer.Create(DXSpriteEngine1.Engine);
 Player.Image := DXImageList.Items.Find('Player');
 Player.X := 250;
 Player.Y := 250;
 Player.Width := Player.Image.Width;
 Player.Height := Player.Image.Height;
Удобство использования этого способа в том что, в любой момент можно получить данные о спрайте, например, в одном из уроков, нам приходилось выносить значение angle для патрона. С использованием этого способа, все значения плеера, будут у нас под рукой.
И ещё очень важный момент, создавайте свои функции и процедуры, это очень удобно.
Теперь насчёт Collision: 
:Во-первых, если объект с чем-то взаимодействует, то не забываете в DoMove вставлять: 
 Collision;
:Во-вторых, там где нужен точный Collision используйте PixelСheck := True; там где точность при столкновении не нужна PixelCheck := False;, или вообще ничего не писать.
Angle: это процедура очень удобная, но есть одно большое НО, она не подходит для разворота очень больших объектов. Происходит очень большое количество вычислений, и игра начинает зверски тормозить или точнее уменьшается число Fps.
Анимация в DelphiX капризная вещь, иногда не дружит с PixelCheck, иногда с Collision, но это бывает очень редко. В анимации, как и в статичном спрайте, главное правильно указывать Transparent Color, хотя DelphiX пытается определить его сам (и у него иногда это хорошо получается), следует смотреть какой цвет он выбрал.
Размер кадра вычитывается ручками (если ты рисовал сам), если ты создавал анимацию в каком-то редакторе, то ты должен знать размер кадра т.к. ты его сам указывал.
С DXInput бывает всего одна проблема это смешивание кнопок, т.е. на одной кнопке находиться несколько действий.
Ну, вот думаю и всё что я могу тебе посоветовать по этому поводу. 
Спросишь ты, а причём тут "Разбираем классы", некоторое написанное выше заставляет наши спрайты меньше тормозить, а разбираем мы их потому, что решаем что хорошо, а что плохо.
Есть вопросы, пиши мне.
Скачать файл с ответами на Д/З можно здесь: part4.rar.
Author: Влад Энгельгардт
Source: https://gamedev.ru
ID: 03932
Category:Графика
Category:DirectX
Category:Учебники