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

Материал из DRKB


Изучаем DelphiX. Часть 6: Мультиплеер.[править | править код]

"SINGLE-хорошо, а 
MULTIPLAYER - лучше"

Здравствуйте дорогие мои читатели. Сегодня мы будем говорить о мультиплеере. Точнее об его создании в DelphiX. В DelphiX это реализуется с помощью компонента:

Dx 20806aa.jpg DXPlay.

Многие, кто пытался что-то сделать на DXPlay, сталкивался с такой проблемой: что если вызывать диалог с DXPlay то получаем непонятный текст:

Dx 20806a.jpg


Устранить ее очень легко. Дело в том, что из-за "НЕРУССКОГО" происхождения программист Hiroyuki Hori в DXPlayFm.dfm в некоторых местах поставил шрифт: "Terminal", а нам нужен "MS Sans Serif". Поэтому то и всё так исковеркано. Я исправил эту проблему вот так:

Dx 20806b.jpg


И выкладываю исправленный мною модуль DXPlayFm.dfm, и более того, он русифицирован. Так что пользуйтесь на здоровье :) DXPlayFm.rar.

Сегодня мы напишем простенькую, мультиплеерную гаму. В этом я помогу вам.

В качестве шаблона я буду использовать part2.rar. Мы его модернизируем, и предадим ему мультиплеерность. Сразу говорю, что part2.rar состоял из 2 интерактивных объектов, соответственно и в наш мультиплеер можно играть только вдвоем.

Теперь я расскажу, как нужно модернизировать пример из второй части под шестой :).

На мой взгляд, эта часть является единственно растолковывающая создание мультиплеера под DelphiX. Сколько я прочитал статей по программированию мультиплеера на DelphiX, ничего путного не увидел. Здесь я использую простой алгоритм. Принцип его следующий: передаются не координаты объекта, а клавиши, которые были нажаты на удалённой машине.

Кидаем на форму компонент DXPlay и устанавливаем вот такие свойства:

MaxPlayers = 2,
GUID = {218A0D84-949C-42BC-A97B-40C3E3A642F1}.


GUID можно создать любой достаточно нажать на кнопку New.

Добавляем в uses модуль: DirectX.

После uses создаём список констант:

const
  DXACTION_MESSAGE = 1; // Это нажата клавиша действия
  DXKEY_LEFT = 1;       // двигаемся влево
  DXKEY_RIGHT = 2;      // двигаемся вправо
  DXKEY_SHOOT = 3;      // Стреляем


Создаём новый тип для наших ACTION сообщений:

type
  TDXActionMessage = record
    dwType: DWORD;
    ActionCode: Integer;
  end;


Далее идут классы спрайтов:

Единственное, что поменялось это класс TBOSS, я его переименовал в TPLU.

Когда мы поменяли названия то и картинку нужно изменить на более подходящую.

Заходим в DXImageList и переименовываем спрайт Boss на Plu .

А вот картинка:

Dx 20806c.jpg


В Var перед implementation обозначаем два наших класса:

var
  Form1: TForm1;
  plu: TPlu;
  PlayerSprite: TPlayerSprite;

implementation


Теперь подходим к процедуре TPlayerSprite.DoMove.

Она должна выглядеть вот так:

procedure TPlayerSprite.DoMove(MoveCount: Integer);
var
  Msg: ^TDXActionMessage; // Само сообщение
  MsgSize: Integer; // размер сообщения
begin
  inherited DoMove(MoveCount);
  MsgSize := SizeOf(TDXActionMessage);
  // пока в игру не зайдёт второй игрок ни кто, не двинется
  if Form1.DXPlay1.Players.Count = 2 then 
  begin
    if isLeft in Form1.DXInput1.States then
    begin
      x := x-5;
      GetMem(Msg, MsgSize);
      Msg.dwType := DXACTION_MESSAGE; // тип сообщения
      Msg.ActionCode := DXKEY_LEFT; // действие
      Form1.DXPlay1.SendMessage(DPID_ALLPLAYERS, Msg, MsgSize); // отсылаем всем
    end;
    if isRight in Form1.DXInput1.States then
    begin
      x := x+5;
      GetMem(Msg, MsgSize);
      Msg.dwType := DXACTION_MESSAGE;
      Msg.ActionCode := DXKEY_RIGHT;
      Form1.DXPlay1.SendMessage(DPID_ALLPLAYERS, Msg, MsgSize);
    end;
    if isUp in Form1.DXInput1.States then
    begin
      if LngPolet - OldLngPolet >= 900 then
      begin
        Inc(LngPolet);
        with TPlayerFa.Create(Engine) do
        begin
          GetMem(Msg, MsgSize);
          Msg.dwType := DXACTION_MESSAGE;
          Msg.ActionCode := DXKEY_SHOOT;
          Form1.DXPlay1.SendMessage(DPID_ALLPLAYERS, Msg, MsgSize);
          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;
end;


Теперь, как вы понимаете, так как TBoss уже не существует, то и некоторые процедуры должны быть пусты:

procedure TPLU.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  Collision;
end;

constructor TPLU.Create(AParent: TSprite);
begin
  inherited Create(AParent);
end;


Далее очищаем событие для формы OnCreate и пишем в нём:

procedure TForm1.FormCreate(Sender: TObject);
begin
  try
    DXPlay1.Open;
  except
    on E: Exception do
    begin
      Application.ShowMainForm := False;
      Application.HandleException(E);
      Application.Terminate;
    end;
  end;

  with TBackgroundSprite.Create(DXSpriteEngine1.Engine) do
  begin
    SetMapSize(1, 1);
    Image := DXImageList1.Items.Find('bg');
    Z := -2;
    Tile := True;
  end;
end;


Далее кликаем по компоненту DXPlay создаём событие: OnMessage

И пишем следующее:

procedure TForm1.DXPlay1Message(Sender: TObject; From: TDXPlayPlayer;
  Data: Pointer; DataSize: Integer);
begin
  if (TDXActionMessage(Data^).ActionCode and DXKEY_LEFT) = DXKEY_LEFT then
    plu.X := plu.X - 5; // получаем сообщение влево, двигаем удалённого
                        // playerа в лево
  if (TDXActionMessage(Data^).ActionCode and DXKEY_RIGHT) = DXKEY_RIGHT then
    plu.X := plu.X + 5; // получаем сообщение вправо, двигаем удалённого
                        // playerа в право
  if (TDXActionMessage(Data^).ActionCode and DXKEY_SHOOT) = DXKEY_SHOOT then
  begin   
    // получаем сообщение - удалённый игрок выстрелил,
    // создаём патрон на месте PLU
    with TPlayerFa.Create(DXSpriteEngine1.Engine) do
    begin
      PixelCheck := True;
      Image := Form1.dxImageList1.Items.Find('Pula');
      X := plu.X + plu.Width - 70;
      Y := plu.Y + plu.Height + 10;
      Width := Image.Width;
      Height := Image.Height;
      Stril := 4;
      Collision;
    end;
  end;
end;


Далее кликаем по DXDraw создаём событие Initialize и в нем пишем:

  plu := TPlu.Create(DXSpriteEngine1.Engine);
  plu.PixelCheck  := True;
  plu.Image := Form1.dxImageList1.Items.Find('plu');
  plu.x := 350;
  plu.y := 10;
  plu.Width := plu.Image.Width;
  plu.Height := plu.Image.Height;

  PlayerSprite := TPlayerSprite.Create(DXSpriteEngine1.Engine);
  PlayerSprite.PixelCheck := True;
  PlayerSprite.Image := Form1.dxImageList1.Items.Find('Pla');
  PlayerSprite.x := 350;
  PlayerSprite.y := 500;
  PlayerSprite.Width := PlayerSprite.Image.Width;
  PlayerSprite.Height := PlayerSprite.Image.Height;


Вот, и всё запускаем, и радуемся.

А теперь я расскажу, как это всё работает.

Прежде чем начать писать мультиплеер, нужно осознавать, что тебе придётся думать за каждую из удалённых машин. Как она получит пакет, кому что нужно будет отправить и так далее. Так вот, в событии Message в DXPlay1 обрабатываются всё принятые сообщения, а отсылать их можно любым плеером из любого места в коде. Так вот когда один из игроков запускает игру, а другой присоединяется к нему, происходит такая картина:

Dx 20806d.jpg


В нашем примере все действия, которые происходят на другой машине, отражается на спрайте Plu.

Скачать всё, что мы творили можно вот здесь: part6.rar.

P.S Назвал я класс PLU потому что, так проще, расшифровывается как плеер удаленный.

P.S.S Я устал уже отвечать на такие письма типа: "У меня не распаковывается архив?" так вот, говорю, всё архивы запакованы WinRAR 3.0.


Author: Влад Энгельгардт
Source: https://gamedev.ru
ID: 03934