用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返回当前是否按下。
单个状态的按钮枚举值如下:

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];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手柄相关推荐

  1. Xbox360手柄驱动xbcd+

    今天玩街机游戏的时候,发现winkawaks的手柄设置中不认我的左方向键,只能将左摇杆作为方向键使用,虽然摇杆精确度比方向键高不少,在古墓之类的3D游戏中确实十分出色.但在街机这类频繁的切换方向的传统 ...

  2. 【STM32F4系列】【HAL库】【自制库】ps2手柄模块驱动

    外观和电气连接 外观 手柄外观如下 接收器外观 这是接收器和底座 电气连接 需要4根连接线 单片机输出是CLK DO CS 单片机输入是DI 电源电压是3.3-5v 注意模块和单片机共地 模块不支持高 ...

  3. 【问题解决】更新PS4手柄库PS4-esp32编译报错GAP_ConnOpen问题解决方法

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 210925程序编译报错及解决方法 今天又又搞小车,准备用ps4手柄控制,经常的日常的 ...

  4. [STM32F1]基于STM32F103实现Xbox 360 Controller for Windows 之Xinput

    上个月由于家里有事,实现了并枚举成功的Xinput,就差数据处理没有完成,请假在家,客户急要,只好请同事改改,玩usb hid已经差不多两年了,至今也算没有实现Xbox 360 Controller ...

  5. [joysticker]使用Ubuntu读取USB手柄/方向盘的输出控制

    摘要:在淘宝上买到的游戏手柄/USB卖家只给了Windows下的驱动,本来以为Ubuntu下没有驱动,没想到网上早已经有人用cpp开发出了USB手柄的驱动,搜索很多博客的方法终于从手柄拿到数据. Li ...

  6. 买了一个Xbox360有线手柄

    假归来,去中关村的电玩市场转了转,本来想买一个PSP的,可是价格都不大合适,老板都说不如过了十五再看看.转着转着,就瞅着一直想买的Xbox360手柄了,以前看网上说是能直接能在电脑上玩的,便问了问,几 ...

  7. xbox无线适配器驱动_手柄杂谈,PS4手柄,Xbox手柄使用感受

    手柄 北通 北通阿修罗手柄,连接方式USB,按键分布与xbox360手柄类似,PC使用无需额外下载软件驱动等,可能是硬件不兼容,只支持部分steam游戏. PS4 PS4 pro原装手柄,连接方式有线 ...

  8. xbox360 功率测试软件,【外星人 Alpha ASM100-1580 游戏主机使用总结】性能|电压|功耗|跑分_摘要频道_什么值得买...

    外星人 Alpha ASM100-1580 游戏主机使用总结(性能|电压|功耗|跑分) 在讨论性能之前,再最后看一看电源.NUC电源外置是必然,除了体积之外,还要考虑散热,所以,电源外置至少在目前来看 ...

  9. 【游戏手柄】如何在PC上使用非XBOX手柄玩游戏

    PC上许多游戏其实都可以使用手柄玩,如果使用XBOX手柄连接,游戏就会自动切换到手柄模式,但是但是XBOX手柄动辄几百块的价格让我这种非硬核游戏玩家感到索然无味,高中时买过一款通威的USB游戏手柄,工 ...

最新文章

  1. 网页转 markdown 的工具
  2. 深度学习论文资源(截至2016年)
  3. 【知识连载】 如何用钉钉宜搭制定企业疫情防控数字化管理方案
  4. Google推出一款用户隐私保护工具箱
  5. php var_dump和var_export区别
  6. 天天生鲜商品详情页HTML+css
  7. wgs84坐标系经纬度投影_南方cass坐标转经纬度_坐标转经纬度软件
  8. postman控制台打印数据
  9. mui 控制旋转屏幕方向
  10. R语言分析股票指数的GARCH效应
  11. Revit-BIM模型轻量化 帧率控制
  12. OpenCL-学习教程(一)
  13. 计算机网络——校园网端口流量passby分析
  14. Nervos 双周报第 8 期:用爱发电的 RustCon Asia
  15. 模型无关的时间序列度量方法及特征
  16. Python Selenium 抖音直播平台实现自动发送评论
  17. Qt 框架性开发实践——基础框架篇
  18. java abc输出bca_面试题24(写一个函数,例如:给你的 a b c 则输出 abc acb bac bca cab c...
  19. EVE-NG-Win-Client-Pack抓包软件与vnc客户端下载,Wireshark、Ultravnc组件下载
  20. Uniform Buffer

热门文章

  1. 2018年秋计算机基础在线作业,《计算机应用基础》2017年的秋学期在线作业三.doc...
  2. 基于Spring-Data-Redis存储JavaBean对象
  3. 今日arXiv精选 | 13篇EMNLP 2021最新论文
  4. WWW 2021 | 通过强化学习控制对话式检索的风险
  5. 直播 | AAAI 2021:自然语言中token-aware的虚拟对抗样本训练
  6. 学多少返多少 | 人工智能核心课零门槛就业涨薪培养计划
  7. 重磅公开课推荐 | 如何搭建聊天机器人:技术架构剖析
  8. Java 图形用户界面 AWT事件处理
  9. python如何修改excel数据库_python修改excel数据库
  10. mysql执行计划_mysql的sql执行计划详解