I’d like to request that the UI Input Module be given an option to disable one of it’s features: clicking an area of the screen with no selectable controls, the current selected game object is deselected.
In my game, I expect the user to swap between mouse, keyboard and gamepad in the menus. If they deselect a button by clicking the background, all controls are deselected and navigation is either broken or not intuitive when they press the gamepad dpad or a cursor key. It presents a more professional feel if the current selected button stays selected and navigation continues to work seamlessly.
For years I’ve been disabling this feature by modifying the source code of the Standalone Input Module to create my own version. Now the new input system is reaching maturity and I’ve started to use the UI Input Module component, I’m having to modify that too.
It’s a simple change. In InputSystemUIInputModule.cs, I comment out these two lines:
// If we have clicked something new, deselect the old thing
// and leave 'selection handling' up to the press event.
//if (selectHandlerGO != eventSystem.currentSelectedGameObject)
// eventSystem.SetSelectedGameObject(null, eventData);
If a checkbox were added to the inspector, the if statement would simply need to check that property as well as the existing condition.
I hope you’ll seriously consider this request. Thanks for listening.
Just chiming in for visibility that I was curious about this too. Our (PC) game has local multiplayer via either MKB or Gamepad, so there’s no real reason to favor one input method over the other, so would definitely appreciate having something like this.
Agreed, I’ve the exact same setup, and if you unselect accidently by clicking somewhere, there is no way to get something selected with keyboard or gamepad (except doing hacky stuff via code).
Agreed. My current VR project uses local multiplay and different players using twin joysticks, mouse and keyboard in combination and we’ve encountered this issue.
One thing that struck me looking into this is that it seems wrong that navigation can be broken by the selection getting dropped. If there’s no current selection, it seems that navigation should simply resume from the last object that was selected. Unfortunately, doesn’t seem like there’s a simple way to make that happen in the current uGUI event system setup.
Thanks for the update @Rene-Damm , very much appreciated.
You’re right about being able to resume, but I’m not sure which object you’re suggesting should be selected. To clarify, I think it should re-select the last object that was selected, and IGNORE the navigation. E.g. if the user presses right, the object that was last selected becomes selected, not the one to the right of it.
Hopefully you can find a suitable solution to the event system, but that’s already good progress, thank you.
Thanks for keeping us in the loop! Yeah, it does seem wonky that it’s possible to end any navigation with deselecting. I can’t really think of a use case for that. I’d imagine if you’re using mouse for controls, navigation wouldn’t be enabled, and if you were using mouse/gamepad, navigation would be enabled, and you’d rather it not get broken.
This feature would be great for us. Before Unity 2019 we implemented our own InputSystem to create a virtual reality keyboard using UI and Buttons. We could disable deselecting when a keyboard button was pressed, so we could type in the input field by emulating system keyboard events. This is currently not possible: the XRUIInputModule does not provide virtual methods to override default behaviour (like StandaloneInputModule.ProcessMousePress) so whenever we press a VR keyboard button the input field loses focus.
Having encountered this issue myself, I explored the EventSystem.cs file using Unity 2019.3.12f1 and found in the method SetSelectedGameObject on line 150 (could be a different line number depending on the Unity version) that if I put this line of code at the very beginning of the method, it made it so you could never set the Selected gameObject to null:
if (selected == null) return;
It worked for me at this point in my project and I will post a follow-up if I run into any issues with it. Am I missing a very glaring issue this will cause outside of UI work?
Thank-you for implementing this. However, it feels as if the deselection is too aggressive, even with DeselectOnBackgroundClick off.
One example would be Selectables that have their Navigation Mode set to None (i.e. Selectables that cannot actually become selected, and are used rather to implement a Transition or for other reasons). For applications that can be controlled using both a mouse and a gamepad, clicking the mouse cursor over such a Selectable will still trigger ProcessPointerButton’s deselection of the currently selected GameObject, even though its expected ‘replacement’ does not seek to be selected, resulting in gamepad navigation breaking until something else is selected by other means.
It is possible to workaround this, albeit not very cleanly, by locally altering the ProcessPointerButton condition to something like this:-
// If we have clicked something new, deselect the old thing and leave 'selection handling' up
// to the press event (except if there's none and we're told to not deselect in that case).
if (selectHandler != eventSystem.currentSelectedGameObject && ( (selectHandler != null && ( null != selectHandler.GetComponent<UnityEngine.UI.Selectable>() ) && ( UnityEngine.UI.Navigation.Mode.None != selectHandler.GetComponent<UnityEngine.UI.Selectable>().navigation.mode ) ) || m_DeselectOnBackgroundClick))
eventSystem.SetSelectedGameObject(null, eventData);
But is there a better way of avoiding this situation?
I’m sure there are some projects in which this would work, but in other cases you might actually want to be able to enter a state in which nothing is selected, even if only in rare circumstances.
I was wondering if this is coming for UIToolkit event system? I am having the same issues. I need, no matter what, to never deselect the focused VisualElement. It looks really bad when I touch the screen then try to use the gamepad I have to use two inputs before it moves correctly.