用XInput库使用xbox360手柄
用XInput庫使用xbox360手柄
前言
XInput庫是微軟開發的庫,功能是讓xbox360類型的手柄能在Windows PC平臺使用。它被附帶在DXSDK_Jun10開發包中(我寫的框架基于這個版本),鏈接和實際使用都特別簡單。這篇文章應該可以讓你更快的使用上XInput。
環境配置
包含文件和DX11的配置一樣,頭文件都在同一個目錄下,如果之前配置了DX庫,就不需要額外做什么。只需要鏈接上附加依賴項 XInput.lib。
在代碼中也只需要包含頭文件XInput.h。
最后接口
class Input_imp;//輸入的單例類 class DLL_API GamePad { friend class Input_imp; public://按鍵狀態bool KeyUp(int pad);bool KeyDown(int pad);bool KeyState(int pad);//LT、RT的按下程度:[0,1]float ForceLT();float ForceRT();//LS、RS的位置偏移程度:[0,1]Vector2 GetLS();Vector2 GetRS();//LS、RS離原地的距離:[0,1]float ForceLS();float ForceRS();//設置振動,左右馬達的各自強度:[0,1]void SetVibration(Vector2 lr); private:UINT32 _id;//手柄編號 0-3Input_imp* _input; };上面的值均規范化到0到1的范圍。
其中LT、RT需要返回按下的程度,在XInput庫中名字叫Trigger。LS、RS代表左右兩個搖桿,在Xinput中叫Thumb。
其它的按鈕均只有按下和非按下狀態,所以使用KeyUp、KeyDown、KeyState來返回它們的狀態。KeyUp代表上一幀按下,這一幀沒有按下。KeyDown代表上一幀沒按下,但此幀按下。KeyState返回當前是否按下。
單個狀態的按鈕枚舉值如下:
通過名字,讀者就應該知道代表什么含義了。如果要檢測組合鍵應該使用第二種方式:
gamepad->KeyDown(UP | LEFT);//錯誤 gamepad->KeyDown(A) && gamepad->KeyDown(B);//正確具體實現
由于暫時支持最多4個手柄,Input_imp類需要存儲4個GamePad類對象,4個當前按鍵狀態,4個上一幀按鍵狀態。用戶調用GetGamePad(N)時返回第N個手柄,如果手柄不存在,就返回NULL。
XINPUT_STATE _xinputState[MAX_CONTROLLERS];//當前狀態 XINPUT_STATE _xinputStatePre[MAX_CONTROLLERS];//上一幀狀態 GamePad _gamePad[MAX_CONTROLLERS];//手柄接口 bool _gamePadConnected[MAX_CONTROLLERS];//手柄是否連接XINPUT_STATE結構體包含了手柄按鍵的所有狀態,如下
typedef struct _XINPUT_GAMEPAD {WORD wButtons; //單個狀態按鈕們的組合值BYTE bLeftTrigger;//LT [0,255]BYTE bRightTrigger;//RT [0,255]SHORT sThumbLX;//LS 的X軸位移 [-32768,32767]SHORT sThumbLY;//LS 的Y軸位移 [-32768,32767]SHORT sThumbRX;//RS 的X軸位移 [-32768,32767]SHORT sThumbRY;//RS 的Y軸位移 [-32768,32767] } XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;typedef struct _XINPUT_STATE {DWORD dwPacketNumber;XINPUT_GAMEPAD Gamepad; } XINPUT_STATE, *PXINPUT_STATE;我們就在每幀調用下面的函數,獲取到手柄的按鈕狀態:
void Input_imp::_xinput_run() {DWORD dwResult;for (DWORD i = 0; i < MAX_CONTROLLERS; i++){_xinputStatePre[i] = _xinputState[i];//上一幀狀態ZeroMemory(&_xinputState[i], sizeof(XINPUT_STATE));// Simply get the state of the controller from XInput.dwResult = XInputGetState(i, &_xinputState[i]);if (dwResult == ERROR_SUCCESS){//獲取成功// Controller is connected _gamePadConnected[i] = true;}else{//獲取失敗,手柄不存在// Controller is not connected _gamePadConnected[i] = false;}}}然后手柄狀態被存儲到了_xinputState中,接下來只需要GampPad類訪問即可,但其中需要做一些處理。第一是將數值規范化到[0,1],其次是過小的值需要歸0。所有成員函數的實現都列出來了,由于并不復雜,我就不多說了,許多直接copy自文檔,并做了一些改動(文檔的并不能直接使用)。
GamePad* Input_imp::GetGamePad(UINT32 id) {if (_gamePadConnected[id])return &_gamePad[id];elsereturn NULL; }bool GamePad::KeyUp(int pad) {return !(_input->_xinputState[_id].Gamepad.wButtons & pad) && (_input->_xinputStatePre[_id].Gamepad.wButtons & pad); }bool GamePad::KeyDown(int pad) {return (_input->_xinputState[_id].Gamepad.wButtons & pad) &&!(_input->_xinputStatePre[_id].Gamepad.wButtons & pad); }bool GamePad::KeyState(int pad) {return _input->_xinputState[_id].Gamepad.wButtons & pad; }float GamePad::ForceLT() {if (_input->_xinputState[_id].Gamepad.bLeftTrigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)return 0;return _input->_xinputState[_id].Gamepad.bLeftTrigger / 255.0f; }float GamePad::ForceRT() {BYTE ret = _input->_xinputState[_id].Gamepad.bRightTrigger;if (ret < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)return 0;return ret / 255.0f; }Vector2 GamePad::GetLS() {float LX = _input->_xinputState[_id].Gamepad.sThumbLX;float LY = -_input->_xinputState[_id].Gamepad.sThumbLY;if (abs(LX) < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)LX = 0;if (abs(LY) < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)LY = 0;if (LX == 0 && LY == 0)return Vector2();//determine how far the controller is pushedfloat magnitude = sqrt(LX*LX + LY*LY);//determine the direction the controller is pushedfloat normalizedLX = LX / magnitude;float normalizedLY = LY / magnitude;return Vector2(normalizedLX, normalizedLY); }Vector2 GamePad::GetRS() {float RX = _input->_xinputState[_id].Gamepad.sThumbRX;float RY = -_input->_xinputState[_id].Gamepad.sThumbRY;if (abs(RX) < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)RX = 0;if (abs(RY) < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)RY = 0; if (RX == 0 && RY == 0)return Vector2();//determine how far the controller is pushedfloat magnitude = sqrt(RX*RX + RY*RY);//determine the direction the controller is pushedfloat normalizedRX = RX / magnitude;float normalizedRY = RY / magnitude;return Vector2(normalizedRX, normalizedRY); }float GamePad::ForceLS() {//copy 自DirectX文檔float LX = _input->_xinputState[_id].Gamepad.sThumbLX;float LY = _input->_xinputState[_id].Gamepad.sThumbLY;//determine how far the controller is pushedfloat magnitude = sqrt(LX*LX + LY*LY);//determine the direction the controller is pushedfloat normalizedLX = LX / magnitude;float normalizedLY = LY / magnitude;float normalizedMagnitude = 0;//check if the controller is outside a circular dead zoneif (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE){//clip the magnitude at its expected maximum valueif (magnitude > 32767) magnitude = 32767;//adjust magnitude relative to the end of the dead zonemagnitude -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;//optionally normalize the magnitude with respect to its expected range//giving a magnitude value of 0.0 to 1.0normalizedMagnitude = magnitude / (32767 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);}else //if the controller is in the deadzone zero out the magnitude{magnitude = 0.0f;normalizedMagnitude = 0.0f;}return normalizedMagnitude; }float GamePad::ForceRS() {//copy 自DirectX文檔float RX = _input->_xinputState[_id].Gamepad.sThumbLX;float RY = _input->_xinputState[_id].Gamepad.sThumbLY;//determine how far the controller is pushedfloat magnitude = sqrt(RX*RX + RY*RY);//determine the direction the controller is pushedfloat normalizedRX = RX / magnitude;float normalizedRY = RY / magnitude;float normalizedMagnitude = 0;//check if the controller is outside a circular dead zoneif (magnitude > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE){//clip the magnitude at its expected maximum valueif (magnitude > 32767) magnitude = 32767;//adjust magnitude relative to the end of the dead zonemagnitude -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;//optionally normalize the magnitude with respect to its expected range//giving a magnitude value of 0.0 to 1.0normalizedMagnitude = magnitude / (32767 - XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);}else //if the controller is in the deadzone zero out the magnitude{magnitude = 0.0f;normalizedMagnitude = 0.0f;}return normalizedMagnitude; }void GamePad::SetVibration(Vector2 lr) {XINPUT_VIBRATION vibration;ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));vibration.wLeftMotorSpeed = lr.a * 65535; // use any value between 0-65535 herevibration.wRightMotorSpeed = lr.b * 65535; // use any value between 0-65535 hereXInputSetState(_id, &vibration);}測試效果
下圖為通過手柄操作后的結果
實際的源碼請參考
https://github.com/Lveyou/DND
要運行此例子請跑其中的DNDTest項目
作者:吳泔游
QQ:1339484752
日期:2017-09-28
總結
以上是生活随笔為你收集整理的用XInput库使用xbox360手柄的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WinSock的recv返回值处理
- 下一篇: 【XAudio2】1.XAudio2 介