We established a goal to make UI navigation with gamepad controllers intuitive and user-friendly within every Roblox experience. In the past couple of months, we have released several updates to the UI highlight movement logic to support this goal.
More info on gamepad UI navigation
We also wanted to give developers more control and options to customize the UI selection behavior, to suit your needs. With the latest release, there is a new set of gamepad API’s to provide additional control and an understanding of where the gamepad selection will move.
With the new API’s, you will be able to:
- Programmatically switch to UI selection mode
- Highlight a specific button on selection start
- Set up selection groups from the properties tab and further customize their behavior
- Listen to additional events when the selection changes
GuiService:Select(instance) is a method that can be called on any instance that is PlayerGui or a child of PlayerGui. This method will look through all of the descendants of the instance (not including the instance itself) and set GuiService.SelectedObject to the guiobject it finds with the lowest SelectionOrder property (described below). In the case of ties, the guiobject located in the topmost position and then the leftmost position on the screen will be selected.
Toggle into UI selection mode, but don’t specify a default button to highlight:
Create a popup window with dynamic elements and set the UI selection focus to that window when it is first displayed, using the selection order to highlight a button.
popupWindow.Visible = True GuiService:Select(popupWindow)
NOTE: if you want to select the same button every time, you can also use the previously available API: GuiService.SelectedObject = MyButton
enum SelectionBehavior GuiBase2d.SelectionBehaviorUp/Down/Left/Right = enum.SelectionBehavior.Stop/Escape
SelectionGroups let a developer constrain where the UI highlight can move, when there are multiple UI elements on the screen. You can now create selection groups right from the properties window and customize the behavior based on direction.
We have added a new behavior called Escape. If gamepad selection moves to a child of a guibase2d with SelectionGroup = true, selection movement will change depending on the (SelectionBehaviorUp/Down/Left/Right) under that selection group. SelectionBehavior.Escape is the default behavior. When escape is set, gamepad selection will first consider any guiobjects that are valid, in the right direction and a descendent of the SelectionGroup object. If it cannot find anything, it will then allow selection to “Escape” the group and search for guiobjects outside of the SelectionGroup.
This ensures that:
- The UI highlight movement prefers staying within the selection group
- The UI highlight can move outside of the group if there are no more valid options in that direction
SelectionBehavior.Stop will stop selection if nothing is found and will not leave the SelectionGroup through that direction.
The following is an example of a popup window displayed with additional UI elements already on the screen. In the image above, if a player has Button A selected and presses the thumbstick to move up, the standard algorithm would select the diamond button above. However, suppose the popup window has SelectionGroup = true. In that case, it will prefer Button X. Furthermore, if Button X is selected and
SelectionBehaviorUp is set to Escape, the up direction would still allow the diamond to be selected when moving up from Button X.
A behavior more commonly found with dialogs, is that they constrain the selection to their own buttons, which can be achieved by setting SelectionBehaviorUp/Right/Down/Left to Stop. This ensures that the user can only select a button within the dialog before continuing further.
When entering UI selection mode with a view button press or by using the GuiService:Select() method (described above), GuiObjects with lower SelectionOrder will be prioritized first. The default value is 0.
You might want to set an inventory button to be the first selection when a player toggles into UI selection mode. If all of the other GuiObjects on screen still have default values of zero, the inventory button can be set to the default starting button by changing the SelectionOrder to be < 0 within the properties panel.
If a player moves the gamepad selection into a group of guiobjects, you can now more easily make group-wide changes based on selection changes.
SelectionChanged is an event that can be attached to a GuiBase2d. This will fire whenever the selection changes within any descendants of the GuiBase2d to which they are attached to. When the selection highlight moves to a GuiObject, the events will bubble from that guiobject to all of its ancestors, informing them that the selection has changed/entered/exited to a guiobject in their descendant tree. These events will pass an amISelected boolean, PreviousSelection and NewSelection as input parameters.
Use this event to change the background color of a window if a button inside is selected.
backgroundWindow.SelectionChanged:Connect(function(amI, old, new) if new:IsDescendantOf(backgroundWindow) then backgroundWindow.BackgroundColor3 = Color3.new(0, 1, 0) -- green when focused else backgroundWindow.BackgroundColor3 = Color3.new(1, 0, 0) -- red when not focused end end)
Another example - strengthen resilience of dynamic GUIs. If a developer has implemented a player list and one of the player’s thumbnail is the current selection. If the selected player leaves the game and the list updates, the SelectedObject gets destroyed. Using the SelectionChanged event on the playerlist itself allows a developer to recapture focus.
playerlist.SelectionChanged:Connect(function(amI, old, new) if new == nil then GuiService.Select(playerlist) end end)
As always, we would love to hear any feedback you may have on the new APIs and gamepad UI selection in general, including any issues you run into or missing capabilities.