Xbox 360 Controller Input in C++ via XInput

So you have that Xbox360 controller laying around and want to connect it to your game?

On XNA this is an out of the box option but if you’re using C++ you have a bit more work to do first.

First of all, you will need the DirecX 9.0+ sdk.

The includes.

#define WIN32_LEAN_AND_MEAN // We don't want the extra stuff like MFC and such
#include <windows>
#include <XInput.h>     // XInput API
#pragma comment(lib, "XInput.lib")   // Library. If your compiler doesn't support this type of lib include change to the corresponding one

Let’s define the buttons

typedef enum
{
	GamePad_Button_DPAD_UP          = 0,
	GamePad_Button_DPAD_DOWN        = 1,
	GamePad_Button_DPAD_LEFT        = 2,
	GamePad_Button_DPAD_RIGHT       = 3,
	GamePad_Button_START            = 4,
	GamePad_Button_BACK             = 5,
	GamePad_Button_LEFT_THUMB       = 6,
	GamePad_Button_RIGHT_THUMB      = 7,
	GamePad_Button_LEFT_SHOULDER    = 8,
	GamePad_Button_RIGHT_SHOULDER   = 9,
	GamePad_Button_A                = 10,
	GamePad_Button_B                = 11,
	GamePad_Button_X                = 12,
	GamePad_Button_Y                = 13,
	GamePadButton_Max =             14
}GamePadButton;

After some digging I don’t think Microsoft exposes access to the Guide button, let me know if you find anything. Anyway it’s not something we need on Windows.

Now the GamePadIndexes

// GamePad Indexes
typedef enum
{
	GamePadIndex_One =  0,
	GamePadIndex_Two =  1,
	GamePadIndex_Three =2,
	GamePadIndex_Four = 3,
}GamePadIndex;

The GamePadIndex is to define the player position. Like on the Xbox360, this way we can control up to 4 controllers.

We need something to store the GamePad state

// The GamePad State Stuct, were we store the buttons positions
struct GamePadState 
{
	bool		_buttons[GamePadButton_Max];
	Vector2 	_left_thumbstick;               // <= I'm using a Vector2 (floats) class but you can replaced it with a float X and Y or whatever your Vector2 class is
	Vector2	        _right_thumbstick;
	float		_left_trigger;
	float		_right_trigger;	
        // Just to clear all values to default
	void reset()
	{
		for (int i=0;i<(int)GamePadButton_Max;++i) _buttons[i] = false;
		_left_thumbstick.set(0.0f);
		_right_thumbstick.set(0.0f);
		_left_trigger = _right_trigger = 0.0f;
	}
};

Now a small class

 
class GamePadXbox 
{
public: 
	GamePadXbox(GamePadIndex player)			
	{
                  _playerIndex = player;
                  State.reset();
	}
 
	virtual ~GamePadXbox(void)
	{
		// We don't want the controller to be vibrating accidentally when we exit the app
		if(is_connected()) vibrate(0.0f,0.0f);
	}
 
	bool is_connected();
        void vibrate(float leftmotor = 0.0f, float rightmotor = 0.0f);
	void update();
 
 public:
   GamePadState	State;
 private:
   XINPUT_STATE _controllerState;
   GamePadIndex _playerIndex;
 
};

Now, onto the GamePadXbox::is_connected()

bool is_connected()
{
     // clean the state
     memset(&_controllerState,0, sizeof(XINPUT_STATE));
 
     // Get the state
     DWORD Result = XInputGetState(_controllerNum, &_controllerState);
 
     if(Result == ERROR_SUCCESS)	return true;
     else return false;
}

Documentation for the XInputGetState is here.

Onto the vibrate function. Now, the Vibration accepts values between 0 and 65535. But we don’t want to be typing that every time so the vibrate accepts from a range of 0.0f (0) to 1.0f (65535);

void vibrate(float leftmotor = 0.0f, float rightmotor = 0.0f)
{
	// Create a new Vibraton 
	XINPUT_VIBRATION Vibration;
 
	memset(&Vibration, 0, sizeof(XINPUT_VIBRATION));
 
	int leftVib = (int)(leftmotor*65535.0f);
	int rightVib = (int)(rightmotor*65535.0f);
 
	// Set the Vibration Values
	Vibration.wLeftMotorSpeed = leftVib;
	Vibration.wRightMotorSpeed = rightVib;
        // Vibrate the controller
	XInputSetState((int)_controllerNum, &Vibration);
}

On the destructor I’ve placed the vibrate at 0.0f otherwise the controller may be left vibrating after your game exits until the user unplugs it.

Finally, the update() function. Now this one is more out of convenience, since you could get the state straight out of XINPUT_STATE but I think this is a more consistent way of doing it. You can decide how you want on yours.

void update()
{
	State.reset();
        // The values of the Left and Right Triggers go from 0 to 255. We just convert them to 0.0f=>1.0f
	if(_controllerState.Gamepad.bRightTrigger && _controllerState.Gamepad.bRightTrigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
	{
		State._right_trigger = _controllerState.Gamepad.bRightTrigger/255.0f;
	}
 
	if(_controllerState.Gamepad.bLeftTrigger && _controllerState.Gamepad.bLeftTrigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
	{
		State._left_trigger = _controllerState.Gamepad.bLeftTrigger/255.0f;
	}
 
       // Get the Buttons
        if(_controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_A) State._buttons[GamePad_Button_A] = true;
        if(_controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_B) State._buttons[GamePad_Button_B] = true;
	if(_controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_X) State._buttons[GamePad_Button_X] = true;
	if(_controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_Y) State._buttons[GamePad_Button_Y] = true;
	if(_controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) State._buttons[GamePad_Button_DPAD_DOWN] = true;
		// The Rest is missing, you can figure out the rest :)
 
....
             (ThumbSticks code below)   
}

For getting all buttons here are the official flags according to 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

The ThumbSticks are a bit more tricky. They return 2 axis each but you have to make sure they are not in the deadzone. The deadzone pretty much are values !=0 but that are not relevant and can lead to errors.

 
  // Check to make sure we are not moving during the dead zone
  // Let's check the Left DeadZone
  if( (_controllerState.Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
	_controllerState.Gamepad.sThumbLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) &&
	(_controllerState.Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
	_controllerState.Gamepad.sThumbLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) )
  {    
	_controllerState.Gamepad.sThumbLX = 0;
	_controllerState.Gamepad.sThumbLY = 0;
  }
 
  // Check left thumbStick
 
  float leftThumbY = _controllerState.Gamepad.sThumbLY;
  if(leftThumbY)
  {
        State._left_thumbstick.Y = leftThumbY;
  }
  float leftThumbX = _controllerState.Gamepad.sThumbLX;
  if(leftThumbX)
  {
	State._left_thumbstick.X = leftThumbX;
  }    
 
  // For the rightThumbstick it's pretty much the same.

So how would we use this then?

int main()
{
     GamePadXbox* pad = new GamePadXbox(GamePadIndex_One);
 
     while(1)
     {
       if(pad->is_connected())
       {
          pad->update();
 
          if(pad->State._buttons[GamePad_Button_BACK]==true) break;
       }   
     }
     delete pad;
}

Anyway, most of this code is not elegant or “pretty” but it should work, the idea is for you to understand the concept, the rest is up to you.

Leave a Reply