Hi there!
I´m writing a wrapper around
XInput, trying to keep the interface as similar to the
SFML API as possible.
If you guys like this code and find it usefull, I will add it to the wiki page
The code compiles and works fine with MinGW
It works with my wired X360 controller, I don´t know if it will work with the wireless controller
Of course you will need the DirectX SDK installed
Note that this will work
only with XBox 360 compatible devices, for another kind of gamepads you still need to use the SFML Joystick API
Ok, here is the code:
X360Controller.hpp
#ifndef X360_CONTROLLER_HPP
#define X360_CONTROLLER_HPP
#include <SFML/System/Vector2.hpp>
namespace xb {
class Joystick {
public:
// Typedefs
typedef unsigned int t_joyNum;
typedef unsigned short t_buttonNum;
// Enums
enum {
Count = 4 // Player 0-3
};
enum {
DPAD_UP = 0x0001,
DPAD_DOWN = 0x0002,
DPAD_LEFT = 0x0004,
DPAD_RIGHT = 0x0008,
START = 0x0010,
BACK = 0x0020,
LEFT_THUMB = 0x0040,
RIGHT_THUMB = 0x0080,
LB = 0x0100,
RB = 0x0200,
A = 0x1000,
B = 0x2000,
X = 0x4000,
Y = 0x8000,
};
// Functions (similar to SFML API)
static bool isConnected (t_joyNum joyNum);
static unsigned int getButtonCount (t_joyNum joyNum) { return 14; }
static bool isButtonPressed (t_joyNum joyNum, t_buttonNum buttonNum);
// X360 specific functions
static bool isAnyXBox360ControllerConnected();
static void getTriggers (t_joyNum joyNum, float &left, float &right);
static void getSticksPosition (t_joyNum joyNum, sf::Vector2f &left, sf::Vector2f &right);
static void setVibration (t_joyNum, float leftMotor = 0.0f, float rightMotor = 0.0f);
}; // class
} // ns
#endif // X360_CONTROLLER_HPP
X360Controller.cpp
#include "X360Controller.hpp"
#ifdef __MINGW32__
// Useless MS defines (needed to compile under MinGW)
#define __in
#define __out
#define __reserved
#endif
// This define makes your program compile faster by excluding things we are not using
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <XInput.h>
namespace xb {
// Returns true if the joystick is connected (and is an XBox 360 Controller)
bool Joystick::isConnected (t_joyNum joyNum)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
auto result = XInputGetState (joyNum, &state);
return (result == ERROR_SUCCESS);
}
// Convenience function: Returns true if there is at least one X360 controller connected
bool Joystick::isAnyXBox360ControllerConnected()
{
return (isConnected(0) || isConnected(1) || isConnected(2) || isConnected(3));
}
/*
// Returns true if the device supports audio capabilities (headset)
// Uncomment if you need this function
bool Joystick::voiceSupported (t_joyNum joyNum)
{
XINPUT_CAPABILITIES caps;
ZeroMemory (&caps, sizeof (XINPUT_CAPABILITIES));
auto result = XInputGetCapabilities (joyNum, XINPUT_FLAG_GAMEPAD, &caps);
if (result != ERROR_SUCCESS)
return false;
return (caps.Flags & XINPUT_CAPS_VOICE_SUPPORTED);
}
*/
// Returns true if the specified button is pressed
// Note that the triggers are NOT recognized as buttons.. You must use
// the getTriggers function for reading the triggers state
bool Joystick::isButtonPressed (t_joyNum joyNum, t_buttonNum buttonNum)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
XInputGetState (joyNum, &state);
return (state.Gamepad.wButtons & buttonNum);
}
// This function returns nothing
// It fills the variables left and right with the current state of the triggers (LT and RT)
// The values will always be in the range 0..1
// TODO: TAKE CARE OF THE DEAD ZONE ??????????????????????????????????
void Joystick::getTriggers (t_joyNum joyNum, float &left, float &right)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
XInputGetState (joyNum, &state);
// Normalize and take care of the Dead Zone
left = static_cast <float> (state.Gamepad.bLeftTrigger) / 255;
right = static_cast <float> (state.Gamepad.bRightTrigger) / 255;
}
// This function returns nothing
// It fills the vectors left and right with the stick positions,
// wich are in the range -100..100, similar to the SFML function
// getAxisPosition
void Joystick::getSticksPosition (t_joyNum joyNum, sf::Vector2f &left, sf::Vector2f &right)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
XInputGetState (joyNum, &state);
// Check for the "DEAD ZONE"
// Left Stick
if ( (state.Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) &&
(state.Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) ) {
state.Gamepad.sThumbLX = 0;
state.Gamepad.sThumbLY = 0;
}
// Right Stick
if ( (state.Gamepad.sThumbRX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbRX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) &&
(state.Gamepad.sThumbRY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbRY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) ) {
state.Gamepad.sThumbRX = 0;
state.Gamepad.sThumbRY = 0;
}
// Convert values to SFML style (-100..100)
left.x = static_cast <float> (state.Gamepad.sThumbLX / 327);
left.y = static_cast <float> (state.Gamepad.sThumbLY / 327);
right.x = static_cast <float> (state.Gamepad.sThumbRX / 327);
right.y = static_cast <float> (state.Gamepad.sThumbRY / 327);
}
// Set vibration (0.0 to 1.0)
// 0 stops the vibration
void Joystick::setVibration (t_joyNum joyNum, float leftMotor, float rightMotor)
{
XINPUT_VIBRATION vib;
ZeroMemory (&vib, sizeof (XINPUT_VIBRATION));
vib.wLeftMotorSpeed = static_cast <WORD> (leftMotor * 65535.0f);
vib.wRightMotorSpeed = static_cast <WORD> (rightMotor * 65535.0f);
XInputSetState (joyNum, &vib);
}
} // ns
Test (main.cpp)
#include <iostream>
using std::cout;
using std::endl;
#include <string>
#include <sstream>
#include <SFML/Graphics.hpp>
// CodeBlocks:
// PROYECT BUILD OPTIONS / SEARCH DIRECTORIES, add
// (DirectX SDK INCLUDE/LIB FOLDERS)
// Linker settings, link libraries, add "XInput" without ".lib"
#include "X360Controller.hpp"
template <class T>
std::string numberToString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
int main ()
{
// If not X360 controllers found, exit
if (!xb::Joystick::isAnyXBox360ControllerConnected()) {
cout << "Error: XBox 360 controllers not detected!" << endl;
return 1;
}
// Create the main window
sf::RenderWindow window (sf::VideoMode (800, 600), "XBox 360 Controller for SFML");
// Load font
sf::Font myFont;
if (!myFont.loadFromFile("steelfish_rg.ttf")) {
cout << "Error loading font file!" << endl;
return 1;
}
sf::Text buttonText ("Press any button", myFont);
sf::Text triggerText ("", myFont);
sf::Text stickText ("", myFont);
triggerText.setPosition(0.0f, 50.0f);
stickText.setPosition(0.0f, 150.0f);
// Start the game loop
while (window.isOpen()) {
// Process events
sf::Event event;
while (window.pollEvent(event)) {
// Close window : exit
if (event.type == sf::Event::Closed)
window.close();
}
// Check for buttons
if (xb::Joystick::isButtonPressed (0, xb::Joystick::A))
buttonText.setString ("A");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::B))
buttonText.setString ("B");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::X))
buttonText.setString ("X");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::Y))
buttonText.setString ("Y");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::LB))
buttonText.setString ("LB");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::RB))
buttonText.setString ("RB");
// Check for the triggers (LT and RT)
float lt, rt;
xb::Joystick::getTriggers(0, lt, rt);
triggerText.setString("Left trigger: " + numberToString(lt) + "\nRight trigger: " + numberToString(rt));
// Triggers controls the vibration
xb::Joystick::setVibration(0, lt, rt);
// Get sticks positions
sf::Vector2f ls, rs;
xb::Joystick::getSticksPosition(0, ls, rs);
stickText.setString("Left stick: " + numberToString(ls.x) + "," + numberToString(ls.y) +
"\nRight stick: " + numberToString(rs.x) + "," + numberToString(rs.y));
// Clear screen
window.clear();
// Dibujar
window.draw(buttonText);
window.draw(triggerText);
window.draw(stickText);
// Update the window
window.display();
}
// Stop vibration
xb::Joystick::setVibration(0);
return 0;
}