用XInput库使用xbox360手柄

您所在的位置:网站首页 hid手柄是什么意思 用XInput库使用xbox360手柄

用XInput库使用xbox360手柄

2023-11-30 05:31| 来源: 网络整理| 查看: 265

用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-3 Input_imp* _input; };

上面的值均规范化到0到1的范围。 其中LT、RT需要返回按下的程度,在XInput库中名字叫Trigger。LS、RS代表左右两个摇杆,在Xinput中叫Thumb。 其它的按钮均只有按下和非按下状态,所以使用KeyUp、KeyDown、KeyState来返回它们的状态。KeyUp代表上一帧按下,这一帧没有按下。KeyDown代表上一帧没按下,但此帧按下。KeyState返回当前是否按下。 单个状态的按钮枚举值如下:

struct DLL_API PadCode { public: enum NAME { UP = 0x00000001, DOWN = 0x00000002, LEFT = 0x00000004, RIGHT = 0x00000008, START = 0x00000010, BACK = 0x00000020, LS = 0x00000040, RS = 0x00000080, LB = 0x0100, RB = 0x0200, A = 0x1000, B = 0x2000, X = 0x4000, Y = 0x8000 }; };

通过名字,读者就应该知道代表什么含义了。如果要检测组合键应该使用第二种方式:

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]; else return 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 pushed float magnitude = sqrt(LX*LX + LY*LY); //determine the direction the controller is pushed float 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 pushed float magnitude = sqrt(RX*RX + RY*RY); //determine the direction the controller is pushed float 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 pushed float magnitude = sqrt(LX*LX + LY*LY); //determine the direction the controller is pushed float normalizedLX = LX / magnitude; float normalizedLY = LY / magnitude; float normalizedMagnitude = 0; //check if the controller is outside a circular dead zone if (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { //clip the magnitude at its expected maximum value if (magnitude > 32767) magnitude = 32767; //adjust magnitude relative to the end of the dead zone magnitude -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; //optionally normalize the magnitude with respect to its expected range //giving a magnitude value of 0.0 to 1.0 normalizedMagnitude = 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 pushed float magnitude = sqrt(RX*RX + RY*RY); //determine the direction the controller is pushed float normalizedRX = RX / magnitude; float normalizedRY = RY / magnitude; float normalizedMagnitude = 0; //check if the controller is outside a circular dead zone if (magnitude > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { //clip the magnitude at its expected maximum value if (magnitude > 32767) magnitude = 32767; //adjust magnitude relative to the end of the dead zone magnitude -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; //optionally normalize the magnitude with respect to its expected range //giving a magnitude value of 0.0 to 1.0 normalizedMagnitude = 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 here vibration.wRightMotorSpeed = lr.b * 65535; // use any value between 0-65535 here XInputSetState(_id, &vibration); } 测试效果

下图为通过手柄操作后的结果

GamePad

实际的源码请参考 https://github.com/Lveyou/DND 要运行此例子请跑其中的DNDTest项目

作者:吴泔游 QQ:1339484752 日期:2017-09-28



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3