MonoGame / MonoGame

One framework for creating powerful cross-platform games.
http://www.monogame.net
Other
11.23k stars 2.89k forks source link

GamePad.GetState().IsConnected is always false on Game.Initialize even if controller is connected #6012

Closed vchelaru closed 2 months ago

vchelaru commented 6 years ago

Summary

The following code returns false on MonoGame UWP when a controller is connected in Game.Initialize, but returns true in Microsoft XNA:

Microsoft.Xna.Framework.Input.GamePad.GetState(PlayerIndex.One, GamePadDeadZone.Circular).IsConnected

Code Example

To reproduce:

  1. Connect an Xbox One wired controller (this will allow the same code and hardware to be used on both a MonoGame UWP project as well as Microsoft XNA project)
  2. Replace the Game1 contents with the code that follows
  3. Run the code in Microsoft XNA and notice that an exception is not thrown.
  4. Run the code in MonoGame UWP and notice that an exception is thrown.
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;

    public Game1() : base()
    {
        graphics = new GraphicsDeviceManager(this);
    }

    protected override void Initialize()
    {
        var gamepadState = Microsoft.Xna.Framework.Input.GamePad.GetState(PlayerIndex.One, GamePadDeadZone.Circular);

        if (gamepadState.IsConnected == false)
        {
            throw new Exception("Not connected!");
        }

        base.Initialize();
    }
}

image

Note that some functions (such as Draw) have been removed for brevity. They are not necessary to reproduce the bug.

Additional Information and Workarounds

The IsConnected property will accurately reflect the connected state of a game pad in a game's Update method (after Initialize has finished). The issue is isolated specifically to the Initialize method. Changing the code to call base.Initialize() before getting the GamePad state does not fix the bug.

Why does this matter?

Tutorials and simple games may query the connected state of the GamePad to identify whether to use gamepad controls or keyboard controls. This behavior may lead programmers to believe that controller support is broken in UWP, when it in fact is not.

tomspilman commented 6 years ago

UWP uses the Windows.Gaming.Input API for gamepads:

https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Input/GamePad.UWP.cs

I don't see any special initialization in this code which suggests to me that WGI internally depends on some other system we enable/trigger/initialize during base.Initialize().

I suggest stepping into base.Initialize() and put Windows.Gaming.Input.Gamepad.Gamepads.Count in the watch window. Then as you step you should see the count go up from zero once the code that "enables" the gamepads is triggered.

tomspilman commented 6 years ago

@cra0zy - You did the UWP input code here originally. Have any ideas?

vchelaru commented 6 years ago

I don't see any special initialization in this code which suggests to me that WGI internally depends on some other system we enable/trigger/initialize during base.Initialize(). I suggest stepping into base.Initialize() ...

Apologies if this wasn't clearly explained above, even if I call base.Initialize() before looking at at the connected, the value is still false for the entirety of the Initialize call.

So I'd guess the critical code isn't in base.Initialize(), but rather somewhere else before Update().

tomspilman commented 6 years ago

but rather somewhere else before Update().

Oh... i mis-read that. Sorry.

Well then it is deeper in. Still stepping thru the bowels of MG code with Windows.Gaming.Input.Gamepad.Gamepads.Count in the Watch Window should let you detect the specific call where it starts working.

harry-cpp commented 6 years ago

Long story short: https://docs.microsoft.com/en-us/uwp/api/windows.gaming.input.gamepad

Remarks This list is initally empty and will not list gamepads even if they are already connected. After a short period this will return a complete list of gamepads.

tomspilman commented 6 years ago

After a short period this will return a complete list of gamepads.

Uggh... yuck!

Hack... if we put a 5 second delay at launch does it hack around this issue?

harry-cpp commented 6 years ago

Hack... if we put a 5 second delay at launch does it hack around this issue?

Do we really want a 5 second delay due to this?

tomspilman commented 6 years ago

Do we really want a 5 second delay due to this?

Not really... just wondering if just waiting a bit solves it.

If so i could see the UWP code look like this:

RETRY:
if (Gamepad.Gamepads.Count == 0)
{
    if (FirstCall)
    {
       FirstCall = false;
       Sleep(1000);
       goto RETRY;
    }
    return GetDefaultState();
}

or something like that.

vchelaru commented 6 years ago

I don't know if it's on a separate thread, but I did put a breakpoint in the Init method and let it sit, then proceeded - it still returned false.

tomspilman commented 6 years ago

I did put a breakpoint in the Init method and let it sit, then proceeded - it still returned false.

Well a breakpoint stops all threads in the application. So i wouldn't expect that to do it. If you put a Sleep in there it might work.

Or it might just depend on some messages pumping in UWP and we can do nothing about it.

amerkoleci commented 6 years ago

Take look at: https://github.com/Microsoft/DirectXTK/blob/master/Src/GamePad.cpp maybe we need to attach to added/removed or rework the gamepad logic

tomspilman commented 6 years ago

Good idea to check DirectXTK @amerkoleci !

@cra0zy @vchelaru ?

harry-cpp commented 6 years ago

Possible, someone would need to test it out ofc.

vchelaru commented 2 months ago

I'm closing out this issue since I don't think anyone cares about UWP anymore. Per @AristurtleDev 's request I'm tagging @SimonDarksideJ in case this really should be left open.