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

Изучаем DelphiX (Часть 3) Изучаем DelphiX. Часть 3: Крутим спрайты. Автор: Влад Энгельгардт "Крутимся не накрутимся:)" В DelphiX существует маленькая проблема, это разворот спрайтов. Она решается относительно просто, сейчас я объясню, как и что. Для начала создадим новый проект в Delphi, подстроим его под шаблон (об этом написано в одной из частей цикла). Далее скачиваем мною модернизированный DXSprite.pas (Dxsprite.rar), в папке, где у вас установлен DelphiX есть папочка Source, копируем его туда. В DXSprite.pas я добавил всего одну процедуру Angle это и есть процедура разворота, как она работает можете посмотреть, сами поковырявшись в исходниках. Ну что приступим, для начала создадим простенький пример (в архиве с исходниками в папке 3.1). Создаём новый класс:

TPlayerone = class(TImageSprite)
 protected
  procedure DoMove(MoveCount: Integer); override;
end;
Соответственно и процедуру DoMove к этому классу:
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 добавляем:
var
 Form1: TForm1;
 speed: integer; // Это у нас переменная скорости объекта
Не забудь в DXtimer добавить:
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 создаём наш спрайт:
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 готов. Теперь давайте усложним наш пример и сделаем так что бы игрок стрелял в ту сторону куда он смотрит, для этого создаём новый класс для патрона. Выглядит он так:
TPlayerFa = class(TImageSprite)
 protected
  procedure DoMove(MoveCount: Integer); override;
 private
  anglefa:integer; // Угол под которым летит пуля
 public
  constructor Create(AParent: TSprite); override;
  destructor Destroy; override;
end;
Перед implementation добавляется ещё одна переменная ang:
var
 Form1: TForm1;
 speed,ang: integer;
Она нужна для выноса значения Angle плеера. А зачем нужен вынос, спросишь ты, да всё очень просто, он нужен для патрона, чтобы указывать ему, под каким углом лететь т.е. под каким углом находиться плеер под таким углом и летит патрон. Маленько модернизируем класс плеера:
TPlayerone = class(TImageSprite)
 private
  lngpolet:integer; // мы же не хотим, чтобы наши пули летали кучами
  oldlngpolet:integer; // а мы сделаем чтобы летали стаями :)
 protected
  procedure DoMove(MoveCount: Integer); override;
end;
Сразу, чтобы не забыть конструктор и деструктор для патрона:
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;
Ну а теперь движение патрона:
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 у игрока:
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 добавим ещё две переменные, только уже для второго игрока:
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 у обоих плееров:
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 у первого игрока в корне изменяется:
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;
Аналогично и у второго плеера:
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 добавляем, чтобы второй игрок креатился:
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 добавим следующие строчки:
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). Если у вас возникли какие-то вопросы или проблемы, пишите мне. Автор: Влад Энгельгардт

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...