Last used device (X360, XOne, PS4, Dualsense, etc)

Hello!

I have a simple problem and I can’t find any solutions.
I need to display glyphs for input actions depending on the last used device. Kinda classical.
BUT, I need to make the distinction between all kind of controllers. I won’t display the same glyphs if it’s a XBOX360 controller nor if it’s a PS4 controller. For the sake of this thread, let’s say we want to make different glyphs for : PS4/PS5/Stadia Controller/Joycon/Switch Pro/X360/XOne.

The closest thread I found for my issue is this one Detect most recent input device/type? but it’s not enough.

In Rewired, there’s those two super helpful methods:

I can then get the hardware GUID for the device

this id is unique per controller hardware (xbox 360, xbox one, ps3, ps4, ps5, stadia controller, switch joycons, switch pro controller, etc).

Thanks and have a nice day.

Up :slight_smile:

Up

I use the PlayerInput script provided by Unity for this and then subscribe to the OnControlsChanged event.

Note: Dualsense support has not been released yet: Adding DualSense basic support by jimon · Pull Request #1404 · Unity-Technologies/InputSystem · GitHub

    private void OnEnable()
    {
        playerInput.onControlsChanged += OnControlsChanged;
    }
 
    private void OnDisable()
    {
        playerInput.onControlsChanged -= OnControlsChanged;
    }
    private ActiveInputType SetActiveInputType()
    {
        if(playerInput.devices[0] is Mouse || playerInput.devices[0] is Keyboard)
        {
            return ActiveInputType.keyboardAndMouse;
        }
        else if(playerInput.devices[0] is XInputController)
        {
            return ActiveInputType.xbox;
        }
        else if(playerInput.devices[0] is DualShockGamepad)
        {
            return ActiveInputType.playstation;
        }
        else if(playerInput.devices[0] is Touchscreen)
        {
            return ActiveInputType.touch;
        }
        else
        {
            // If you are using a generic controller, it should default to xbox icons.
            return ActiveInputType.xbox;
        }
    }

    private void OnControlsChanged(PlayerInput playerInput)
    {
        previousActiveInputType = activeInputType;
        activeInputType = SetActiveInputType();

        if(previousActiveInputType != activeInputType)
        {
            EventManager.InvokeOnActiveInputChanged(previousActiveInputType, activeInputType);
        }
    }

Hope it helps!

1 Like

Thanks for your answer!

However:

  • I would like to avoid using PlayerInput (but if it’s the only drawback, I think I can live with it)
  • It does not give me the level of precision I need (for instance, making the distinction between a 360 and a One controller). I can’t know if it’s a Stadia controller neither for instance. Relying on the type is not enough, especially when they can change depending on the platform you’re building your game on.

But again, thanks for the help! :slight_smile:

No problem! If you ever find an answer would love to know :slight_smile:

1 Like

Depending on what exactly you want to do, Gamepad.current might suffice, if you want to get all state notifications ever then InputState.onChange will do, just don’t forget to remove the callback after you’re done, otherwise it will be called way too often.

Usually things like “I need to do figure out what is the device of recently joined player” are done via listening to a button press, with gamepads connected over bluetooth it’s often done as “press A to join”.

Then for identification purposes you could try using InputDeviceDescription.serial, but the current implementation is a bit spotty, partially because we rarely do have a serial, but also because we’re not exposing port connection hierarchy right now. In this thread 2 Arduino's Leonardo as joystick inputs, only one inputs it's axis. we covered some basis how it should work, hopefully will come around to make device identification more robust.

Hello. Thanks for your answer.

I want to be able to detect (at all time, not only in a “press A to join” menu) whenever the player change device.
I can’t use only Gamepad.current as the player might be using a Keyboard, or a Touchpad. I want him to be able to change device on the fly, and for the game to adapt the glyphs automatically.

Furthermore, InputDeviceDescription.serial is empty (I use a xbox series controller, wireless, on a windows editor)
7610356--945379--upload_2021-10-28_15-29-9.png

@Bastienre4 as I mentioned we currently don’t have a great platform independent implementation for this, sorry about it, in the future we plan to revisit device identification and make it as uniform as possible.

In this particular case XInput itself doesn’t provide any information about gamepads. Instead it reports user id, up to 4 in total, from 0 to 3. This user id is stored in device description → capabilities → user index, which you can see on your screenshot.
More info Getting Started With XInput in Windows applications - Win32 apps | Microsoft Learn

For USB HID devices you should be able to get the serial in most cases, but not all devices report it.
USB port topology is currently not exposed.

If your case is only to get the latest type of a controller, maybe you can achieve that by listening to all state events, checking device id on them, and then checking with input system what is the type for that device id.

@Bastienre4 reading a bit in our code, we have InputSystem.GetDevice(Type type), try passing InputDevice type there and see if that does what you want.

Because reading the code:

        public static InputDevice GetDevice(Type type)
        {
            InputDevice result = null;
            var lastUpdateTime = -1.0;
            foreach (var device in devices)
            {
                if (!type.IsInstanceOfType(device))
                    continue;

                if (result == null || device.m_LastUpdateTimeInternal > lastUpdateTime)
                {
                    result = device;
                    lastUpdateTime = result.m_LastUpdateTimeInternal;
                }
            }

            return result;
        }

It returns the last updated for the type, so passing the most generic type to it should work.

1 Like

That would be “ok”, waiting for a better device identification (in order to display proper glyphs depending on the precise type of the controller).
It at least would solve one of the issues, getting the last used device.
One question though, does it take into account the “noisy” devices or not (I’ve heard that PS4 controller for instance is noisy due to the gyro sensors & all)?

Any update?

Sorry m_LastUpdateTimeInternal is currently ignoring the noise flag. Device.MakeCurrent() is the only thing currently called when there is any non-noise activity on a device. The way how PlayerInput does it currently is hooking into InputSystem.OnEvent and for every event doing EnumerateChangedControls to see if anything has changed.

I don’t immediately see how would you solve this without writing almost exactly the same code as in InputUser, maybe you can leverage it to do what you need?

Otherwise I would suggest filing a bug report.

1 Like

I wonder if it shouldn’t be added in the InputSystem. It seem kind of an mandatory feature to me, esp. on some platforms like Stadia who requires you to react to controller change. And having to rely on some complicated, heavy code copied from an other component doesn’t seem very robust. I know I’m not the only Unity customer having (a lot of) issues with this subject, maybe there’s something you guys can do on your side to help us?

TBH, I really like the philosophy of the InputSystem, but the struggle to work around those features I need will probably make me go back to Rewired.

@Bastienre4 I understand that this use case is a bit of pain right now, sorry about it.

Would you mind to explain a bit in more detail what exactly you’re trying to do so I can better figure out what feature/fix we need to make? I get that is “show graphics for last used device”, which implies you probably want to switch control schemes based on last used device? We have a somewhat conflicting feature requests here. As in this thread 2 Arduino's Leonardo as joystick inputs, only one inputs it's axis. the solution is not to use last active device, but rather figure out the connection hierarchy.

Thanks!

1 Like

TBH, I just gave up and went back using Rewired.
I encountered a 100% crash while I was trying to develop the rebind menu, it was too much for me :stuck_out_tongue: (Case 1379126)

I don’t think it’s conflicting, it may just be different approaches.
In my case, as it’s a single player game, I just want an easier way to detect the device the player is really using to display the exact glyphs to show (and depending on the actual device, it’s not the same (X360, XOne, XSeries, PS4, PS5, etc).