Search Unity

How can I know that any button on gamepad is pressed?

Discussion in 'Input System' started by Dureo, Oct 8, 2019.

  1. Dureo

    Dureo

    Joined:
    Feb 16, 2016
    Posts:
    3
    To know if any keyboard key is pressed we can use
    Keyboard.current.anyKey.isPressed
    but there is no function like this in Gamepad.current.

    How can I know that any button is pressed on the current gamepad?


    PS.
    I tried using
    Gamepad.current.IsActuated(0.2f)
    but is always TRUE (with any threshold value)
     
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    ATM there's no equivalent to the keyboard's "anyKey" for gamepads. Makes me wonder whether we should actually adapt AnyKeyControl to be AnyButtonControl and add that to more devices. Could be useful.

    Anyway. In code, one alternative way is to do

    Code (CSharp):
    1. var gamepadButtonPressed = Gamepad.current.allControls.Any(x => x is ButtonControl button && x.isPressed && !x.synthetic);
    Unrolling it into a for loop will avoid GC heap garbage.

    It should only be true as long as there is some state on the gamepad that isn't at default value. In practice, that will, however, almost always be the case (sticks are rarely at exact zero, for example). Overall, there's currently no meaningful definition of a whole device being actuated. So it uses the default definition which is just that it's state is not at default.
     
    DDmeow and Dureo like this.
  3. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    @Rene-Damm Indeed, an "anyKey" on keyboard/gamepad/whatever would be really usefull ! Atm I can't use the autoswitch of PlayerInput (it just bugs or idk, related to VR always sending input probably), so being able to do the "autoswitch" myself with a anyKey binding would be super usefull !
     
  4. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    While not super efficient, there are ways in which you can bind to all buttons or all controls on a device even without a dedicated control for that.

    To do that, you need to switch the "Path" property to text mode using the little T button on the right. With that, you have access to the path language directly which allows for writing more expressions than the control picker can currently generate.

    Some examples.

    Code (CSharp):
    1. // Bind to all toplevel buttons on the left-hand XR controller.
    2. <XRController>{LeftHand}/<Button>
    3.  
    4. // Bind to all toplevel controls on the right-hand XR controller.
    5. <XRController>{RightHand}/*
    Would like to know more about the problems here. Sorry if I missed something you already posted somewhere else. Still catching up.
     
    Westland likes this.
  5. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    No problem, so basically what I'm doing :

    I'm using version 0.9.6 of the input system (because nothing works if I go to 1.0 due to the gamepad/xr parts that got removed).

    I'm using PlayerInput, but stuck with using "Send message" as Invoke Unity Event is still broken (someone from Unity said it should be fixed in 2019.2.8f but it's still broken for me).

    I'm able to toggle between VR/Non-VR (gamepad or mouse/keyboard) with the "isTracked" action + XRDevie.userPresence, so I automatically switch by putting on the VR headset. For that, I added the headset as optionnal device to all Non-VR control scheme

    I'm listening to InputUser.onChange to modify everything I need between VR/Non-VR.

    I'm currently facing 2 problems :

    1. if I toggle the auto-switch on PlayerInput, VR control scheme gets set everyframe even without the headset / controllers moving or doing anything, my guess is that it's always sending inputs no matter what, so PlayerInput is switching to this ?

    2. to be able to switch BACK to VR, I need to do this
    Code (CSharp):
    1. _playerInput.defaultControlScheme = "VR";
    2. _playerInput.enabled = false;
    3. _playerInput.enabled = true;
    if I don't, both controllers are not tracked anymore. Buttons/Joystick works fine, but no position/rotation tracking.


    I can give you the full code of my Player if needed.



    Edit : Using <Gamepad>/* and <Keyboard>/* works fine ! I'm able to switch "automatically" now :)
     
    Last edited: Oct 9, 2019
  6. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Thank you for the details.

    Sorry about that. I know that the fix has made it out in 2019.3b4 but I'm not sure about the state of the backport to 2019.2. All I know is it did happen so should make it out eventually... :/

    A likely cause of that is controls missing a noise flag. The system relies on controls that give noisy input on being marked explicitly. If a noisy control (such as orientation) is missing that flag, the system will think there's valid user input on the control. Let me go and talk to the XR guys about that.

    Could you file a ticket through the Unity bug reporter and attach the project? What you describe definitely sounds buggy and we should have closer look.
     
  7. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    No problem, just a bit annoying with the SendMessage (and that I will need to rewrite every action :p)

    Don't know if it's that you're referring to, but I tried checking the "Filter noise on current" in the project settings / input system package, but it did nothing. (btw, UI is weird here, can't read all the text).


    I'm doing it and will post the case number here, thanks !

    EDIT : Case #1190144
     
  8. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Every control has a noise flag (in the API, that determines the value of InputControl.noisy). This flag has to be set by the "layout", i.e. the data for a specific device. In this case, the XR layout builder is responsible for setting these flags.

    The code that detects control scheme switches will ignore any input that happens to come from a control that's marked as noisy. This will prevent, for example, a control scheme switch to trigger from the gyro on a PS4 gamepad.

    If a layout isn't setting these flags correctly, the control scheme switching code will mistake noise for real input and initiate spurious control scheme switches. That'd be a bug in how a specific device is wired up for the new input system.
     
  9. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    Oh ok, so I cannot modify this on my end ?

    That's weird nobody encountered that before, but I guess there is not a lot of people doing VR/Non-VR project anyway.

    Posted the case number in the previous post. Thanks again
     
  10. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    It is possible to fix up / modify layouts using "layout overrides". Which can be used to retroactively set the noisy flag.

    Code (CSharp):
    1. InputSystem.RegisterLayoutOverride(@"
    2.    {
    3.        ""name"" : ""MyOverrides"",
    4.        ""extend"" : ""NameOfLayoutYouWantToModify"",
    5.        ""controls"" : [
    6.            { ""name"" : ""nameOrPathOfControlToModify"", ""noisy"" : true }
    7.        ]
    8.    }
    9. ");
    (NB: There were some bugs in the layout override code which are fixed on latest develop and will be in the next package; but yeah, means you'd potentially run into some issues on 0.9.6)

    I'll make sure we address it on our side (if this is indeed the problem; could also be something else).
     
  11. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    Yeah, I'll stick with my solution for the moment then, it's working fine.
     
  12. NeatWolf

    NeatWolf

    Joined:
    Sep 27, 2013
    Posts:
    924
    Is there also a way to get "any axis on the gamepad that are over 0.2 in intensity"?

    Or even "any gamepad axis"?
     
  13. Khaeops

    Khaeops

    Joined:
    Dec 28, 2014
    Posts:
    31
    An 'Any Button' or 'Any Axis' would be super helpful, as right now it seems impossible to recreate something like this for gamepad or mouse input. The code snippet alternative provided early on in this post no longer works.
     
  14. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Indeed. It's in the works and hopefully will make it for 1.1 final. A partially complete implementation adds a API like this

    Code (CSharp):
    1. InputSystem.onAnyButtonPressCallOnce +=
    2.     button =>
    3.     {
    4.         Debug.Log($"Button {button} was pressed");
    5.     };
    ATM the quickest way to make it happen is to either rely on
    InputUser
    for this (which, however, only works for devices not paired to any player):

    Code (CSharp):
    1. ++InputUser.listenForUnpairedDeviceActivity;
    2. InputUser.onUnpairedDeviceUsed +=
    3.     (control, eventPtr) => { ... };
    or to hook into
    InputSystem.onEvent
    (which is relatively quick but not efficient):

    Code (CSharp):
    1. InputSystem.onEvent +=
    2.     (eventPtr, device) =>
    3.     {
    4.         if (!eventPtr.IsA<StateEvent>() && !eventPtr.IsA<DeltaStateEvent>())
    5.             return;
    6.  
    7.         var controls = device.allControls;
    8.         foreach (var control in controls)
    9.         {
    10.             if (!(control is ButtonControl button))
    11.                 continue;
    12.             if (button.ReadValueFromEvent(eventPtr) > InputSettings.defaultButtonPressPoint)
    13.             {
    14.                 Debug.Log($"Button {button} pressed");
    15.                 break;
    16.             }
    17.         }
    18.     };
    An API to do these kind of event queries a lot more efficiently is in the works (both
    RebindingOperation
    and
    InputUser
    currently have performance problems related to this).
     
    NeatWolf likes this.
  15. laessnb

    laessnb

    Joined:
    Jun 10, 2014
    Posts:
    101
    Hi, will this apply to the mouse as well? I have a tutorial dialog that I want to go away on any input other than things like analog stick deflection or mouse movement.
     
  16. NeatWolf

    NeatWolf

    Joined:
    Sep 27, 2013
    Posts:
    924
    Thanks Rene!

    I wonder if there's any new way to do it in the latest publicly preview version of the package, as per today (guess it's 1.1.0.preview.2?)

    That's quite some boilerplate to get "AnyKey for Any Device".

    Which would be used on most splash screens.
     
  17. ArtyBoomshaka

    ArtyBoomshaka

    Joined:
    Mar 5, 2013
    Posts:
    226
    Sorry for the somewhat necrobump.

    Gamepad.allControls doesn't seem to be documented anywhere in the gamepad scripting API reference?
    I may have missed it but I couldn't find it in any version of the documentation.
    Additionally, the 1.0.2 input system doesn't contain a definition for Gamepad.current.allControls.Any

    Anyone know how to effectively check for any gamepad input as of Input System v1.0.2?
    Searching the forums failed me this time...

    Edit : This very verbose code snippet seems to work. For any practical use, a more elegant solution like Keyboard.current.anyKey would be more convenient though.

    Code (CSharp):
    1.         for (int i = 0; i < Gamepad.current.allControls.Count; i++)
    2.         {
    3.             var c = Gamepad.current.allControls[i];
    4.             if (c is ButtonControl)
    5.             {
    6.                 if (((ButtonControl)c).wasPressedThisFrame)
    7.                 {
    8.                     Debug.Log("Using Pad");
    9.                 }
    10.             }
    11.         }
     
    Last edited: Mar 29, 2021
  18. NeatWolf

    NeatWolf

    Joined:
    Sep 27, 2013
    Posts:
    924
    Definitely not a necrobump - I'm still on the development and now basically still facing the same issue, but pertaining racing Wheel devices.

    Which - are seen as Joysticks for some reason.

    I wish there was a way to just "get the break pedal value" instead of having to buy each and every wheel on the market to make sure it's mapped to rx, z, y, or whatever else axis (also, why are they unpressed at 1, half pressed at 0, and fully pressed at -1? A pedal?).

    I read wheel support has been cut away from the IS and added back to some other major change but - I'd need it working now, and on PC/XBOX S|X, PSx, etc. :p

    Also, a non-reactive wheel is a wheel which -could- be left as turned 90 degrees - which makes it a bit harder to determine whether it's actually being used now or just being plugged in and forgot :p
     
  19. theghostronaut

    theghostronaut

    Joined:
    Jul 27, 2018
    Posts:
    41
    Any update on this @Unity?

    Code (CSharp):
    1. Gamepad.current.allControls.Any
    "Any" does not seem to be implemented in the curent version of the Input System.

    As for this snippet, if anyone (like me) couldn't get this to work at first, it requires the "InputSystem.Controls" namespace be added.

     
    Last edited: Feb 7, 2022
  20. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Code (CSharp):
    1. InputSystem.onAnyButtonPress.Call(
    2.     ctrl =>
    3.     {
    4.         if (ctrl.device is Gamepad gamepad)
    5.             Debug.Log($"Button {ctrl} on {device} was pressed");
    6.     });
     
    saskenergy likes this.
  21. theghostronaut

    theghostronaut

    Joined:
    Jul 27, 2018
    Posts:
    41
    Still no luck, "onAnyButtonPress" is not recognized. I'm on Unity 2021.1.15f1 (Windows) with Input System 1.0.2.
     
  22. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Was added sometime in >=1.1. Recommend upgrading to 1.3.
     
  23. TomTheMan59

    TomTheMan59

    Joined:
    Mar 8, 2021
    Posts:
    356
    I am using 1.2.0 and I cannot use your posted code for InputSystem.onAnyButtonPress.


    Code (CSharp):
    1.     private void Start()
    2.     {
    3.         InputSystem.onAnyButtonPress.Call(
    4.         ctrl =>
    5.         {
    6.             if (ctrl.device is Gamepad gamepad)
    7.                 Debug.Log($"Button {ctrl} on {ctrl.device} was pressed");
    8.         });
    9.     }
    I get a bunch of errors:

    ArgumentException while executing 'InputSystem.onEvent' callbacks
    UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
    UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr) (at /Users/bokken/buildslave/unity/build/Modules/Input/Private/Input.cs:120)

    ArgumentException: Event must be a StateEvent or DeltaStateEvent but is a IMES instead
    Parameter name: eventPtr
    UnityEngine.InputSystem.InputControlExtensions.EnumerateControls (UnityEngine.InputSystem.LowLevel.InputEventPtr eventPtr, UnityEngine.InputSystem.InputControlExtensions+Enumerate flags, UnityEngine.InputSystem.InputDevice device, System.Single magnitudeThreshold) (at Library/PackageCache/com.unity.inputsystem@1.2.0/InputSystem/Controls/InputControlExtensions.cs:1014)
    UnityEngine.InputSystem.InputControlExtensions.GetFirstButtonPressOrNull (UnityEngine.InputSystem.LowLevel.InputEventPtr eventPtr, System.Single magnitude, System.Boolean buttonControlsOnly) (at Library/PackageCache/com.unity.inputsystem@1.2.0/InputSystem/Controls/InputControlExtensions.cs:1096)
    UnityEngine.InputSystem.InputSystem+<>c.<get_onAnyButtonPress>b__79_0 (UnityEngine.InputSystem.LowLevel.InputEventPtr e) (at Library/PackageCache/com.unity.inputsystem@1.2.0/InputSystem/InputSystem.cs:2404)
    UnityEngine.InputSystem.LowLevel.SelectObservable`2+Select[TSource,TResult].OnNext (TSource evt) (at Library/PackageCache/com.unity.inputsystem@1.2.0/InputSystem/Utilities/Observables/SelectObservable.cs:43)
    UnityEngine.InputSystem.LowLevel.InputEventListener+ObserverState.<.ctor>b__2_0 (UnityEngine.InputSystem.LowLevel.InputEventPtr eventPtr, UnityEngine.InputSystem.InputDevice device) (at Library/PackageCache/com.unity.inputsystem@1.2.0/InputSystem/Events/InputEventListener.cs:127)
    UnityEngine.InputSystem.Utilities.DelegateHelpers.InvokeCallbacksSafe[TValue1,TValue2] (UnityEngine.InputSystem.Utilities.CallbackArray`1[System.Action`2[TValue1,TValue2]]& callbacks, TValue1 argument1, TValue2 argument2, System.String callbackName, System.Object context) (at Library/PackageCache/com.unity.inputsystem@1.2.0/InputSystem/Utilities/DelegateHelpers.cs:71)
    UnityEngine.InputSystem.LowLevel.<>c__DisplayClass7_0:<set_onUpdate>b__0(NativeInputUpdateType, NativeInputEventBuffer*)
    UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate(NativeInputUpdateType, IntPtr) (at /Users/bokken/buildslave/unity/build/Modules/Input/Private/Input.cs:120)

    1. This code is also executing when I quit playmode. Am I doing something wrong? Why is it executing in edit mode when I use this on Start()? I don't have any [ExecuteInEditoMode]
    2. Am I suppose to subscribe to it then unsubscribe? If so, how?


    EDIT: I found this documentation that helps with the code executing in edit mode. However, I still get the errors:

    https://docs.unity3d.com/Packages/c.../api/UnityEngine.InputSystem.InputSystem.html
     
    Last edited: Feb 11, 2022
  24. loconeko73

    loconeko73

    Joined:
    Aug 13, 2020
    Posts:
    17
    I'm all for trying the cool new stuff, and I am enjoying some of the benefits of the new input system, but I cannot get this to work : all I need is a simple "Press a key to continue" once my main game scene is loaded and I can't seem to make it work.

    Could you provide more context in the code ? The example provided by the user above from the docs just fails because IObservable doesn't implement the Call method.

    Can you put me on the right tracks ?
     
  25. loconeko73

    loconeko73

    Joined:
    Aug 13, 2020
    Posts:
    17
    Sorry, the answer is actually properly provided here : https://forum.unity.com/threads/input-system-1-3-0-released-more-fixes.1222632/ (post #15) and it cannot be more clear.

    It's also a good and robust way of implementing the "Wait for any key" through code with events, so you don't clutter your project needlessly. Even though it's recommended to segregate functionality (so input should really be in its own manager), this is the perfect exception to the rule : you just need to register that one key press event, wait for it, then de-register it.

    Clean and efficient.
     
    Abdo-Reda likes this.