65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 XInput 在 C++ 中获取 Xbox 360 控制器输入

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.46/5 (12投票s)

2008年6月14日

CPOL

4分钟阅读

viewsIcon

230717

downloadIcon

8835

一个小型教程,介绍如何使用 XInput (需要 DX SDK) 来处理 Windows 上 Xbox 360 控制器的输入。

引言

在本教程结束时,您应该能够了解如何在 C++ 程序中实现 Windows 版 Xbox 360 控制器的基础知识,无论是游戏还是任何其他需要使用控制器的项目。

类定义 (CXBOXController)

这是我们的 Xbox 控制器类的定义代码

#ifndef _XBOX_CONTROLLER_H_
#define _XBOX_CONTROLLER_H_

// No MFC
#define WIN32_LEAN_AND_MEAN

// We need the Windows Header and the XInput Header
#include <windows.h>
#include <XInput.h>

// Now, the XInput Library
// NOTE: COMMENT THIS OUT IF YOU ARE NOT USING
// A COMPILER THAT SUPPORTS THIS METHOD OF LINKING LIBRARIES
#pragma comment(lib, "XInput.lib")

// XBOX Controller Class Definition
class CXBOXController
{
private:
    XINPUT_STATE _controllerState;
    int _controllerNum;
public:
    CXBOXController(int playerNumber);
    XINPUT_STATE GetState();
    bool IsConnected();
    void Vibrate(int leftVal = 0, int rightVal = 0);
};

#endif

代码详解

这定义了一个简单的 C++ 类,用于管理 Xbox 360 控制器。

_controllerState 存储 Xbox 360 控制器的状态,_controllerNum 存储类正在存储的控制器的引用 (0-3)。GetState() 更新控制器的状态并向调用者返回状态信息,以便可以检查输入 (这将在稍后完善程序时进行)。IsConnected() 检查以确保控制器已连接,如果成功则返回 ERROR_SUCCESSVibrate() 函数为我们提供了一种快速简便的振动控制器的方法。

完善代码

#include "CXBOXController.h"

CXBOXController::CXBOXController(int playerNumber)
{
    // Set the Controller Number
    _controllerNum = playerNumber - 1;
}

XINPUT_STATE CXBOXController::GetState()
{
    // Zeroise the state
    ZeroMemory(&_controllerState, sizeof(XINPUT_STATE));

    // Get the state
    XInputGetState(_controllerNum, &_controllerState);

    return _controllerState;
}

bool CXBOXController::IsConnected()
{
    // Zeroise the state
    ZeroMemory(&_controllerState, sizeof(XINPUT_STATE));

    // Get the state
    DWORD Result = XInputGetState(_controllerNum, &_controllerState);

    if(Result == ERROR_SUCCESS)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void CXBOXController::Vibrate(int leftVal, int rightVal)
{
    // Create a Vibraton State
    XINPUT_VIBRATION Vibration;

    // Zeroise the Vibration
    ZeroMemory(&Vibration, sizeof(XINPUT_VIBRATION));

    // Set the Vibration Values
    Vibration.wLeftMotorSpeed = leftVal;
    Vibration.wRightMotorSpeed = rightVal;

    // Vibrate the controller
    XInputSetState(_controllerNum, &Vibration);
}

代码详解

这是 `CXBOXController` 类被完善并赋予生命的地方。

首先,`CXBOXController` 构造函数根据玩家编号分配控制器编号 (玩家 1 = 0,玩家 2 = 1,玩家 3 = 2,玩家 4 = 3)。

接下来,我们完善获取控制器状态的函数。首先,我们必须将游戏状态的指针清零,以确保没有任何伪影,从而可以检查输入。然后,我们调用 `XInputGetState`,并将控制器编号和控制器状态变量的地址传递进去,以存储控制器的状态。这将确保 `_controllerState` 始终是最新的。然后,我们返回控制器的状态。

之后,我们实现 `IsConnected()`,如果控制器已连接且没有错误,则返回 true,如果出现问题则返回 false。与 `GetState()` 函数一样,我们需要将内存清零并更新状态,以便在控制器突然断开连接时能够跟上控制器的状态。如果控制器已连接且没有问题,`XInputGetState()` 将返回 `ERROR_SUCCESS`,表示一切正常。

最后,我们实现一个允许振动的特性。Xbox 360 有两个振动马达,一个在左边,一个在右边。我们通过允许左马达和右马达的单独值来考虑这一点。每个值都可以从 0 到 65535,表示马达振动的强度,65535 为最强。我们首先定义一个 `XINPUT_VIBRATION` 结构体的实例,它允许我们将振动速度存储在一个结构体中。为了谨慎起见,我们将此内存清零,然后设置振动速度,最后使用 `XInputSetState` 设置振动状态。由于我们将 `leftVal` 和 `rightVal` 的默认值设置为 0,我们可以单独调用 *controller->Vibrate()* 来停止控制器上的所有振动。

测试应用程序

这段小代码将允许我们测试控制器类

#include "CXBOXController.h"
#include <iostream>

CXBOXController* Player1;
int main(int argc, char* argv[])
{
    Player1 = new CXBOXController(1);

    std::cout << "Instructions:\n";
    std::cout << "[A] Vibrate Left Only\n";
    std::cout << "[B] Vibrate Right Only\n";
    std::cout << "[X] Vibrate Both\n";
    std::cout << "[Y] Vibrate Neither\n";
    std::cout << "[BACK] Exit\n";

    while(true)
    {
        if(Player1->IsConnected())
        {
            if(Player1->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_A)
            {
                Player1->Vibrate(65535, 0);
            }

            if(Player1->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_B)
            {
                Player1->Vibrate(0, 65535);
            }

            if(Player1->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_X)
            {
                Player1->Vibrate(65535, 65535);
            }

            if(Player1->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_Y)
            {
                Player1->Vibrate();
            }

            if(Player1->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_BACK)
            {
                break;
            }
        }
        else
        {
            std::cout << "\n\tERROR! PLAYER 1 - XBOX 360 Controller Not Found!\n";
            std::cout << "Press Any Key To Exit.";
            std::cin.get();
            break;
        }
    }

    delete(Player1);

    return( 0 );
}

我将不对最后这段代码的大部分内容做详细解释,因为它应该很明显它做什么。

但是,我将重点介绍以下几点:

CXBOXController* Player1;
...
Player1 = new CXBOXController(1);

我选择这样定义是因为我喜欢在处理控制器这类事物时使用指针。这使得我可以轻松地传递它们,如果我想以非全局变量的形式发送它们的话。构造函数中的 1 表示这是玩家 1 的控制器 (或根据 XInput 为控制器 #0)。

if(Player1->GetState().Gamepad.wButtons & XINPUT_GAMEPAD_A)

这是检查游戏手柄状态的代码。要检查按钮输入,必须使用逻辑 `AND` 来检查按钮是否被按下。

用于检查控制器状态按钮的十六进制值

(注意:从 MSDN 网站获取)

XINPUT_GAMEPAD_DPAD_UP          0x00000001
XINPUT_GAMEPAD_DPAD_DOWN        0x00000002
XINPUT_GAMEPAD_DPAD_LEFT        0x00000004
XINPUT_GAMEPAD_DPAD_RIGHT       0x00000008
XINPUT_GAMEPAD_START            0x00000010
XINPUT_GAMEPAD_BACK             0x00000020
XINPUT_GAMEPAD_LEFT_THUMB       0x00000040
XINPUT_GAMEPAD_RIGHT_THUMB      0x00000080
XINPUT_GAMEPAD_LEFT_SHOULDER    0x0100
XINPUT_GAMEPAD_RIGHT_SHOULDER   0x0200
XINPUT_GAMEPAD_A                0x1000
XINPUT_GAMEPAD_B                0x2000
XINPUT_GAMEPAD_X                0x4000
XINPUT_GAMEPAD_Y                0x8000

您还可以使用 `GetState()` 来获取操纵杆位置以及左右扳机的值,通过 `bLeftTrigger`、`bRightTrigger`、`sThumbLX`、`sThumbLY`、`sThumbRX` 和 `sThumbRY`。更多信息可以在 MSDN 上找到。

XINPUT_GAMEPAD 结构体

(注意:从 MSDN 网站获取)

typedef struct _XINPUT_GAMEPAD {
    WORD wButtons;
    BYTE bLeftTrigger;
    BYTE bRightTrigger;
    SHORT sThumbLX;
    SHORT sThumbLY;
    SHORT sThumbRX;
    SHORT sThumbRY;
} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;

历史

  • 2008 年 6 月 13 日:编写教程。
© . All rights reserved.