You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Windows 11下XINPUT库无法使用,求游戏手柄开发替代库

问题描述

我基于XINPUT库(复制自开源项目)开发了Xbox 360手柄应用,代码如下。尝试执行生成的.exe文件时,报错“This app can't run on your pc”;在VS Code中运行时,提示“Error exists after running PrelaunchTask”,点击“Debug anyway”后显示“Launch: Program 'C:/C++\Gamepad.exe' does not exist”。查阅XInput版本文档得知XInput 1.4随Windows 10发布,用于UWP应用,我使用的是Windows 11 64位系统,误以为无法使用XINPUT库,现咨询可替代的游戏手柄应用开发库,若我的理解有误请指正。

Gamepad.cpp

#include "Gamepad.h"
#include <algorithm>
#include <climits>

float normalize(float input, float min, float max);

Gamepad::Gamepad(UINT id) : controllerID(id),
    deadzoneX(XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE),
    deadzoneY(XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
    ZeroMemory(&state, sizeof(XINPUT_STATE));
    ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
}

Gamepad::Gamepad(UINT id, float deadzoneX, float deadzoneY)
    : deadzoneX(deadzoneX), deadzoneY(deadzoneY)
{
    ZeroMemory(&state, sizeof(XINPUT_STATE));
    ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
}

UINT Gamepad::getControllerID() const
{
    return controllerID;
}
XINPUT_GAMEPAD* Gamepad::getGamepad()
{
    return &state.Gamepad;
}
/*XINPUT_BATTERY_INFORMATION* Gamepad::getBatteryInfo()
{
    XInputGetBatteryInformation(controllerID, XINPUT_DEVTYPE_GAMEPAD, &battery);
    return &battery;
}*/
bool Gamepad::isConnected()
{
    if (XInputGetState(controllerID - 1, &state) == ERROR_SUCCESS)
    {
        return true;
    }
    else
    {
        return false;
    }
}

float normalize(float input, float min, float max)
{
    float average = (min + max) / 2;
    float range = (max - min) / 2;
    return (input - average) / range;
} 

float Gamepad::ApplyDeadzone(float value, float maxValue, float deadzone)
{
    if (value < -deadzone)
    {
        value += deadzone; //increase neg vals to remove deadzone discontinuity
    }
    else if (value > deadzone)
    {
        value -= deadzone; //decrease pos vals to remove deadzone discontinuity
    }
    else
    {
        return 0; //hey values are zero for once
    }
    float normValue = (float)value / (float)(maxValue - deadzone);
    return std::max(-1.0f, std::min(normValue, 1.0f));
}

bool Gamepad::Update()
{
    if (!isConnected())
        return false;

    float normLX = normalize(static_cast<float>(state.Gamepad.sThumbLX), -32767, 32767);
    float normLY = normalize(static_cast<float>(state.Gamepad.sThumbLY), -32767, 32767);

    float normRX = normalize(static_cast<float>(state.Gamepad.sThumbRX), -32767, 32767);
    float normRY = normalize(static_cast<float>(state.Gamepad.sThumbRY), -32767, 32767);
    
    if (deadzoneX <= 1.0f || deadzoneY <= 1.0f)
    {
        leftStickX = ApplyDeadzone(normLX,  maxValue, deadzoneX);
        leftStickY = ApplyDeadzone(normLY, maxValue, deadzoneY);
        rightStickX = ApplyDeadzone(normRX, maxValue, deadzoneX);
        rightStickY = ApplyDeadzone(normRY, maxValue, deadzoneY);
    }
    else
    {
        leftStickX = ApplyDeadzone(normLX,  maxValue, normalize(deadzoneX, SHRT_MIN, SHRT_MAX));
        leftStickY = ApplyDeadzone(normLY, maxValue, normalize(deadzoneY, SHRT_MIN, SHRT_MAX));
        rightStickX = ApplyDeadzone(normRX, maxValue, normalize(deadzoneX, SHRT_MIN, SHRT_MAX));
        rightStickY = ApplyDeadzone(normRY, maxValue, normalize(deadzoneY, SHRT_MIN, SHRT_MAX));
    }

    leftTrigger = static_cast<float>(state.Gamepad.bLeftTrigger) / 255.0f;//normalize input 
    rightTrigger = static_cast<float>(state.Gamepad.bRightTrigger) / 255.0f;
    return true;
}

void Gamepad::Vibrate(USHORT leftSpeed, USHORT rightSpeed)
{
    vibration.wLeftMotorSpeed = leftSpeed;
    vibration.wRightMotorSpeed = rightSpeed;
    XInputSetState(controllerID - 1, &vibration);
}

void Gamepad::Vibrate(USHORT speed)
{
    vibration.wLeftMotorSpeed = speed;
    vibration.wRightMotorSpeed = speed;
    XInputSetState(controllerID - 1, &vibration);
}

void Gamepad::resetVibration()
{
    vibration.wLeftMotorSpeed = 0;
    vibration.wRightMotorSpeed = 0;
    XInputSetState(controllerID - 1, &vibration);
}

bool Gamepad::isButtonPressed(UINT button) const
{
    return (state.Gamepad.wButtons & button) != 0;
}

Main.cpp

#include <iostream>
#include <chrono>
#include <thread>
#include <iomanip>
#include "Gamepad.h"
#define NOMINMAX
#include <windows.h>

int main()
{
    Gamepad gamepad(1);
    bool aPressed = false;
    if (!gamepad.isConnected())
    {
        std::cout << "Controller not connected" << std::endl;
        return -1;
    }

    while (true)
    {
        
        if (gamepad.Update())
        {
            system("cls");
            std::cout << std::fixed << std::setprecision(2) << "Left Trigger: " << gamepad.leftTrigger <<
                ", Right Trigger: " << gamepad.rightTrigger << "n" <<

            "Left Stick " << "X: " << gamepad.leftStickX
            << ", Y: " << gamepad.leftStickY << "n" <<

            "Right Stick " << "X: " << gamepad.rightStickX
            << ", Y: " << gamepad.rightStickY << std::endl;

            if (gamepad.isButtonPressed(XINPUT_GAMEPAD_A))
            {
                aPressed = true;
            }
            else
            {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
                aPressed = false;
            }

            if (aPressed == true)
            {
                gamepad.Vibrate(20000, 20000);
            }
            else
            {
                gamepad.resetVibration();
            }

            if (gamepad.isButtonPressed(XINPUT_GAMEPAD_BACK))
                break;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    return 0;
}

Gamepad.h

#pragma once
    

#include <Windows.h>
#include <Xinput.h>

class Gamepad
{
private:
    UINT controllerID;
    XINPUT_STATE state;
    XINPUT_VIBRATION vibration;
    //XINPUT_BATTERY_INFORMATION battery;//not used in v1.4
    
    const float maxValue = 1.0f;

    float deadzoneX;
    float deadzoneY;

    float ApplyDeadzone(float value, float maxValue, float deadzone);
public:
    Gamepad(UINT id);
    Gamepad(UINT id, float deadzoneX, float deadzoneY);
    inline UINT getControllerID() const;
    XINPUT_GAMEPAD* getGamepad();
    //XInputGetBatteryInformation is not supported for v1.4
    //XINPUT_BATTERY_INFORMATION* getBatteryInfo();
    bool isConnected();
    bool Update();
    void Vibrate(USHORT leftSpeed, USHORT rightSpeed);
    void Vibrate(USHORT speed);
    void resetVibration();
    bool isButtonPressed(UINT button) const;
    
    float leftStickX, leftStickY;
    float rightStickX, rightStickY;
    float leftTrigger, rightTrigger;
};
解答

XInput版本误解纠正

Windows 11完全支持XInput库,XInput 1.4并非仅用于UWP应用,桌面Win32程序同样可以调用。此外,Windows系统还兼容更早的XInput版本(如9.1.0、1.3),这些版本对Xbox 360手柄的支持同样完善。你的代码基于XInput是可行的,无需因系统版本放弃XInput。

解决当前运行错误

1. “This app can't run on your pc”错误

大概率是编译架构不匹配:

  • 若编译的是32位程序,而系统是64位,或反之,可能出现此错误。检查编译目标架构,设置为与系统匹配的x64或x86。
  • 另一种可能是程序未成功编译,生成的exe文件损坏或不完整。

2. VS Code中的编译运行错误

  • “Error exists after running PrelaunchTask”说明编译任务执行失败,检查.vscode/tasks.json中的编译配置,确保链接了XInput库(添加xinput.libxinput1_4.lib到链接参数),且头文件路径正确。
  • “Program does not exist”是因为编译未生成目标exe文件,先解决编译错误,确保编译成功后再调试。
  • 代码小问题:Main.cpp的输出字符串中使用了"n",应改为"\n",否则会导致输出混乱,甚至影响程序运行逻辑。

替代游戏手柄开发库推荐

若确实需要替代方案,以下几个库值得考虑:

  • SDL2:跨平台手柄支持库,兼容Windows、Linux、macOS等系统,能识别绝大多数主流手柄,API简洁易用,同时提供图形、音频等多媒体功能,适合开发跨平台游戏或应用。
  • GLFW:轻量型跨平台库,常用于OpenGL/Vulkan图形应用,内置手柄输入支持,API简单,无多余依赖。
  • DirectInput:微软老牌输入库,支持更多类型的旧手柄,但对Xbox手柄的优化不如XInput,适合需要兼容多种非Xbox手柄的场景。
  • SFML:多媒体开发库,包含手柄输入模块,跨平台支持,API设计直观,适合快速开发小型多媒体应用。

内容的提问来源于stack exchange,提问作者Tenny_J

火山引擎 最新活动