Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: XBox 360 Controller support for SFML games  (Read 23315 times)

0 Members and 1 Guest are viewing this topic.

mateandmetal

  • Full Member
  • ***
  • Posts: 171
  • The bird is the word
    • View Profile
    • my blog
XBox 360 Controller support for SFML games
« on: July 09, 2013, 12:08:44 am »
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  8)

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;

}
 
- Mate (beverage) addict
- Heavy metal addict _lml
- SFML 2 addict
- My first (and free) game: BichingISH!

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: XBox 360 Controller support for SFML games
« Reply #1 on: July 31, 2013, 08:44:16 am »
I have never had any problems with SFML's joystick API and joystick events in combination with (wireless) XBox 360 controllers, what advantages does your API provide over it?
JSFML - The Java binding to SFML.

Jebbs

  • Sr. Member
  • ****
  • Posts: 358
  • DSFML Developer
    • View Profile
    • Email
Re: XBox 360 Controller support for SFML games
« Reply #2 on: August 01, 2013, 10:03:25 am »
I think there was a thread that was active around the same time as this was posted that mentioned how on Windows the triggers for the 360 controller don't register properly, or something like that. Using XInput fixes that issue if I remember correctly.
DSFML - SFML for the D Programming Language.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: XBox 360 Controller support for SFML games
« Reply #3 on: August 04, 2013, 09:24:10 pm »
The triggers worked fine for me when I tested a wired 360 controller with my program.  Dunno if that counts.

afaik the only advantage you'd get by having this much code for a specific kind of 'joystick' would be allowing the program to say "Fire Primary Weapon is bound to Right Trigger" instead of the rather cryptic "Fire Primary Weapon is bound to negative movement along joystick axis 2".

Foaly

  • Sr. Member
  • ****
  • Posts: 453
    • View Profile
Re: XBox 360 Controller support for SFML games
« Reply #4 on: August 05, 2013, 08:10:43 am »
Also this code allows you to give force feedback (vibrate), which is not possible in current SFML.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: XBox 360 Controller support for SFML games
« Reply #5 on: August 05, 2013, 11:34:37 am »
Wow, how did I miss that.

Too bad that part's Windows-only. =(

mateandmetal

  • Full Member
  • ***
  • Posts: 171
  • The bird is the word
    • View Profile
    • my blog
Re: XBox 360 Controller support for SFML games
« Reply #6 on: August 08, 2013, 12:48:06 am »
Thanks for the comments!
I'm planning to implement joystick force feedback support for Linux too  8)
- Mate (beverage) addict
- Heavy metal addict _lml
- SFML 2 addict
- My first (and free) game: BichingISH!