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

=== Немного о джойстиках ===
 Наталия Елманова
 Компьютер Пресс - 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): 
 
Интерфейс приложения готов. Теперь можно приступить к созданию кода. 
Для начала следует не забыть сослаться в приложении сослаться на файл 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). 
 
Перевод на Delphi - Сергей Бодров.
Source: Взято из http://delphi.chertenok.ru
ID: 01623
Category:Статьи