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

Previous  Top  Next

    
 

Изучаем DelphiX. Часть 3: Крутим спрайты.

Автор: Влад Энгельгардт

"Крутимся не накрутимся:)"

В DelphiX существует маленькая проблема, это разворот спрайтов. Она решается относительно просто, сейчас я объясню, как и что.

Для начала создадим новый проект в Delphi, подстроим его под шаблон (об этом написано в одной из частей цикла). Далее скачиваем мною модернизированный DXSprite.pas (Dxsprite.rar), в папке, где у вас установлен DelphiX есть папочка Source, копируем его туда. В DXSprite.pas я добавил всего одну процедуру Angle это и есть процедура разворота, как она работает можете посмотреть, сами поковырявшись в исходниках. Ну что приступим, для начала создадим простенький пример (в архиве с исходниками в папке 3.1).

Создаём новый класс:

Code:

TPlayerone = class(TImageSprite)

protected

   procedure DoMove(MoveCount: Integer); override;

end;

 

Соответственно и процедуру DoMove к этому классу:

 

Code:

Procedure TPlayerone.DoMove(MoveCount: Integer);

begin

inherited DoMove(MoveCount);

x:=x+cos256(Angle)*speed; //обработчик движения по X

y:=y+sin256(Angle)*speed; //обработчик движения по Y

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;

begin

   speed:=0;     //когда ничего не делаем, скорость равна 0

   if isLeft in Form1.DXInput1.States then angle:=angle-5;

   if isRight in Form1.DXInput1.States then angle:=angle+5;

   if isup in Form1.DXInput1.States then speed:=4;

   if isDown in Form1.DXInput1.States then speed:=-4;

end;

end;

 

И перед implementation в Var добавляем:

 

Code:

var

Form1: TForm1;

speed: integer;  // Это у нас переменная скорости объекта

 

Не забудь в DXtimer добавить:

Code:

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;

DXDraw1.Flip;

end;

 

И в процедуре FormCreate создаём наш спрайт:

Code:

procedure TForm1.FormCreate(Sender: TObject);

begin

with TPlayerone.Create(Dxspriteengine1.Engine) do

begin

   PixelCheck := True;

   Image := form1.dxImageList1.Items.Find('krut');

   x:=350;

   y:=250;

   Width := Image.Width;

   Height := Image.Height;

end;

end;

 

Далее в DXImageList добавляем спрайт "krut" он у меня выглядит вот так:

clip0112

И всё, простейший пример с Angle готов.

Теперь давайте усложним наш пример и сделаем так что бы игрок стрелял в ту сторону куда он смотрит, для этого создаём новый класс для патрона. Выглядит он так:

Code:

TPlayerFa = class(TImageSprite)

protected

   procedure DoMove(MoveCount: Integer); override;

private

   anglefa:integer; // Угол под которым летит пуля

public

   constructor Create(AParent: TSprite); override;

   destructor Destroy; override;

end;

 

Перед implementation добавляется ещё одна переменная ang:

Code:

var

Form1: TForm1;

speed,ang: integer;

 

Она нужна для выноса значения Angle плеера. А зачем нужен вынос, спросишь ты, да всё очень просто, он нужен для патрона, чтобы указывать ему, под каким углом лететь т.е. под каким углом находиться плеер под таким углом и летит патрон.

Маленько модернизируем класс плеера:

Code:

TPlayerone = class(TImageSprite)

private

   lngpolet:integer;  // мы же не хотим, чтобы наши пули летали кучами

   oldlngpolet:integer; // а мы сделаем чтобы летали стаями :)

protected

   procedure DoMove(MoveCount: Integer); override;

end;

 

Сразу, чтобы не забыть конструктор и деструктор для патрона:

Code:

constructor TPlayerFa.Create(AParent: TSprite);

begin

inherited Create(AParent);

Image := form1.DXImageList1.Items.Find('pul');

Width := Image.Width;

Height := Image.Height;

end;

 

destructor TPlayerFa.Destroy;

begin

inherited Destroy;

end;

 

Ну а теперь движение патрона:

Code:

procedure TPlayerFa.DoMove(MoveCount: Integer);

begin

inherited DoMove(MoveCount);

angle := anglefa;

x:=x+cos256(angle)*7; // цифра 7 здесь скорость патрона и изменять её надо

y:=y+sin256(angle)*7; // пропорционально

if X>= 800 then Dead;

if y>= 600 then Dead;

if X<= 0 then Dead;

if y<= 0 then Dead;

Collision;

end;

 

Надеюсь, ты помнишь, на прошлых уроках я рассказывал тебе об DXInput, так вот теперь нам понадобятся дополнительные кнопочки. Их значения ты можешь поменять, два раза кликнув на самом компоненте. Так вот, гляди процедуру DoMove у игрока:

Code:

Procedure TPlayerone.DoMove(MoveCount: Integer);

begin

inherited DoMove(MoveCount);

ang:=angle; // наша переменная для патрона

x:=x+cos256(Angle)*speed;

y:=y+sin256(Angle)*speed;

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;

begin

   speed:=0;

   if isLeft in Form1.DXInput1.States then angle:=angle-5;

   if isRight in Form1.DXInput1.States then angle:=angle+5;

   if isup in Form1.DXInput1.States then speed:=4;

   if isDown in Form1.DXInput1.States then speed:=-4;

   if isbutton1 in Form1.DXInput1.States then

   begin

     if lngpolet-oldlngpolet>=250 then

     begin

       Inc(lngpolet);

       with TPlayerFa.Create(Engine) do

       begin

         Image := form1.DXImageList1.Items.Find('pul');

         X:=self.X+cos256(ang)*50// здесь 50 расстояние патрона от плеера

         Y:=self.y+sin256(ang)*50;   //они должны быть пропорциональны

         anglefa:=ang; // передаём угол

         oldlngpolet := lngpolet;

       end;

     end;

   end;

   lngpolet := lngpolet + MoveCount;

end;

end;  

 

И последнее что осталось это создать спрайт "pul" в DXimageList. Он выглядит у меня вот так:

clip0113

Что, устал? Ну, расслабься, если хочешь. Дальше мы сделаем следующее:
1. Добавим второго плеера
2. И научимся делать стрэйфы

Для начала установим новую раскладку в Dxinput. Для этого два раза кликнем по кампоненту Dxinput и преходим в закладку Keybord:

clip0114

Сразу маленький совет: чтобы на одной кнопке у тебя не было 300 действий для каждой метки, назначай только одну кнопку.

Я выбрал такой вариант для первого игрока:
Up- Num 8
Down - Num 5
Left - Num 4
Right - Num 6
Button1 - Num 0
Button2 - Num 7
Button3 - Num 9
Для второго игрока:
Button4 - T
Button5 -G
Button6 -F
Button7- H
Button8 -E
Button9 -R
Button10 -Y

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

Code:

var

Form1: TForm1;

speed,speed2,ang,ang2: integer;

implementation

Теперь соответственно добавляем 2 игрока и модернизируем первого:

TPlayerone = class(TImageSprite)

private

   lngpolet:integer;

   oldlngpolet:integer;

protected

   procedure DoMove(MoveCount: Integer); override;

   //Добавили столкновение

   procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;

end;

 

TPlayertwo = class(TImageSprite)

private

   lngpolet:integer;

   oldlngpolet:integer;

protected

   procedure DoMove(MoveCount: Integer); override;

   procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;

end;

 

и теперь обрабатываем DoCollision у обоих плееров:

Code:

procedure TPlayerone.DoCollision(Sprite: TSprite; var Done: Boolean);

begin

if Sprite is Tplayerfa then dead;

end;

 

procedure TPlayertwo.DoCollision(Sprite: TSprite; var Done: Boolean);

begin

if Sprite is Tplayerfa then dead;

end;

 

Процедура DoMove у первого игрока в корне изменяется:

Code:

Procedure tPlayerone.DoMove(MoveCount: Integer);

begin

inherited DoMove(MoveCount);

ang:=angle;

x:=x+cos256(Angle)*speed;

y:=y+sin256(Angle)*speed;

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;

begin

   speed:=0;

   if isLeft in Form1.DXInput1.States then angle:=angle-5;

   if isRight in Form1.DXInput1.States then angle:=angle+5;

   if isup in Form1.DXInput1.States then speed:=4;

   if isDown in Form1.DXInput1.States then speed:=-4;

   if isbutton2 in Form1.DXInput1.States then

   begin

     x:= x+cos256 (angle+64)*3; // это у нас стрейф

     y:= y+sin256 (angle+64)*3; // 3 - на сколько быстро стрейфиться

   end;

   if isbutton3 in Form1.DXInput1.States then

   begin

     x:= x+cos256 (angle-64)*3; //тоже стрейф, только в

     y:= y+sin256 (angle-64)*3; //другую сторону

   end;

   if isbutton1 in Form1.DXInput1.States then

   begin

     if lngpolet-oldlngpolet>=250 then

     begin

       Inc(lngpolet);

       with TPlayerFa.Create(Engine) do

       begin

         Image := form1.DXImageList1.Items.Find('pul');

         X:=self.X+cos256(ang)*50;

         Y:=self.y+sin256(ang)*50;

         anglefa:=ang;

         oldlngpolet := lngpolet;

       end;

     end;

   end;

   lngpolet := lngpolet + MoveCount;

end;

Collision;

end;

 

Аналогично и у второго плеера:

Code:

Procedure TPlayertwo.DoMove(MoveCount: Integer);

begin

inherited DoMove(MoveCount);

ang2:=angle;

x:=x+cos256(Angle)*speed2;

y:=y+sin256(Angle)*speed2;

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;

begin

   speed2:=0;

   if isbutton6 in Form1.DXInput1.States then angle:=angle-5;

   if isbutton7 in Form1.DXInput1.States then angle:=angle+5;

   if isbutton4 in Form1.DXInput1.States then speed2:=4;

   if isbutton5 in Form1.DXInput1.States then speed2:=-4;

   if isbutton9 in Form1.DXInput1.States then

   begin

     x:= x+cos256 (angle-64)*3;

     y:= y+sin256 (angle-64)*3;

   end;

   if isbutton10 in Form1.DXInput1.States then

   begin

     x:= x+cos256 (angle+64)*3;

     y:= y+sin256 (angle+64)*3;

   end;

   if isbutton8 in Form1.DXInput1.States then

   begin

     if lngpolet-oldlngpolet>=250 then

     begin

       Inc(lngpolet);

       with TPlayerFa.Create(Engine) do

       begin

         Image := form1.DXImageList1.Items.Find('pul');

         X:=self.X+cos256(ang2)*50;

         Y:=self.y+sin256(ang2)*50;

         anglefa:=ang2;

         oldlngpolet := lngpolet;

       end;

     end;

   end;

   lngpolet := lngpolet + MoveCount;

end;

Collision;

end;

 

И последнее в FormCreate добавляем, чтобы второй игрок креатился:

Code:

procedure TForm1.FormCreate(Sender: TObject);

begin

with TPlayerone.Create(Dxspriteengine1.Engine) do

begin

   PixelCheck := True;

   Image := form1.dxImageList1.Items.Find('krut');

   x:=350;

   y:=250;

   Width := Image.Width;

   Height := Image.Height;

end;

 

with TPlayertwo.Create(Dxspriteengine1.Engine) do

begin

   PixelCheck := True;

   Image := form1.dxImageList1.Items.Find('krut');

   x:=50;

   y:=250;

   Width := Image.Width;

   Height := Image.Height;

end;

 

И пару советов, для дебага можно выводить любую переменную на экран. Для этого в DXTimer добавим следующие строчки:

Code:

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(0, 0, 'FPS: '+inttostr(DXTimer1.FrameRate)); //вывод текста

   Textout(0, 24, 'спрайты: '+inttostr(DXSpriteEngine1.Engine.AllCount));

   Release;

end;

DXDraw1.Flip;

end;

 

Здесь я вывожу Fps (кадры в секунду), и количество спрайтов на экране.

Д/З:
1. Сделай анимированные патроны.
2. Реализуй, чтобы вторым игроком управлял не человек, а созданный тобой интеллект.
3. Сделай так, чтобы вёлся счёт фрагов.
4. После смерти любого игрока, чтобы через 5 сек. происходил респаун.

Вот архив всего, что мы сегодня натворили: part3.rar(18kB).

Если у вас возникли какие-то вопросы или проблемы, пишите мне.

Автор: Влад Энгельгардт

©Drkb::03931