Since the GamePad class doesn’t work unless you have an XBox controller (correct me if I’m wrong), I’ve coded my own generic joystick class, unfortunately only for Windows. If anyone is interested I can post it here.
Just post it? Someone might be able to help you improve it, not something for me right now but like I said, you might be surprised what people do with it, if you are happy sharing it, put it out there and see where it goes.
Code On!
The GamePad API will work for any SDL-compatible controllers on DesktopGL projects, which also run on Windows.
I have 3 different USB controllers: 2 joysticks (THE C64 Joystick; Xin-Mo Dual Arcade) and 1 gamepad (PLAYSTATION®3).
GamePad.GetState(playerIndex).IsConnected
always returns false
for each of them and for any playerIndex
value (I tried 0 to 9).
Probably they are not SDL-compatible.
My joystick class, instead, recognizes all the controllers (it uses the joyGetPosEx
winmm API).
EDIT [I forgot to add, I looked over at my XBOX ONE controllers, Steelseries iPhone Gamepad and Nintendo Switch Gamepad and thought, what the hell!?]
None of them are even Windows compatible as far as I can be bothered doing research past images. and Sony only allowed the PS4 - and beyond - controllers to be compatible with Desktops.
Like I said, just post it
EDIT
Have you checked them in the Windows Gamepad control tool to test button presses too?
I noticed you mentioned USB, so disregard compatibility above, but still, what the hell dude!?
Yes, they perfectly work in the tool.
Ok, here is the class.
using System.Collections.Generic;
using System.Runtime.InteropServices;
public enum JoystickDirection
{
Left,
Right,
Up,
Down
}
public struct JoystickState
{
public bool Left;
public bool Right;
public bool Up;
public bool Down;
public int Buttons;
}
public static class Joystick
{
public static bool IsConnected { get; private set; }
static JoystickState JoystickState;
static JoystickState PreviousJoystickState;
static JOYINFOEX JoyInfo;
public static void Update()
{
const uint JOYERR_NOERROR = 0;
const uint JOY_RETURNALL = 0xFF;
JoyInfo.dwSize = (uint)Marshal.SizeOf(JoyInfo);
JoyInfo.dwFlags = JOY_RETURNALL;
JoystickState state = new JoystickState();
if (joyGetPosEx(0, ref JoyInfo) == JOYERR_NOERROR)
{
if (JoyInfo.dwXpos == 0) state.Left = true;
else if (JoyInfo.dwXpos == 0xFFFF) state.Right = true;
if (JoyInfo.dwYpos == 0) state.Up = true;
else if (JoyInfo.dwYpos == 0xFFFF) state.Down = true;
state.Buttons = (int)JoyInfo.dwButtons;
IsConnected = true;
}
else
{
IsConnected = false;
}
PreviousJoystickState = JoystickState;
JoystickState = state;
}
public static bool IsMoved(JoystickDirection direction)
{
if (direction == JoystickDirection.Left) return JoystickState.Left == true;
if (direction == JoystickDirection.Right) return JoystickState.Right == true;
if (direction == JoystickDirection.Up) return JoystickState.Up == true;
if (direction == JoystickDirection.Down) return JoystickState.Down == true;
return false;
}
public static bool HasJustBeenMoved(JoystickDirection direction)
{
if (direction == JoystickDirection.Left) return JoystickState.Left == true && PreviousJoystickState.Left == false;
if (direction == JoystickDirection.Right) return JoystickState.Right == true && PreviousJoystickState.Right == false;
if (direction == JoystickDirection.Up) return JoystickState.Up == true && PreviousJoystickState.Up == false;
if (direction == JoystickDirection.Down) return JoystickState.Down == true && PreviousJoystickState.Down == false;
return false;
}
public static bool IsPressed(int button)
{
if ((JoystickState.Buttons & (1u << (button - 1))) != 0) return true;
return false;
}
public static bool HasJustBeenPressed(int button)
{
if ((JoystickState.Buttons & (1u << (button - 1))) != 0 && (PreviousJoystickState.Buttons & (1u << (button - 1))) == 0) return true;
return false;
}
public static int[] GetPressedButtons()
{
return GetPressedButtons(JoystickState.Buttons);
}
public static int[] GetJustPressedButtons()
{
List<int> pressedButtons = new List<int>(GetPressedButtons(JoystickState.Buttons));
List<int> previousPressedButtons = new List<int>(GetPressedButtons(PreviousJoystickState.Buttons));
pressedButtons.RemoveAll(button => previousPressedButtons.Contains(button));
return pressedButtons.ToArray();
}
static int[] GetPressedButtons(int buttons)
{
List<int> pressedButtons = new List<int>();
if (buttons != 0)
{
for (int i = 0; i < 32; i++)
{
if ((buttons & (1u << i)) != 0)
{
pressedButtons.Add(i + 1);
}
}
}
return pressedButtons.ToArray();
}
[StructLayout(LayoutKind.Sequential)]
struct JOYINFOEX
{
public uint dwSize;
public uint dwFlags;
public uint dwXpos;
public uint dwYpos;
public uint dwZpos;
public uint dwRpos;
public uint dwUpos;
public uint dwVpos;
public uint dwButtons;
public uint dwButtonNumber;
public uint dwPOV;
public uint dwReserved1;
public uint dwReserved2;
}
[DllImport("winmm.dll")]
static extern uint joyGetPosEx(uint uJoyID, ref JOYINFOEX pji);
}
This class only manages one joystick (the first one it finds) but you can improve it by changing the uJoyID
parameter of the joyGetPosEx
function.
Usage:
Joystick.Update()
// Updates the joystick state.
Joystick.IsPressed(button)
// Returns true
if the specified button is pressed. button
can be a value from 1 to 32.
Joystick.HasJustBeenPressed(button)
// Returns true
if the specified button has been pressed during the last update, otherwise false
until the player releases and presses the button again. Useful if you want the player to fire only once when the button is pressed (autofire OFF).
Joystick.IsMoved(direction)
// Returns true
if the joystick is moved in the specified direction.
Joystick.HasJustBeenMoved(direction)
// Returns true
if the joystick has been moved in the specified direction during the last update.
Joystick.GetPressedButtons()
// Returns an array containing all the pressed buttons.
Joystick.GetJustPressedButtons()
// Returns an array containing all the buttons that has been pressed during the last update.
I have released a generic solution if you want to look at it
For complete beginners, explaining why this is at the aft of the loop may explain a lot also, explain how to handle DLL’s properly to avoid memory leaks.
Not questioning you here, just a nudge to get you to explain things, I often find, we understand things better when we are asked to teach them, this is my methodology.
Good work and thank you for your contribution here!
I think Microsoft’s documentation can explain it much better than I do. You can find it on https://docs.microsoft.com/en-us/previous-versions/ms709354(v%3Dvs.85). It is simply a system function and I don’t think we should worry about memory leaks. The documentation also explains well the JOYINFOEX structure which also contains data such as Z, R, U, and V pos as well as POV.
It’s at the bottom of the Class, not the bottom of a loop. It’s just an import so there won’t be a memory leak.
Typically Imports are put a the top of the file just as a matter of practice.
Anyway, it’s a real shame (and a limit) that Monogame can’t handle all kinds of controllers, isn’t it? Maybe future versions will take this into account?
Luca, Monogame doesn’t need to handle all controllers, it’s not a game engine.
If you want your game based on Monogame to support all controllers, then you can do it.
Just look at the demo I posted, I support almost anything with a USB connection.
Don’t expect Monogame to do everything for you, it’s a damn good start, but you still have to work to make anything with it
If developing for Steam, I would recommend checking out the SteamInput API. That’s how we support a crazy number of devices, many natively with matching in-game input glyphs (and even virtual gamepads on phones via SteamLinkTouch). Whether connected via USB, Bluetooth, or otherwise. SteamInput uses SDL under the hood.
https://muffed.kelsam.net/docs/game/engine/input/devices/#connect-natively-supported-devices
Maybe I got the wrong idea of what Monogame is and what it does or what it should do. As a matter of fact, when I created my first project and saw that the first line of the Update
method said “if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed...
” I thought: “Well, I don’t have to worry about creating code to handle gamepads, since they are already supported.”. You can imagine that I was disappointed when I found out that this is not exactly the case. It’s as if the Keyboard class only handles certain keyboard types.
With that, don’t get me wrong. Monogame is already doing a lot and that’s fine. I’m just wondering if things like this are expected to be added / improved in future releases. I also think of future beginner game makers who at some point will have to choose which game engine to bet on.
I think I’ll stay with my class: it’s simple and does what I need. But out of curiosity, I took a look at the site but I don’t see anything related to gamepad / input.
A gamepad is just a HID device
Instead of having a bespoke gamepad class and a joystick class and a throttle class and a “what the hell is this thing?” class, you can have a generic input device class.
Horses for courses though, if you don’t expect anyone to ever plug in a “ElITe Pro-Mysterion twenty two axis force feedback AI joystick” what is the point of supporting it
I correct myself: I don’t pretend that Monogame supports every type of gamepad, even the most unlikely, but I expect that at least those recognized by Windows (therefore in some standard way) are normally managed. As I said, I have 3 completely different types of gamepads, but Windows recognizes them without problems, which is what my class does. For me, the question always remains: one day, will Monogame be able to manage the gamepads that Windows (and me too) consider standard?
Have you tried XINPUT?