前段時間花38元從網上買了一對北通的USB游戲手柄,這樣周末與晚上的休閒時間就可以玩玩孩兒時的 SFC與街機模擬游戲了。
某日在某個網站上玩一個Flash游戲時,突然想到,如果也能使用手柄來玩Flash游戲,那該多爽 。 但可惜的是,目前的Flash都是不支持對游戲手柄進行編程,這不免是Flash中的一個遺憾。。
雖然Flash中不支持對游戲手柄進行編程,但我們可以換種方法,做一個輔助程序(外掛? ) ,將手柄中的操作事件轉換為Flash中可接受的鍵盤與鼠標操作事件,這樣不就可以使用游戲手柄來玩 Flash游戲了嗎?!於是,上網查了相關資料,但卻發現只有C++方面的案例,而C#一個也找不,這不打緊 ,自己動手,豐衣足食 。
(注:類似這樣的功能,網絡已有現成的軟件,是一個日本人開發的,叫JoyToKey)
對游戲手柄進行操作,大概有兩種方式:采用系統API或者使用DirectInput操作游戲手柄設備。(也 許還有其它方式,但我的知識范圍有限,其它方式就不得而知了)
采用系統API是一種最簡單的方式,因為系統已幫我們封裝好了所有細節,我們只要在程序中定時取得 游戲手柄設備的狀態就可以了(輪循)。
操作游戲手柄(桿)的API有以下幾個:
函數名稱 函數說明 joyGetNumDevs 獲取當前系統支持的游戲設備數量 joyGetDevCaps 查詢獲取指定的游戲桿設備以確定其性能 joySetCapture 向系統申請捕獲某個游戲設備並定時將該設備的狀態值通過消息發送到某 個窗口 joyReleaseCapture 釋放對某個游戲設備的捕獲 joyGetPos 獲取游戲設備的坐標位置和按鈕狀態 joyGetPosEx 獲取游戲設備的坐標位置和按鈕狀態 joyGetThreshold 查詢指定的游戲桿設備的當前移動阈值 joySetThreshold 設置指定的游戲桿設備的移動阈值
其中,根據調用不同的方法又可分為兩種方式。
1)被動方式:
調用joySetCapture方法,向系統申請對某個游戲手柄的捕捉,如果成功申請,系統將會定時將此游戲 手柄的狀態信息通過消息方式通知到我們的某個窗口上。
2)主動方式:
即是根據我們自己的需要,按需調用joyGetPos或joyGetPosEx方法查詢獲取某個游戲手柄的當前狀態 。
而在本篇中,我們要講解的只是“被動方式”。
joySetCapture方法的C#定義原型如下:
/// <summary>
/// 向系統申請捕獲某個游戲桿並定時將該設備的狀態值通過消息發送到某個窗口
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="uJoyID">指定游戲桿(0-15),它可以是JOYSTICKID1或 JOYSTICKID2</param>
/// <param name="uPeriod">每隔給定的輪詢間隔就給應用程序發送有關游戲桿的信 息。這個參數是以毫妙為單位的輪詢頻率。</param>
/// <param name="fChanged">是否允許程序當操縱桿移動一定的距離後才接受消息 </param>
/// <returns></returns>
[DllImport("winmm.dll")]
public static extern int joySetCapture(IntPtr hWnd, int uJoyID, int uPeriod, bool fChanged);
當我們調用此方法向系統申請捕獲某個游戲手柄後,如果成功,則返回JOYERR_NOERROR(值為0),否則 返回其它值的話表示申請失敗。並且在不再需要捕獲游戲手柄時要記得調用joyReleaseCapture方法釋放 捕捉。
如果申請成功,系統將會定時(根據uPeriod的值決定時間的長短)將游戲手柄的狀態以消息包形式發 送到hWnd對應的窗口界面。所以我們必須要在程序中處理對應的消息(如重寫WndProc方法進行處理)。
並且根據不同的uJoyID值,系統發送的消息號又會有所不同,如對於JOYSTICKID1系統將會分別發送以 下消息包:
消息號 說明 MM_JOY1MOVE 當手柄的位置已變動或按了某些按鈕時,將會發送此消息包。 MM_JOY1BUTTONDOWN 當手柄的A,B,C,D四個按鈕中的一個或多個正被按下時,將會發送此消 息包。 MM_JOY1BUTTONUP 當手柄的A,B,C,D四個按鈕中的一個或多個正被彈起時,將會發送此消 息包。
而對於JOYSTICKID2 系統發出的消息包分別為MM_JOY2MOVE、MM_JOY2BUTTONDOWN、MM_JOY2BUTTONUP!
並且要注意!不管你有沒有按游戲手柄上的按鈕,系統也會定時發送MM_JOYXMOVE消息!!
怎樣判斷按了哪些鍵?
在消息包中,游戲手柄的狀態信息(按鈕狀態)分別存儲在消息包中的WParam與LParam參數。
1)WParam參數:
對於游戲手柄來說WParam存儲的是除了上下左右四個方向鍵之外的所有按鈕中當前被按下的按鈕值, 它的值是一個復合值。如它的值為JOY_BUTTON1 | JOY_BUTTON2時,就表明按下的按鍵是1號和2號按鈕。
注意:對於MM_JOYXBUTTONDOWN與MM_JOYXBUTTONUP兩個消息,用於判斷的按鈕值是不同於MM_JOYXMOVE 的按鈕值!!
2)LParam參數:
此參數存儲的是游戲手柄的坐標參數,並且此參數的高16位存儲的是Y坐標值,低16位存儲的是X坐標 值。
而對於游戲手柄來說,判斷上下左右四個方向鍵有沒有被按下就是通過此參數進行判斷的。如果當四 個方向鍵都沒有被按下時,表示當前游戲手柄處於中心坐標中!也就是X,Y坐標都是在中心點位置上,而 當某些方向鍵被按下時,X,Y坐標將根據所按的鍵向對應方向偏移。如當按了向右鍵,則X坐標向右偏移 ,Y坐標保持在中心點位置,而如果按了右、上兩個方向鍵同時按下,則X坐標向右偏移,Y坐標向上偏移 。所以我們可以根據LParam參數取得X,Y坐標的值,然後再根據其中心點來判斷。參考代碼如下:
/// <summary>
/// 獲取X,Y軸的狀態
/// </summary>
/// <param name="lParam"></param>
/// <param name="buttons"></param>
private void GetXYButtonsStateFromLParam(int lParam, ref JoystickButtons buttons)
{
//處理X,Y軸
int x = lParam & 0x0000FFFF; //低16位存儲X軸坐標
int y = (int)((lParam & 0xFFFF0000) >> 16); //高16位存儲 Y軸坐標(不直接移位是為避免0xFFFFFF時的情況)
int m = 0x7EFF; //中心點的值
if (x > m)
{
buttons |= JoystickButtons.Right;
}
else if (x < m)
{
buttons |= JoystickButtons.Left;
}
if (y > m)
{
buttons |= JoystickButtons.Down;
}
else if (y < m)
{
buttons |= JoystickButtons.UP;
}
}
好了,對游戲手柄的“被動方式”編程就講解完成了,剩下的就是要怎麼利用游戲手柄來 實現模擬鍵盤或鼠標的操作了……
PS:如果各位有興趣的可考慮一下怎麼實現游戲手柄的“主動方式”編程開發。
本文配套源碼