Немного о джойстиках

Материал из DRKB

Немного о джойстиках[править | править код]

Наталия Елманова
Компьютер Пресс - CD, 1999, N 7© Copyright N.Elmanova & ComputerPress Magazine. 

Посвящается всем любителям компьютерных игр..

В предыдущей статье данного цикла ("Компьютер Пресс", 1999, N 3) были рассмотрены некоторые из вопросов, связанных с использованием внешних устройств. Данная статья продолжает начатую тему. Так как данный номер посвящен приложениям multimedia, а примеры использования компонента TMediaPlayer и создания проигрывателя звуковых компакт-дисков доступны российским читателям в нескольких различных модификациях (см., например, недавно вышедшую книгу С.Тейксейра и К.Пачеко "Borland Delphi 4: руководство разработчика"; подобные примеры есть и в других источниках), данная статья посвящена теме довольно экзотической - использованию джойстика. Некоторые (но далеко не все) идеи, послужившие основой для рассмотренного ниже примера, заимствованы из малодоступного российскому читателю источника "C++Builder - how-to" (J.Miano, T.Cabanski, H.Howe. - Waite Group Press, 1997).

Использование джойстика базируется на использовании нескольких функций Windows API и структур Windows, описанных в файле mmsystem.h.

Сервисы, обслуживающие джойстики, загружаются при загрузке операционной системы. В 32-разрядных версиях Windows джойстиков может быть два, и каждый может иметь от одной до четырех кнопок. Каков именно ваш джойстик и каковы характеристики его драйвера, можно определить с помощью соответствующих функций Windows API. Помимо этого, с помощью других функций можно определить, какие из кнопок нажаты в данный момент, и каковы текущие координаты, связанные с наклоном его ручки (joystick's positional information). Это можно сделать, либо опрашивая джойстик периодически, либо обрабатывая полученные от него события.

Создадим простейшее приложение для тестирования джойстика. С этой целью создадим форму, на которую поместим компонент TImage с изображением джойстика, и четыре компонента TShape (в данном случае это красные эллипсы) для изображения нажатых кнопок. Сделаем компоненты TShape невидимыми. Кроме того, поместим на форму компоненты TMemo и TPaintBox. И, наконец, поместим на форму компонент TImageList, в который поместим одно-единственное изображение, более или менее похожее на прицел джойстика (рис.1):

Рис.1. Главная форма приложения для тестирования джойстика


Интерфейс приложения готов. Теперь можно приступить к созданию кода.

Для начала следует не забыть сослаться в приложении сослаться на файл mmsystem.h (в нем описаны необходимые структуры и функции для управления джойстиком и получения информации от него). Первое, что нужно сделать - это узнать, установлен ли вообще драйвер джойстика, и если да, подключен ли джойстик. Для этой цели используется функция joyGetNumDevs(). Если она возвращает значение, отличное от нуля, это означает, что драйвер джойстика установлен. Однако это еще не означает, что сам джойстик подключен:


var joycount := Integer;
joycount := joyGetNumDevs();
if (joycount = 0) then
  Memo1.Lines.Add('Драйверы джойстика не установлены');


Чтобы узнать, подключен ли джойстик, следует обратиться к функции joyGetPosEx и убедиться, что она не возвращает кода ошибки.


  jr := joyGetPosEx(JOYSTICKID1, JoyInfo);
  if (jr = JOYERR_NOERROR) then
  begin
    connect := True;
    jnum := JOYSTICKID1;
  end
  else if (jr = MMSYSERR_INVALPARAM) then
    ShowMessage('Ошибка определения наличия джойстика')
  else if(joyGetPosEx(JOYSTICKID2, JoyInfo) = JOYERR_NOERROR) then
  begin
    connect := True;
    jnum := JOYSTICKID2;
  end;


Если джойстик подключен, можно узнать о его характеристиках с помощью структуры JOYCAPS. Список доступных характеристик весьма обширен, и мы выведем в компонент TMemo информацию о некоторые из них. Сведения об остальных можно получить аналогичным способом.

Memo1.Lines.Add('Номер устройства -  ' + IntToStr(jnum));
Memo1.Lines.Add('Наименование - ' + AnsiString(JoyCaps.szPname));
Memo1.Lines.Add('Число кнопок - ' + IntToStr(JoyCaps.wNumButtons));


Если джойстик подключен, можно обрабатывать его события. Функция joySetCapture предназначена для перенаправления сообщений джойстика данному окну (в нашем случае - главному окну нашего приложения). Параметры этой функции содержат дескриптор окна, указатель на то, первый или второй джойстик должен посылать сообщения, частоту отправки этих сообщений (в данном случае используется минимально возможный период, определяемый с помощью все той же структуры JOYCAPS), а также параметр, указывающий на то, нужно ли посылать сообщения, если в состоянии джойстика ничего не изменилось.

if (connect) then joySetCapture(Handle, jnum, 2 * JoyCaps.wPeriodMin, False);

Соответственно для того, чтобы остановить поток этих сообщений, перед закрытием формы следует вызвать функцию joyReleaseCapture.


if (connect) then joyReleaseCapture(jnum);


Теперь можно создать функции, реагирующие на события джойстика. Одна из них должна делать видимыми те из объектов TShape, которые соответствуют нажатым кнопкам. Вторая должна отображать "прицел", перемещая его внутри компонента TPaintBox. Помимо этого, следует сослаться на эти функции в h-файле, указав, что они являются реакцией на соответствующие сообщения Windows. Текст h-файла, таким образом, имеет вид:

unit joytest;

interface

uses
  Windows, SysUtils, Forms, Controls, StdCtrls, ExtCtrls, mmsystem;

type
  { TForm1 }
  TForm1 = class(TForm)
    // IDE-managed Components
    Image1: TImage;
    Shape2: TShape;
    Shape1: TShape;
    Shape3: TShape;
    Shape4: TShape;
    Memo1: TMemo;
    ImageList1: TImageList;
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    jnum: Integer;
    JoyCaps: TJOYCAPS;
    connect: Boolean;
    stepX, stepY: Integer;
    Pos: TPoint;

    procedure UpdButton(var Msg: TMessage);
    procedure UpdCoord(var Msg: TMessage);

    procedure MMJoy1ButtonDown(var Msg: TMessage); message MM_JOY1BUTTONDOWN;
    procedure MMJoy1ButtonUp(var Msg: TMessage); message MM_JOY1BUTTONUP;
    procedure MMJoy1Move(var Msg: TMessage); message MM_JOY1MOVE;

    procedure MMJoy2ButtonDown(var Msg: TMessage); message MM_JOY2BUTTONDOWN;
    procedure MMJoy2ButtonUp(var Msg: TMessage); message MM_JOY2BUTTONUP;
    procedure MMJoy2Move(var Msg: TMessage); message MM_JOY2MOVE;
  public
  end;

var
  Form1: TForm1;

implementation


{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  jr: MMRESULT;
  JoyInfo: TJOYINFOEX;
  JoyCount: Integer;
begin
  JoyCount := joyGetNumDevs();
  if (JoyCount = 0) then
    Memo1.Lines.Add('Драйверы джойстика не установлены')
  else
  begin
    connect := False;
    jr := joyGetPosEx(JOYSTICKID1, JoyInfo);
    if (jr = JOYERR_NOERROR) then
    begin
      connect := True;
      jnum := JOYSTICKID1;
    end
    else if (jr = MMSYSERR_INVALPARAM) then
      ShowMessage('Ошибка определения наличия джойстика')
    else
    begin
      jr := joyGetPosEx(JOYSTICKID2, JoyInfo);
      if (jr = JOYERR_NOERROR) then
      begin
        connect := True;
        jnum := JOYSTICKID2;
      end;
    end;
  end;

  joyGetDevCaps(jnum, JoyCaps, SizeOf(JOYCAPS));
  if (connect) then
    Memo1.Lines.Add('Джойстик подключен');
  else
    Memo1.Lines.Add('Джойстик не подключен');
  Memo1.Lines.Add('Номер устройства -  ' + IntToStr(jnum));
  Memo1.Lines.Add('Наименование - ' + AnsiString(JoyCaps.szPname));
  Memo1.Lines.Add('Число кнопок - ' + IntToStr(JoyCaps.wNumButtons));
  if (connect) then
    joySetCapture(Handle, jnum, 2*JoyCaps.wPeriodMin, False);
  stepX := (JoyCaps.wXmax - JoyCaps.wXmin) div PaintBox1.Width;
  stepY := (JoyCaps.wYmax - JoyCaps.wYmin) div PaintBox1.Height;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 if (connect) then joyReleaseCapture(jnum);
end;

procedure TForm1.UpdButton(var Msg: TMessage);
begin
  Shape1.Visible := ((Msg.WParam and JOY_BUTTON1) <> 0);
  Shape2.Visible := ((Msg.WParam and JOY_BUTTON2) <> 0);
  Shape3.Visible := ((Msg.WParam and JOY_BUTTON3) <> 0);
  Shape4.Visible := ((Msg.WParam and JOY_BUTTON4) <> 0);
end;

procedure TForm1.UpdCoord(var Msg: TMessage);
var
  JoyInfo: TJOYINFO;
  pCanvas: TCanvas;
  x, y: Integer;
begin
  pCanvas := PaintBox1.Canvas;
  pCanvas.Brush.Color := Form1.Color;
  pCanvas.FillRect(Rect(0, 0, PaintBox1.Width, PaintBox1.Height));
  Pos.x := msg.LParamLo;
  Pos.y := msg.LParamHi;
  x := (Pos.x - JoyCaps.wXmin) div stepX - (ImageList1.Width div 2);
  y := (Pos.y - JoyCaps.wYmin) div stepY - (ImageList1.Height div 2);
  ImageList1.Draw(pCanvas, x, y, 0, True);
end;

procedure TForm1.MMJoy1ButtonDown(var Msg: TMessage);
begin
  UpdButton(Msg);
end;

procedure TForm1.MMJoy1ButtonUp(var Msg: TMessage);
begin
  UpdButton(Msg);
end;

procedure TForm1.MMJoy1Move(var Msg: TMessage);
begin
  UpdCoord(Msg);
end;

procedure TForm1.MMJoy2ButtonDown(var Msg: TMessage);
begin
  UpdButton(Msg);
end;

procedure TForm1.MMJoy2ButtonUp(var Msg: TMessage);
begin
  UpdButton(Msg);
end;

procedure TForm1.MMJoy2Move(var Msg: TMessage);
begin
 UpdCoord(Msg);
end;

end.


Запустив приложение при корректно установленном драйвере и подключенном джойстике, можно попробовать нажимать на кнопки и перемещать рычаг. При этом изображение прицела будет перемещаться внутри компонента TPaintBox, а при нажатии на кнопки на экране будут появляться красные эллипсы в соответствующих местах (рис.2).

Рис.2. Приложение для тестирования джойстика на этапе выполнения


Перевод на Delphi - Сергей Бодров.


Source: Взято из http://delphi.chertenok.ru
ID: 01623