當前位置:編程學習大全網 - 編程語言 - 在C++Builder中如何使用遊戲操縱桿

在C++Builder中如何使用遊戲操縱桿

在Windows環境下通過編程來操縱鼠標、鍵盤是壹件再簡單不過的事了,不過大家有沒有想過要嘗試壹下另壹樣我們比較常見的輸入工具——遊戲操縱桿呢?在某些情況下,尤其是象編制壹些小型的遊戲軟件的時候,加入對遊戲操縱桿的支持可以給使用者提供更為友好的人機界面,極大的提高遊戲軟件的可玩性。

C++Builder中沒有專門控制操縱桿函數(其實在常見的編程語言中基本上都沒有),因此要增加對遊戲操縱桿的支持,就要和Windows的MCI API函數打交道,這裏我們首先介紹壹些在讀取操縱桿的屬性、狀態,位置和按鈕信息時要用到的API函數、常量及數據結構。

相關常量:

#define MM_JOY1MOVE 0x3A0 /* 用以傳遞操縱桿當前狀態的壹些消息 */

#define MM_JOY2MOVE 0x3A1

#define MM_JOY1ZMOVE 0x3A2

#define MM_JOY2ZMOVE 0x3A3

#define MM_JOY1BUTTONDOWN 0x3B5

#define MM_JOY2BUTTONDOWN 0x3B6

#define MM_JOY1BUTTONUP 0x3B7

#define MM_JOY2BUTTONUP 0x3B8

#define JOY_BUTTON1 0x0001 /* 用以表明當前操縱桿的狀態 */

#define JOY_BUTTON2 0x0002

#define JOY_BUTTON3 0x0004

#define JOY_BUTTON4 0x0008

#define JOY_BUTTON1CHG 0x0100

#define JOY_BUTTON2CHG 0x0200

#define JOY_BUTTON3CHG 0x0400

#define JOY_BUTTON4CHG 0x0800

/* 遊戲操縱桿錯誤返回值 */

#define JOYERR_BASE 160

#define JOYERR_NOERROR (0) /* 正常 */

#define JOYERR_ParmS (JOYERR_BASE+5) /* 參數錯誤 */

#define JOYERR_NOCANDO (JOYERR_BASE+6) /* 無法正常工作 */

#define JOYERR_UNPLUGGED (JOYERR_BASE+7) /* 操縱桿未連接 */

/* 操縱桿標識號 */

#define JOYSTICKID1 0

#define JOYSTICKID2 1

相關函數:

WINMMAPI UINT WINAPI joyGetNumDevs(void);

獲取設備標識號。

MMRESULT WINAPI joyGetDevCaps(UINT uJoyID, LPJOYCAPS pjc, UINT cbjc);

獲取操縱桿屬性信息,以結構體JoyCaps接收。

WINMMAPI MMRESULT WINAPI joyGetPos(UINT uJoyID, LPJOYINFO pji);

獲取操縱桿位置和按鈕狀態,以結構體接收。

WINMMAPI MMRESULT WINAPI joyGetThreshold(UINT uJoyID, LPUINT puThreshold);

讀取操縱桿移動閾值。

WINMMAPI MMRESULT WINAPI joyReleaseCapture(UINT uJoyID);

結束對操縱桿信息的接收。

WINMMAPI MMRESULT WINAPI joySetCapture(HWND hwnd, UINT uJoyID, UINT uPeriod,

BOOL fChanged);

設置接收某壹操縱桿的信息的窗口以及將何種頻度接收。

WINMMAPI MMRESULT WINAPI joySetThreshold(UINT uJoyID, UINT uThreshold);

設置操縱桿移動閾值。

相關結構體: typedef struct joyCaps{

WORD wMid; /* 制造商標識 */

WORD wPid; /* 生產編號 */

char szPname[MAXPNAMELEN]; /* 產品名稱 */

UINT wXmin; /* X軸最小值 */

UINT wXmax; /* X軸最大值 */

UINT wYmin; /* Y軸最小值 */

UINT wYmax; /* Y軸最大值 */

UINT wZmin; /* Z軸最小值 */

UINT wZmax; /* Z軸最大值 */

UINT wNumButtons; /* 按鈕數 */

UINT wPeriodMin; /* 最小調用間隔時間(單位 毫秒)*/

UINT wPeriodMax; /* 最大調用間隔時間(單位 毫秒)*/

}JOYCAPS, *PJOYCAPS, NEAR *NPJOYCAPS, FAR *LPJOYCAPS;

typedef struct joyInfo{

UINT wXpos; /* x 軸位置 */

UINT wYpos; /* y 軸位置 */

UINT wZpos; /* z 軸位置 */

UINT wButtons; /* 按鈕狀態 */

} JOYINFO, *PJOYINFO, NEAR *NPJOYINFO, FAR *LPJOYINFO;

以上這些定義存儲在mmsystem.h文件中,所以程序要包含這個頭文件。

程序需要首先檢查遊戲操縱桿的存在,這包括了檢查驅動程序支持和確認操縱桿已與系統相連的兩項工作。joyGetNumDevs調用檢查系統是否配置了遊戲端口和驅動程序。如果返回值為零,表明不支持操縱桿功能。如果joyGetNumDevs返回值不為零,則說明系統支持遊戲操縱桿功能。但joyGetNumDevs並不能確定操縱桿是否已被連接上了,通過調用可以完成這些工作,並檢查是否有錯誤發生。

如果有遊戲端口,joyGetNumDevs返回值通常為16.

壹旦確認了操縱桿已連上,就可以接受器發來的消息。joySetCapture通知Windows操縱桿消息應發送到哪裏機發送的頻率如何。

joySetCapture中的第壹個參數通知Windows誰將得到消息,第二個參數確定程序將從那個操縱桿接收消息。第三個參數時表示希望以怎樣的頻度接受JM_MOVE消息(單位為毫秒),無論操縱桿是否移動,都將以這個頻度接受JM_MOVE消息。joySetCapture的四個參數允許程序當操縱桿移動壹定的距離後才接受消息。該距離由joySetThreshold設置。

joySetCapture被調用後,窗口將接受操縱桿事件。MM_JOYXMOVE(X=操縱桿號)事件已joySetCapture定義的時間間隔發生。只有當操縱桿的按鈕被按下時,MM_JOYXBUTTONUP和MM_JOYXBUTTONDOWN事件才發生。操縱桿時間出發句柄,改變相應的標簽狀態信息。移動消息也同時通知程序在新的位置重畫操縱桿標誌。調用joyReleaseCapture通知Windows已結束操縱桿的調用。

在實際編制程序時,應首先在Form1的頭文件Form1.h中加入對mmsystem.h的引用,再加入壹些相關的消息映射即對MM_JOYXMOVE、MM_JOYXBUTTONUP和MM_JOYXBUTTONDOWN事件的響應函數說明。

#include mmsystem.h

//--------------------

class Tform1:public TForm

{

__published:

...

...

private:

...

TPoint Position;//用於存儲操縱桿的坐標位置。

...

public:

MESSAGE_HANDLER(MM_JOY1BUTTONDOWN,TMessage,JMButonUpdate)

MESSAGE_HANDLER(MM_JOY1BUTTONUP,TMessage,JMButonUpdate)

MESSAGE_HANDLER(MM_JOY1MOVE,TMessage,JMMove)

END_MESSAGE_MAP(TForm)

};

在Form1的OnCreate事件中加入以下代碼用以檢測操縱桿。

void __fastcall TForm1::FormCreate(TObject *Sender)

{

DriverCount = joyGetNumDevs();

Connected = false;

MMRESULT JoyResult;

JOYINFO JoyInfo;

//檢查系統是否配置了遊戲端口和驅動程序。

if(DriverCount != 0)

{

//仍需調用joyGetPos進行檢測,如果返回JOYERR_NOERROR則表示操縱桿連接正常。

//測試第壹個操縱桿。

JoyResult = joyGetPos(JOYSTICKID1,JoyInfo);

if(JoyResult == JOYERR_NOERROR )

{

Connected = true;

JoystickID = JOYSTICKID1;

}

//如果發生INVALIDPARAM錯誤,則退出。

else if(JoyResult == MMSYSERR_INVALPARAM)

Application-MessageBox("An error occured while calling joyGetPos",

"Error", MB_OK);

// 如果第壹個操縱桿為連接,則檢查第二個操縱桿。

else if((JoyResult=joyGetPos(JOYSTICKID2,JoyInfo)) == JOYERR_NOERROR)

{

Connected = true;

JoystickID = JOYSTICKID2;

}

}

}

在確定操縱桿已正確連接之後就可以讀取操縱桿的設備信息。

void TForm1::ShowDeviceInfo(void)

{

joyGetDevCaps(JoystickID,JoyCaps, sizeof(JOYCAPS));

Label1-Caption = "Number of joysticks supported by driver = " +

IntToStr(DriverCount);

Label2-Caption = "Current Joystick ID = " +

IntToStr(intJoystickID);

Label3-Caption = "Manufacturer ID = " +

IntToStr(JoyCaps.wMid);

Label4-Caption = "Product ID = " +

IntToStr(JoyCaps.wPid);

Label5-Caption = "Number of buttons = "+

IntToStr(JoyCaps.wNumButtons);

.

.

.

// 設置當前窗口接收操縱桿信息。

if(Connected)

joySetCapture(Handle,JoystickID,2*JoyCaps.wPeriodMin,FALSE);

//計算操縱桿活動範圍和屏幕範圍的比率,在後面繪制操縱桿標誌時會用到。

XDivider = (JoyCaps.wXmax - JoyCaps.wXmin)/ Width;

YDivider = (JoyCaps.wYmax - JoyCaps.wYmin)/ Height;

}

讀取操縱桿位置信息和按鈕狀態:

void TForm1::ShowStatusInfo(void)

{

if(Connected)

{

JOYINFO JoyInfo;

TPoint Position;

joyGetPos(JoystickID,JoyInfo);

Position.x = JoyInfo.wXpos;

Position.y = JoyInfo.wYpos;

//顯示操縱桿的X、Y軸位置。

Label6-Caption = "X Position = " + IntToStr(int(JoyInfo.wXpos));

Label7-Caption = "Y Position = " + IntToStr(int(JoyInfo.wYpos));

//判斷某按鈕是否被按下,這裏只是指按鈕初始的狀態。

if(JoyInfo.wButtons

JOY_BUTTON1)

Label8-Caption = "Button 1 = Pressed";

else

Label8-Caption = "Button 1 = Not Pressed";

}

}

下面可以編寫用以響應當初在頭文件中定義的事件JMMove、JMButtonUpdate的代碼: JMButtonUpdate的代碼:

void __fastcall TForm1::JMMove(TMessage msg)

{

/*當操縱桿位置發生變化時會自動調用本函數。

在本函數中經常是根據操縱桿當前的位置來繪制操縱桿在屏幕上顯示的標誌,並擦 去原來的標誌。這裏只是簡單的改變Image的坐標位置來表示操縱桿為的移動。 */

Position.x = msg.LParamLo;

Position.y = msg.LParamHi;

//計算新的坐標。

ScreenX = (Position.x-JoyCaps.wXmin)/XDivider - ImageList1-Width/2;

ScreenY = (Position.y-JoyCaps.wYmin)/YDivider - ImageList1-Height/2;

//顯示新位置的X、Y值。

Label6-Caption = "X Position = " + IntToStr(int(Position.x));

Label7-Caption = "Y Position = " + IntToStr(int(Position.y));

//移動Image的位置。

Image1-Top=ScreenY;

Image1-Left=ScreenX;

}

void __fastcall TForm1::JMButtonUpdate(TMessage msg)

{

//當程序接收到JM_BUTTONDOWN和JM_BUTTONUP消息時,即某壹按鈕的狀態發生改變時,都會調用本函數。

if(msg.WParam

JOY_BUTTON1) //判斷按鈕1是否被按下

Label8-Caption = "Button 1 = Pressed";

else

Label8-Caption = "Button 1 = Not Pressed";

}

最後在程序退出的時候要記得關閉對操縱桿的調用,即在FormDestroy事件中加入joyReleaseCapture(JoystickID)。

void __fastcall TForm1::FormDestroy(TObject *Sender)

{

if(Connected)

joyReleaseCapture(JoystickID);

}

最後說明壹下,本程序在 CBuilder4/PWin98 SE 環境下通過,在 WindowsNT 下用到的API函數將會與本程序中介紹的函數有所不同,詳細區別請參閱Windows API函數手冊。另外在程序測試中,我僅使用了最壹般的接在聲卡上的那種普通4鍵模擬手柄。針對其他的操縱桿及新型USB手柄/操縱桿,還希望有條件的朋友自己去測試壹下。

  • 上一篇:求聲光控路燈制作詳列
  • 下一篇:《基督山伯爵》的寫作藝術
  • copyright 2024編程學習大全網