simple XInput Manager

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
User avatar
lickstab
Kobold
Posts: 26
Joined: Mon Jan 19, 2009 3:11 pm
Location: Stockholm, Sweden

simple XInput Manager

Post by lickstab »

hello. i'd like to share something i've created, in case anyone's interested.
i like my xbox360 gamepad very much, and i always used it when i made games in xna.
but now i use ogre, so i wrote a little manager to easily access any connected xbox360 gamepad.
of course this can be used in any program at all, you don't need ogre..

please note that i am by no means an expert c++ programmer, nor even an intermediate one.
i just write code that (appears to?) work for me.
all code i wrote for this is free/public domain, so feel free to use and/or modify it however you like.

to use it, you need to:

Code: Select all

#include "XInputManager.h"
XInputManager x = new XInputManager();
every frame:

Code: Select all

x->Update();
now you can simply access any gamepad like this:

Code: Select all

// 0 = first gamepad, 1= second.. 
// .error indicates if the given gamepad is disconnected.
// you can only have up to 4 gamepads at once,
// so passing any number over 3 will always result in .error == true
if(!x->GetState(0).error)
{
		if(x->GetState(0).Buttons.A)
			walkforward(x->GetState(0).LeftStick.Y);
}
all values for sticks/triggers/vibration are floats in the range 0.0f - 1.0f.
you might notice there are three sets of .Buttons/.DPad structs.
- the "plain" ones will return true every frame. (ie constant press)
- the "..Single" ones will only return true once per button press (ie single press)
- the "..Old" ones are used internally to keep track of whether to reset the Single state or not

you can also set the pad vibration using .SetVibration(padnumber, leftmotor, rightmotor).

you will need the directx sdk (http://msdn.microsoft.com/en-us/directx/aa937788.aspx)
to use this. also, the header file automatically links to XInput.lib using a #pragma statement.
i think these might be visual studio-only, so if you aren't using that, you'll have to remove that line
and link to XInput.lib manually. in that case, there's also the #pragma once statement to replace.

here are the code files:

XInputManager.h

Code: Select all

//   ___________________
//  / XInputManager.h   \
// |                     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\ 
// |  Defines an interface to access an  |
// |  Xbox 360 controller via XInput.    |
// |                                      \_______
// |  by lickstab @ http://www.ogre3d.org/forums/ \
//  \ This work is in the public domain.          /
//   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

// only include once, link to XInput.lib
#pragma once
#pragma comment(lib, "XInput.lib")

// include files
#include <windows.h>
#include <XInput.h>

// holds information about a single gamepad
struct GamepadState
{
public:
	// used for disconnected gamepads and when trying to access a non-existant gamepad (ie higher than number 4)
	bool error; 
	// for thumbsticks
	struct { float X, Y; } LeftStick, RightStick;
	// triggers
	float LeftTrigger, RightTrigger;
	// buttons except dpad
	struct
	{
		bool LeftShoulder,
			RightShoulder,
			A,
			B,
			X,
			Y,
			Back,
			Start,
			LeftThumb,
			RightThumb;
	} Buttons;
	// dpad
	struct
	{
		bool Up, Down, Left, Right;
	} DPad;

	// for limiting repeat 'presses'
	struct
	{
		bool LeftShoulder,
			RightShoulder,
			A,
			B,
			X,
			Y,
			Back,
			Start,
			LeftThumb,
			RightThumb;
	} ButtonsSingle;
	struct
	{
		bool Up, Down, Left, Right;
	} DPadSingle;
	struct
	{
		bool LeftShoulder,
			RightShoulder,
			A,
			B,
			X,
			Y,
			Back,
			Start,
			LeftThumb,
			RightThumb;
	} ButtonsOld;
	struct
	{
		bool Up, Down, Left, Right;
	} DPadOld;
};

// handles XInput gamepads
class XInputManager
{
private:
	XINPUT_STATE state[4];			// xinput states
	GamepadState Pads[4];			// represents each gamepad
	GamepadState PadError;			// returned on error
public:
	XInputManager();
	void Update();					// update pads
	GamepadState GetState(int pad);	// get a pad's info
	void SetVibration(int pad, float left, float right); // set vibration strength
	~XInputManager();
};
XInputManager.cpp

Code: Select all

//   ___________________
//  / XInputManager.cpp \
// |                     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\ 
// |  Defines an interface to access an  |
// |  Xbox 360 controller via XInput.    |
// |                                      \_______
// |  by lickstab @ http://www.ogre3d.org/forums/ \
//  \ This work is in the public domain.          / 
//   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

#include <Ogre.h>
#include "XInputManager.h"

// constructor
XInputManager::XInputManager()
{
	for(int i = 0; i<4;i++)
	{
		Pads[i].error = true;
	}
	PadError.error = true;
}

XInputManager::~XInputManager() { }

// update all input
void XInputManager::Update()
{
	DWORD dwResult;
	
	for(int i = 0; i<4;i++)
	{
		ZeroMemory( &state[i], sizeof(XINPUT_STATE) );
	
		dwResult = XInputGetState(i, &state[i]);

		if(dwResult == ERROR_SUCCESS)
		{
			Pads[i].error = false;

			// get thumbsticks
			// (deadzone stuff from http://www.kmcgrail.com/XInputTut.htm)
			if( (state[i].Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
				state[i].Gamepad.sThumbLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) &&
				(state[i].Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
				state[i].Gamepad.sThumbLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) )
				{
					Pads[i].LeftStick.X = 0.0f;
					Pads[i].LeftStick.Y = 0.0f;
				}
			else
			{
				Pads[i].LeftStick.X = state[i].Gamepad.sThumbLX / 255.0f;
				Pads[i].LeftStick.Y = state[i].Gamepad.sThumbLY / 255.0f;
			}
			if( (state[i].Gamepad.sThumbRX < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
				state[i].Gamepad.sThumbRX > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) &&
				(state[i].Gamepad.sThumbRY < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
				state[i].Gamepad.sThumbRY > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) )
				{
					Pads[i].RightStick.X = 0.0f;
					Pads[i].RightStick.Y = 0.0f;
				}
			else
			{
				Pads[i].RightStick.X = state[i].Gamepad.sThumbRX / 255.0f;
				Pads[i].RightStick.Y = state[i].Gamepad.sThumbRY / 255.0f;
			}

			// get triggers
			if(state[i].Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
			{
				Pads[i].LeftTrigger = state[i].Gamepad.bLeftTrigger / 255.0f;
			}
			else
			{
				Pads[i].LeftTrigger = 0.0f;
			}

			if(state[i].Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
			{
				Pads[i].RightTrigger = state[i].Gamepad.bRightTrigger / 255.0f;
			}
			else
			{
				Pads[i].RightTrigger = 0.0f;
			}

			// store old button states
			Pads[i].ButtonsOld.A = Pads[i].Buttons.A;
			Pads[i].ButtonsOld.B = Pads[i].Buttons.B;
			Pads[i].ButtonsOld.X = Pads[i].Buttons.X;
			Pads[i].ButtonsOld.Y = Pads[i].Buttons.Y;
			Pads[i].ButtonsOld.Start = Pads[i].Buttons.Start;
			Pads[i].ButtonsOld.Back = Pads[i].Buttons.Back;
			Pads[i].ButtonsOld.LeftShoulder = Pads[i].Buttons.LeftShoulder;
			Pads[i].ButtonsOld.RightShoulder = Pads[i].Buttons.RightShoulder;
			Pads[i].ButtonsOld.LeftThumb = Pads[i].Buttons.LeftThumb;
			Pads[i].ButtonsOld.RightThumb = Pads[i].Buttons.RightThumb;

			Pads[i].DPadOld.Up = Pads[i].DPad.Up;
			Pads[i].DPadOld.Down = Pads[i].DPad.Down;
			Pads[i].DPadOld.Left = Pads[i].DPad.Left;
			Pads[i].DPadOld.Right = Pads[i].DPad.Right;

			// get buttons
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_A)
			{
				Pads[i].Buttons.A = true;
			} else { Pads[i].Buttons.A = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_B)
			{
				Pads[i].Buttons.B = true;
			} else { Pads[i].Buttons.B = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_X)
			{
				Pads[i].Buttons.X = true;
			} else { Pads[i].Buttons.X = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_Y)
			{
				Pads[i].Buttons.Y = true;
			} else { Pads[i].Buttons.Y = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_BACK)
			{
				Pads[i].Buttons.Back = true;
			} else { Pads[i].Buttons.Back = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_START)
			{
				Pads[i].Buttons.Start = true;
			} else { Pads[i].Buttons.Start = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
			{
				Pads[i].Buttons.LeftShoulder = true;
			} else { Pads[i].Buttons.LeftShoulder = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
			{
				Pads[i].Buttons.RightShoulder = true;
			} else { Pads[i].Buttons.RightShoulder = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
			{
				Pads[i].Buttons.LeftThumb = true;
			} else { Pads[i].Buttons.LeftThumb = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
			{
				Pads[i].Buttons.RightThumb = true;
			} else { Pads[i].Buttons.RightThumb = false; }

			// get dpad
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
			{
				Pads[i].DPad.Up = true;
			} else { Pads[i].DPad.Up = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
			{
				Pads[i].DPad.Down = true;
			} else { Pads[i].DPad.Down = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
			{
				Pads[i].DPad.Left = true;
			} else { Pads[i].DPad.Left = false; }
			if(state[i].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
			{
				Pads[i].DPad.Right = true;
			} else { Pads[i].DPad.Right = false; }

			// set single press states
			// A
			if(!Pads[i].Buttons.A)
				Pads[i].ButtonsSingle.A = false;
			else
			{
				if(!Pads[i].ButtonsSingle.A && !Pads[i].ButtonsOld.A)
					Pads[i].ButtonsSingle.A = true;
				else
					Pads[i].ButtonsSingle.A = false;
			}
			// B
			if(!Pads[i].Buttons.B)
				Pads[i].ButtonsSingle.B = false;
			else
			{
				if(!Pads[i].ButtonsSingle.B && !Pads[i].ButtonsOld.B)
					Pads[i].ButtonsSingle.B = true;
				else
					Pads[i].ButtonsSingle.B = false;
			}
						if(!Pads[i].Buttons.X)
				Pads[i].ButtonsSingle.X = false;
			else
			{
			// X
			if(!Pads[i].ButtonsSingle.X && !Pads[i].ButtonsOld.X)
					Pads[i].ButtonsSingle.X = true;
				else
					Pads[i].ButtonsSingle.X = false;
			}
			// Y
			if(!Pads[i].Buttons.Y)
				Pads[i].ButtonsSingle.Y = false;
			else
			{
				if(!Pads[i].ButtonsSingle.Y && !Pads[i].ButtonsOld.Y)
					Pads[i].ButtonsSingle.Y = true;
				else
					Pads[i].ButtonsSingle.Y = false;
			}
			// Start
			if(!Pads[i].Buttons.Start)
				Pads[i].ButtonsSingle.Start = false;
			else
			{
				if(!Pads[i].ButtonsSingle.Start && !Pads[i].ButtonsOld.Start)
					Pads[i].ButtonsSingle.Start = true;
				else
					Pads[i].ButtonsSingle.Start = false;
			}
			// Back
			if(!Pads[i].Buttons.Back)
				Pads[i].ButtonsSingle.Back = false;
			else
			{
				if(!Pads[i].ButtonsSingle.Back && !Pads[i].ButtonsOld.Back)
					Pads[i].ButtonsSingle.Back = true;
				else
					Pads[i].ButtonsSingle.Back = false;
			}
			// Left Shoulder
			if(!Pads[i].Buttons.LeftShoulder)
				Pads[i].ButtonsSingle.LeftShoulder = false;
			else
			{
				if(!Pads[i].ButtonsSingle.LeftShoulder && !Pads[i].ButtonsOld.LeftShoulder)
					Pads[i].ButtonsSingle.LeftShoulder = true;
				else
					Pads[i].ButtonsSingle.LeftShoulder = false;
			}
			// Right Shoulder
			if(!Pads[i].Buttons.RightShoulder)
				Pads[i].ButtonsSingle.RightShoulder = false;
			else
			{
				if(!Pads[i].ButtonsSingle.RightShoulder && !Pads[i].ButtonsOld.RightShoulder)
					Pads[i].ButtonsSingle.RightShoulder = true;
				else
					Pads[i].ButtonsSingle.RightShoulder = false;
			}
			// Left Thumbstick
			if(!Pads[i].Buttons.LeftThumb)
				Pads[i].ButtonsSingle.LeftThumb = false;
			else
			{
				if(!Pads[i].ButtonsSingle.LeftThumb && !Pads[i].ButtonsOld.LeftThumb)
					Pads[i].ButtonsSingle.LeftThumb = true;
				else
					Pads[i].ButtonsSingle.LeftThumb = false;
			}
			// Right Thumbstick
			if(!Pads[i].Buttons.RightThumb)
				Pads[i].ButtonsSingle.RightThumb = false;
			else
			{
				if(!Pads[i].ButtonsSingle.RightThumb && !Pads[i].ButtonsOld.RightThumb)
					Pads[i].ButtonsSingle.RightThumb = true;
				else
					Pads[i].ButtonsSingle.RightThumb = false;
			}

			// DPad Up
			if(!Pads[i].DPad.Up)
				Pads[i].DPadSingle.Up = false;
			else
			{
				if(!Pads[i].DPadSingle.Up && !Pads[i].DPadOld.Up)
					Pads[i].DPadSingle.Up = true;
				else
					Pads[i].DPadSingle.Up = false;
			}
			// DPad Down
			if(!Pads[i].DPad.Down)
				Pads[i].DPadSingle.Down = false;
			else
			{
				if(!Pads[i].DPadSingle.Down && !Pads[i].DPadOld.Down)
					Pads[i].DPadSingle.Down = true;
				else
					Pads[i].DPadSingle.Down = false;
			}
			// DPad Left
			if(!Pads[i].DPad.Left)
				Pads[i].DPadSingle.Left = false;
			else
			{
				if(!Pads[i].DPadSingle.Left && !Pads[i].DPadOld.Left)
					Pads[i].DPadSingle.Left = true;
				else
					Pads[i].DPadSingle.Left = false;
			}
			// DPad Right
			if(!Pads[i].DPad.Right)
				Pads[i].DPadSingle.Right = false;
			else
			{
				if(!Pads[i].DPadSingle.Right && !Pads[i].DPadOld.Right)
					Pads[i].DPadSingle.Right = true;
				else
					Pads[i].DPadSingle.Right = false;
			}

			// whew. all done!
		}
		else
		{
			// error! error! abort!
			Pads[i].error = true;
		}
	}
}

// get the state of a given pad.
GamepadState XInputManager::GetState(int pad)
{
	// there are only 4 gamepads..
	if(pad > 3)
	{
		return PadError;
	}

	return Pads[pad];
}
// set the vibration rate of a given pad
void XInputManager::SetVibration(int pad, float left, float right)
{
	// we can't have more than 4 pads!
	if(pad > 3)
		return;

	// http://msdn.microsoft.com/en-us/library/bb173045(VS.85).aspx
	XINPUT_VIBRATION vibration;
	ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
	vibration.wLeftMotorSpeed = 65535 * left; // use any value between 0-65535 here
	vibration.wRightMotorSpeed = 65535 * right; // use any value between 0-65535 here
	XInputSetState( pad, &vibration );

}
BeyondAphelion
Gnoblar
Posts: 20
Joined: Sat Nov 07, 2009 10:20 pm

Re: simple XInput Manager

Post by BeyondAphelion »

Could you elaborate slightly on how you are implementing this? I'm getting the following error from your code:

Code: Select all

error C2440: 'initializing' : cannot convert from 'XInputManager *' to 'XInputManager'
This is simply from the line:

Code: Select all

 XInputManager x = new XInputManager();
I haven't even tried to update every frame or access the gamepad yet.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Re: simple XInput Manager

Post by jacmoe »

You are missing a star:

Code: Select all

XInputManager* x = new XInputManager();
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
BeyondAphelion
Gnoblar
Posts: 20
Joined: Sat Nov 07, 2009 10:20 pm

Re: simple XInput Manager

Post by BeyondAphelion »

Haha. Wow, I feel dumb. Thanks.