使用SDL库读取手柄摇杆数据
由于之前使用 MMSystem
库对手柄的数据进行读取 [[2023-10-17]] 的方式,在笔记本上接手柄总是出现一些虚拟手柄占用接口的问题(未找到原因)。另外找一种读取手柄数据的方式。
简单介绍一下使用 [[SDL]] 库读取手柄摇杆的方法。主要参考的源码是[1]。
初始化手柄子系统
bool init() {
// Initialization flag
bool success = true;
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { // 源码里为了可视化手柄数值,进行了箭头绘制,所以额外初始化了一个video子系统
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
success = false;
} else {
// Set texture filtering to linear
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
printf("Warning: Linear texture filtering not enabled!");
}
// Check for joysticks
if (SDL_NumJoysticks() < 1) {
printf("Warning: No joysticks connected!\n");
} else {
// Load joystick
gGameController = SDL_JoystickOpen(0);
if (gGameController == NULL) {
printf("Warning: Unable to open game controller! SDL Error: %s\n",
SDL_GetError());
}
}
}
return success;
}
手柄关闭子系统
void close() {
// Close game controller
SDL_JoystickClose(gGameController);
gGameController = NULL;
// Quit SDL subsystems
SDL_Quit();
}
主程序
int WinMain(int argc, char* args[]) {
// Start up SDL and create window
if (!init()) {
printf("Failed to initialize!\n");
} else {
bool quit = false;
// Event handler
SDL_Event e;
// Normalized direction
int xDir = 0;
int yDir = 0;
// While application is running
while (!quit) {
// Handle events on queue
while (SDL_PollEvent(&e) != 0) {
// User requests quit
if (e.type == SDL_QUIT) {
quit = true;
} else if (e.type == SDL_JOYAXISMOTION) {
// Motion on controller 0
if (e.jaxis.which == 0) {
std::cout<<"e.jaxis.axis: "<<e.jaxis.axis<<std::endl;
// X axis motion
if (e.jaxis.axis == 0) {
// Left of dead zone
if (e.jaxis.value < -JOYSTICK_DEAD_ZONE) {
xDir = e.jaxis.value;
}
// Right of dead zone
else if (e.jaxis.value > JOYSTICK_DEAD_ZONE) {
xDir = e.jaxis.value;
} else {
xDir = 0;
}
}
// Y axis motion
else if (e.jaxis.axis == 1) {
// Below of dead zone
if (e.jaxis.value < -JOYSTICK_DEAD_ZONE) {
yDir = e.jaxis.value;
}
// Above of dead zone
else if (e.jaxis.value > JOYSTICK_DEAD_ZONE) {
yDir = e.jaxis.value;
} else {
yDir = 0;
}
}
}
}
}
// Calculate angle
double joystickAngle = atan2((double)yDir, (double)xDir) * (180.0 / M_PI);
std::cout << "yDire: " << yDir << " xDir: " << xDir << std::endl;
// Correct angle
if (xDir == 0 && yDir == 0) {
joystickAngle = 0;
}
}
}
// Free resources and close SDL
close();
return 0;
}
关键的逻辑是,通过一个事件 SDL_Event
来表示摇杆是否运动,如果摇杆发生了运动,通过 axis
这个属性判断是 x
还是 y
方向的运动,然后保存到相应的摇杆位置状态值中。
识别手柄被拔出
同样通过事件类型判断手柄是否被拔出
else if (e.type == SDL_JOYDEVICEREMOVED) {
printf("Warning: A joystick is removed!\n");
if (SDL_NumJoysticks() < 1) {
SDL_JoystickClose(gGameController);
gGameController = NULL;
xDir = 0;
yDir = 0;
printf("Warning: No joysticks connected!\n");
}
plugtimes++;
这里只是对单手柄的一个实现,多个手柄的逻辑会比较复杂。
特别要说明的是,plugtimes
虽然字面意思是描述插拔了几次,实际上和前面代码中的 e.jaxis.which
有关,每次插拔都会识别成一个新的手柄,所以 e.jaxis.which
这个索引量就会加 1.
识别手柄插入
同样通过事件类型判断手柄是否插入
else if (e.type == SDL_JOYDEVICEADDED) {
if (SDL_NumJoysticks() == 1) {
gGameController = SDL_JoystickOpen(0);
if (gGameController == NULL) {
printf("Warning: Unable to open game controller! SDL Error: %s\n",
SDL_GetError());
}
}
}
SDL无法正常捕获事件的问题
造成 [[SDL]] 的函数无法正常捕获所有的操作,事件类型是 SDL_POLLSENTINEL = 0x7F00
。
查看文档发现需要初始化 SDL_INIT_VIDEO
子系统[2]
正确的启动方式是
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
另外一个原因,必须由主线程捕获事件。[3] [4] [5]或者说只有主线程才能捕获到事件。
通过上述两处修改解决。
Reference
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下
作者: pomolnc
出处: https://www.cnblogs.com/pomolnc/p/18006948
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
若内容有侵犯您权益的地方,请公告栏处联系本人,本人定积极配合处理解决。