AGT: Using XBox360 Pad with XInput

XBox 360 ControllerFor my AGT assignment I’ve decided to use a XBox 360 pad for input since I found out it can be used in Windows. To do this requires use of the XInput library that comes with DirectX.

So after setting up my input manager which automatically updates all input devices (there can be a maximum of four pad devices, defined by the variable XUSER_MAX_COUNT in the XInput header), I began work on the controller class to manage the state information about the pad.

The following header is my source for this class, when a controller is created, an id is passed to it that is used as the pad index for XInput.

#ifndef CONTROLLER_H_INCLUDED
#define CONTROLLER_H_INCLUDED
 
// --- [ libraries ] ------------------------------------------
#pragma comment(lib, "XInput.lib")
 
// --- [ includes ] -------------------------------------------
#include <windows.h>
#include <XInput.h>
 
// --- [ class ] ----------------------------------------------
class Controller
{
	//
	public:
		enum Button
		{
			kButton_DPad_Up = 0,
			kButton_DPad_Down,
			kButton_DPad_Left,
			kButton_DPad_Right,
			kButton_Start,
			kButton_Back,
			kButton_Thumb_Left,
			kButton_Thumb_Right,
			kButton_Shoulder_Left,
			kButton_Shoulder_Right,
			kButton_X,
			kButton_Y,
			kButton_A,
			kButton_B
		}
 
		enum AxisType
		{
			kAxis_Left = 0,
			kAxis_Right,
			kNumAxisTypes
		};
 
		enum MotorType
		{
			kMotor_Left = 0,
			kMotor_Right,
			kNumMotorTypes
		};
 
		enum TriggerType
		{
			kTrigger_Left = 0,
			kTrigger_Right,
			kNumTriggerTypes
		};
 
		struct AxisData
		{
			double x;
			double y;
		};
 
	// Attributes
	private:
		const unsigned int m_id;
		bool m_isConnected;
 
		unsigned int m_currentButtons;
		unsigned int m_previousButtons;
 
		AxisData m_axis[kNumAxisTypes];
		float m_triggers[kNumTriggerTypes];
		double m_motorSpeed[kNumMotorTypes];
 
	// Functions
	public:
		// Constructor
		Controller( const unsigned int inControllerId );
 
		// Check button has just been pressed
		bool IsButtonDown( const Button inButton ) const
		{
			return ( ~m_previousButtons & m_currentButtons ) & ( 1 << inButton );
		}
 
		// Check button has just been released
		bool IsButtonUp( const Button inButton ) const
		{
			return ( m_previousButtons & ~m_currentButtons ) & ( 1 << inButton );
		}
 
		// Check button is pressed
		bool IsButtonPressed( const Button inButton ) const
		{
			return m_currentButtons & ( 1 << inButton );
		}
 
		// Get Thumb Axis
		void GetAxis( const AxisType inType, AxisData & outData ) const
		{
			outData = m_axis[inType];
		}
 
		// Get Trigger Values
		float GetTrigger( const TriggerType inType ) const
		{
			return m_triggers[inType];
		}
 
		// Get Motor Speed
		double GetMotorSpeed( const MotorType inType ) const
		{
			return m_motorSpeed[inType];
		}
 
		// Set MotorSpeed
		void SetMotorSpeed( const MotorType inType, const double inValue );
 
		// Check Controller is Connected
		bool IsConnected() const
		{
			return m_isConnected;
		}
 
		// Get Controller Id
		unsigned int GetId() const
		{
			return m_id;
		}
 
		// Update
		void Update();
 
	private:
		// Update an Axis
		void UpdateAxis( const AxisType inType, const short inX, const short inY );
 
		// Update a Trigger
		void UpdateTrigger( const TriggerType inType, const unsigned char inValue );
};

 

All values that are variable are values between 0.0 and 1.0 in the case of triggers and motor speeds, and -1.0 and 1.0 in the case of axis components. You may have noticed that there is only one function that can change values explicitly, SetMotorSpeed(), the only other way to change values is direct input from the controller, so Update() must be called every frame.

You may notice that the division used to normalise the speed is 0x7FFF instead of 0xFFFF, this is because the number is signed, not unsigned like the other values we normalise.

Calling Update() more than once will break the IsButtonUp() and IsButtonDown() calls to always return false since they are based on the previous button states gotten by an update call.

The following is the source code for the controller class source file.

// --- [ includes ] -------------------------------------------
#include "Controller.h"
 
// --- [ constructor / destructor ] ---------------------------
Controller::Controller( const unsigned int inControllerId )
	:
	m_id( inControllerId ),
	m_isConnected( false ),
	m_currentButtons( 0 ),
	m_previousButtons( 0 )
{
	for( unsigned int i = 0; i < kNumAxisTypes; ++i )
	{
		m_axis[i].x = 0.0;
		m_axis[i].y = 0.0;
	}
 
	for( unsigned int i = 0; i < kNumTriggerTypes; ++i )
	{
		m_triggers[i] = 0.0f;
	}
 
	for( unsigned int i = 0; i < kNumMotorTypes; ++i )
	{
		m_motorSpeed[i] = 0.0f;
	}
}
 
// --- [ functions ] ------------------------------------------
void Controller::Update()
{
	XINPUT_STATE state;
 
	// Get new controller state and check if it is connected
	m_isConnected = XInputGetState( m_id, &state ) == ERROR_SUCCESS;
	if( !m_isConnected )
	{
		return;
	}
 
	// Set New Axis Positions and normalise
	UpdateAxis( kAxis_Left, state.Gamepad.sThumbLX, state.Gamepad.sThumbLY );
	UpdateAxis( kAxis_Right, state.Gamepad.sThumbRX, state.Gamepad.sThumbRY );
 
	// Set New Trigger Values and normalise
	UpdateTrigger( kTrigger_Left, state.Gamepad.bLeftTrigger );
	UpdateTrigger( kTrigger_Right, state.Gamepad.bRightTrigger );
 
	// Set previous button states to m_currentButtons, and current button
	// state to new button state.
	m_previousButtons = m_currentButtons;
	m_currentButtons = static_cast<unsigned int>( state.Gamepad.wButtons );
}
 
void Controller::SetMotorSpeed( const MotorType inType, const double inValue )
{
	XINPUT_VIBRATION vibration;
 
	m_motorSpeed[inType] = inValue;
 
	vibration.wLeftMotorSpeed = static_cast<WORD>( 0xffff * m_motorSpeed[kMotor_Left] );
	vibration.wRightMotorSpeed = static_cast<WORD>( 0xffff * m_motorSpeed[kMotor_Right] );
 
	XInputSetState( m_id, &vibration );
}
 
void Controller::UpdateAxis( const AxisType inType, const short inX, const short inY )
{
	const short kAxisDeadzones[kNumAxisTypes] =
	{
	XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE,
	XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE
	};
 
	m_axis[inType].x = 0.0;
	m_axis[inType].y = 0.0;
 
	if( inX < -kAxisDeadzones[inType] || inX > kAxisDeadzones[inType] )
	{
		m_axis[inType].x = static_cast<double>( state.Gamepad.sThumbLX ) / static_cast<double>( 0x7fff );
	}
 
	if( inY < -kAxisDeadzones[inType] || inY > kAxisDeadzones[inType] )
	{
		m_axis[inType].y = static_cast<double>( state.Gamepad.sThumbLY ) / static_cast<double>( 0x7fff );
	}
}
 
void Controller::UpdateTrigger( const TriggerType inType, const unsigned char inValue )
{
	m_triggers[inType] = 0.0f;
 
	if( inValue > XINPUT_GAMEPAD_TRIGGER_THRESHOLD )
	{
		m_triggers[inType] = static_cast<float>( inValue ) / static_cast<float>( 0xff );
	}
}

 

And that is all that is required for getting state information from a XBox 360 pad.