概述

最近突然对如何编程读取游戏手柄输入比较感兴趣。所以上网找了找相关的资料,发现没有什么简单明了的教程,所以在此将收集到跟joystick游戏杆编程相关资料整理一下,方便大家参考使用。

JoyStick简介

先给出JoyStick的维基百科介绍 维基百科词条:JoyStick
按照维基百科中的介绍,JoyStick事实上就是电子输入装置,可以输入按键,方向等数据。可以用来控制电子游戏,也可以用来控制飞行器、汽车或者其他装置等。事实上,一般的JoyStick游戏手柄或者摇杆的输入都有遵循的标准,这样我们就可以使用统一的joystick接口来读取对应的输入数据。
在我的认识中,至少我们玩飞行模拟游戏所使用的飞行摇杆,以及我们玩PS4/Xbox等游戏所使用的USB游戏手柄都属于joystick装置的。我们可以使用统一的编程接口来读取输入。

本文的目标

本文的目的是简单介绍游戏手柄输入的读取方法,并给出一些简单快捷的joystick编程方法。

JoyStick编程方法

1. 基于底层操作直接操作游戏手柄

有一篇文章Windows下对游戏杆编程也列出列了几个游戏杆的使用方法,其中第一个和第二个就是通过驱动开发接口DDK或者通过读取USB设备直接访问。只能说这样做的难度不小,而且未必能够达到我们想要的目标。喜欢探索或者编程大触可以尝试一下。

2. 使用Windows API

在Windows系统下,使用VS写读取joystick的C/C++代码是非常容易的,此处给出一篇参考文章:JoyStick编程学习笔记。
给出一段测试读取游戏手柄输入的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>#include<Windows.h>//添加joystick操作api的支持库
#include<MMSystem.h>
#pragma comment(lib, "Winmm.lib")int main(int argc, char* argv[])
{JOYINFO joyinfo;//定义joystick信息结构体JOYINFOEX joyinfoex;joyinfoex.dwSize = sizeof(JOYINFOEX);joyinfoex.dwFlags = JOY_RETURNALL;while(1){//读取手柄信息UINT joyNums;joyNums = joyGetNumDevs();
//      printf("当前手柄数量:%d \n",joyNums);if (joyNums>=1){MMRESULT joyreturn = joyGetPosEx(JOYSTICKID1, &joyinfoex);if(joyreturn == JOYERR_NOERROR){printf("0x%09d ", joyinfoex.dwXpos);printf("0x%09d ", joyinfoex.dwYpos);//printf("0x%09X ", joyinfoex.dwZpos);//printf("0x%09X ", joyinfoex.dwPOV);//printf("0x%09X ", joyinfoex.dwButtons);printf("\n");}else{switch(joyreturn) {case JOYERR_PARMS :printf("bad parameters\n");break;case JOYERR_NOCANDO :printf("request not completed\n");break;case JOYERR_UNPLUGGED :printf("joystick is unplugged\n");break;default:printf("未知错误\n");break;}}}if(kbhit()) break;Sleep(300);}return 0;
}

上述代码实现的效果是在命令行窗口中循环读取游戏手柄设备输入 ,并将读取到到数据打印出来。如果有游戏手柄插入,并按下对应的按键,则对应的打印数据就会发生变化。
测试时用的是我之前买的一个老旧的杂牌游戏手柄。在测试代码是否有效时遇到的非常多的问题:

  1. 首先,不管我插不插游戏手柄,joyGetNumDevs的返回值始终是16,后来查了些资料:在C++ Builder中使用游戏操纵杆说如果电脑有游戏端口,那么joyGetNumDevs 返回值通常为16。。。但是也没告诉我怎样判断游戏手柄是不是插入了,所以只能通过joyGetPosEx的返回值来进行判断了。
  2. 第二个问题是使用上述代码编译出来的exe运行时,在不同的电脑和系统上读取游戏手柄输入的效果时灵时不灵。测试环境就是win10,win7和xp,分别测试了使用vc6.0以及vs2010编译运行,总之测试结果达不到编译一个exe然后到其他平台使用的效果。

3. 使用Directlnput或者XInput技术

DirectInput是微软提供的一个输入设备的API,用于结合键盘、鼠标、摇杆,或其它的游戏控制器。如果是想要在Windows平台下使用摇杆的,可以参考Directlnput和XInput这两篇文章。
如果是游戏开发,可能对操纵杆或者输入设备的操作比较复杂,而且对兼容性要求较高,而DirectInput和XInput提供的接口比较全面,而且和direct X的技术结合紧密。所以这个技术应该是开发Windows平台游戏的不二选择了。

4. 使用joystick接口库

以前曾经用过一个windwos平台上的JoyStick库,使用这个库操作joystick很是方便。可是忘记了这个库叫什么。不过我在github上找了找,还真找到了一些joystick库,先给出两个结果:

  1. SDL-mirror/SDL
  2. Tasssadar/libenjoy

SDL全称是Simple DirectMedia Layer,是一个很全面的跨平台媒体/游戏开发库,但是我没精力折腾这些,所以转向了libenjoy。这是一个简单的JoyStick操作接口库,使用C语言实现,可以与任何C/C++应用程序一起使用,而且是跨平台的,可以说非常方便。在此给出libenjoy工程中的测试例程和libenjoy库的源代码与测试工程:

//测试例程
#include <stdio.h>
#ifdef __linux#include <unistd.h>
#else#include <windows.h>
#endif//包含libenjoy库的头文件
#include "../src/libenjoy.h"// This tels msvc to link agains winmm.lib. Pretty nasty though.
// 导入winmm.lib库
#pragma comment(lib, "winmm.lib")int main()
{// libenjoy初始化libenjoy_context *ctx = libenjoy_init(); // initialize the librarylibenjoy_joy_info_list *info;// Updates internal list of joysticks. If you want auto-reconnect// after re-plugging the joystick, you should call this every 1s or so// 更新joystick可用列表,如果想要实现热插拔效果,则需要每隔1秒调用一次这个函数libenjoy_enumerate(ctx);// get list with available joysticks. structs are// 获得joystick可用的列表// typedef struct libenjoy_joy_info_list {//     uint32_t count;//     libenjoy_joy_info **list;// } libenjoy_joy_info_list;//// typedef struct libenjoy_joy_info {//     char *name;//     uint32_t id;//     char opened;// } libenjoy_joy_info;//// id is not linear (eg. you should not use vector or array), // and if you disconnect joystick and then plug it in again,// it should have the same IDinfo = libenjoy_get_info_list(ctx);if(info->count != 0) // just get the first joystick {libenjoy_joystick *joy;printf("Opening joystick %s...", info->list[0]->name);joy = libenjoy_open_joystick(ctx, info->list[0]->id);//获得第一个游戏杆的信息if(joy){int counter = 0;libenjoy_event ev;printf("Success!\n");printf("Axes: %d btns: %d\n", libenjoy_get_axes_num(joy),libenjoy_get_buttons_num(joy));while(1){// Value data are not stored in library. if you want to use// them, you have to store them// That's right, only polling available// 调用libenjoy_poll函数监听joystick按键事件while(libenjoy_poll(ctx, &ev)){switch(ev.type){case LIBENJOY_EV_AXIS:printf("%u: axis %d val %d\n", ev.joy_id, ev.part_id, ev.data);break;case LIBENJOY_EV_BUTTON:printf("%u: button %d val %d\n", ev.joy_id, ev.part_id, ev.data);break;case LIBENJOY_EV_CONNECTED:printf("%u: status changed: %d\n", ev.joy_id, ev.data);break;}}
#ifdef __linuxusleep(50000);
#elseSleep(50);
#endifcounter += 50;// 如果joystick被拔出了,则每隔1秒调用一次libenjoy_enumerate函数// 来监控joystick的连接状态if(counter >= 1000){libenjoy_enumerate(ctx);counter = 0;}}// Joystick is really closed in libenjoy_poll or libenjoy_close,// because closing it while libenjoy_poll is in process in another thread// could cause crash. Be sure to call libenjoy_poll(ctx, NULL); (yes,// you can use NULL as event) if you will not poll nor libenjoy_close// anytime soon.// 关闭joystick库libenjoy_close_joystick(joy);}elseprintf("Failed!\n");}elseprintf("No joystick available\n");// Frees memory allocated by that joystick list. Do not forget it!// 清除内存libenjoy_free_info_list(info);// deallocates all memory used by lib. Do not forget this!// libenjoy_poll must not be called during or after this call// 关闭libenjoy库,在关闭之后,就不可以再调用libenjoy_poll函数了libenjoy_close(ctx);return 0;
}

总结

本文介绍了joystick游戏杆编程的基本概念,并给出了几种读取joystick游戏杆输入的方法。这几种方法种,我最青睐的还是第四种使用joystick接口库。虽然使用Windows平台微软提供的库进行joystick编程也很方便,但是我在使用时还是遇到了许多兼容性问题。使用joystick接口库则是拿来了一个造好并且调试好的轮子,直接使用很方便。
为了更加方便大家使用,在此将libenjoy库编译成为了静态库和动态库:libenjoy动态链接库(win32vc6.0)。Linux版的暂时没有需求,所以就没有做,有需要的可以自己来。

引用:

  1. 维基百科词条:JoyStick
  2. Windows下对游戏杆编程
  3. JoyStick编程学习笔记
  4. 在C++ Builder中使用游戏操纵杆
  5. Directlnput
  6. XInput
  7. SDL-mirror/SDL
  8. Tasssadar/libenjoy

资源:

  1. libenjoy_master源码和测试工程
  2. libenjoy动态链接库(win32vc6.0)

JoyStick游戏杆编程实践相关推荐

  1. 游戏杆编程心得二:如何判断按钮的有效按下

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在游戏杆编程中通过一般需要获取按钮状态来执行特定事件,比如实现按下按钮1一次,变量num递增100,但在调试系统时往往 ...

  2. Windows下对游戏杆编程

    我现在所知道的获取游戏杆输入的方法有4种,第4种才是我要说的正题. 1.用DDK通过USB接口直接访问游戏杆 2.通过汇编访问游戏杆 据说在NT下不能这么用了 3.用Direct Input 也许会很 ...

  3. Cocos2d-x 类COC手游与RTS(即时战略)游戏的编程实践总结

    本文乃Siliphen原创,转载请注明出处:http://blog.csdn.net/stevenkylelee 概述 先来看一段视频.这个视频很短.4分钟.是我的一个技术demo演示视频. http ...

  4. 3D游戏编程实践——打飞碟(Hit UFO)游戏运动与物理兼容版

    这次编程实践是在上一次实践的基础上,增加了物理运动,做到了运动学和物理运动会的兼容.而原来的代码只实现了运动学部分. 首先需要实现物理运动部分的Action类和ActionManager类. 相比起运 ...

  5. Python游戏编程实践2:魔塔

    Python游戏编程实践2:魔塔 前言 运行 前言 在逗游游戏盒上玩了一个名叫<魔塔>的游戏,感觉颇有心得体会."与其临渊羡鱼,不如退而结网",使用Python语言编写 ...

  6. Python游戏编程实践3:魔法洞穴2

    Python游戏编程实践3:魔法洞穴2 前言 运行 前言 之前在TapTap上玩了一个名叫<魔法洞穴2>的小游戏,感觉很有意思,就使用Python语言编写了一个类似的命令行小游戏,为了简单 ...

  7. 3D游戏编程实践——Priests and Devils

    编程实践--Priests and Devils github链接:https://github.com/ctlchild/SYSU-unity3d-learning/tree/master/hw2 ...

  8. 3D游戏编程实践——Priests and Devils(动作分离版)

    编程实践--Priests and Devils 动作分离版 Priests and Devils is a puzzle game in which you will help the Priest ...

  9. Python游戏编程实践1:鲁蒂亚的世界

    Python游戏编程实践1:鲁蒂亚的世界 前言 运行 前言 在逗游游戏盒上玩了一个名叫<鲁蒂亚的世界>的小游戏,感觉颇有心得体会."与其临渊羡鱼,不如退而结网",使用P ...

最新文章

  1. NLP被英语统治?打破成见,英语不应是「自然语言」同义词
  2. 基于经验的SOA成功原则
  3. 监控工具—Prometheus—监控Java程序
  4. 设计模式(命令模式)
  5. 失血多少会贫血_阿胶糕治贫血两大好处,治疗贫血两大方法要掌握
  6. 软件测试--cookie学习
  7. 免费的开源软件那么“香”,为何他们宁愿花钱去买软件?
  8. 白糖详细 制造工艺、等级划分、国家标准号和注意事项
  9. iOS如何完成蓝牙打印机功能
  10. 《架构师修炼之道》读书笔记之五:换位思考
  11. HTML 字体图标的引入
  12. 记一次npm install 报RequestError: connect ETIMEDOUT 20.205.243.166:443和RequestError: socket hang up
  13. IT路要如何走?——老程序员的10个忠告
  14. RTSP拉流h265(hevc)+AAC关键节点详解!史上最全RTSP+hevc 交互全记录!
  15. 机器学习基础---pr曲线的绘制
  16. 《海外华人写兰州国际书法展》亮相美国丹佛
  17. 11.30 - 每日一题 - 408
  18. 《计算机科学》投稿心得
  19. C#语言生成PDF文件函数总结
  20. PowerMILL Robot 2017 机器人编程视频教程

热门文章

  1. 2016-2017 7th BSUIR Open Programming Contest. Final 补题
  2. 修改3389远程端口号
  3. Altium Designer10铺铜技巧小结
  4. UVA 1218 完美的服务
  5. 怎样使用NetFlow分析网络异常流量一
  6. 匹配文本中的网址java正则表达式
  7. matlab多径信道模型,多径时变信道模型的仿真与性能分析课程设计(样例3)
  8. Final Cut Pro 导出视频教程「上」
  9. Confluence相关背景知识
  10. 手动搭建redis集群