New Gamepad UI Selection APIs

Hi Developers,

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

[Method] GuiService:Select(instance)

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.

Use Cases

Toggle into UI selection mode, but don’t specify a default button to highlight:

GuiService:Select(PlayerGui)

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


[Properties] bool GuiBase2d.SelectionGroup

enum SelectionBehavior GuiBase2d.SelectionBehaviorUp/Down/Left/Right = enum.SelectionBehavior.Stop/Escape

image

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.

Use Cases

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.

[Property] int GuiObject.SelectionOrder

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.

Use Cases

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.

[Event] GuiBase2d.SelectionChanged

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 Case

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)

Feedback

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.

153 Likes

This topic was automatically opened after 11 minutes.

As a developer with 3 games on the xbox featured sort, this is amazing! Please keep showing xbox some of the love, would love to see the bugs in #bug-reports:xbox-bugs and features in #feature-requests:xbox-features looked through!

13 Likes

This is great to see from Roblox! We finally got these features and I’ve been waiting for a while.

5 Likes

I believe they intended to say “Are you looking to have a good developer”

2 Likes

What does the amISelected parameter indicate exactly?
From the documentation, I thought it might be a shortcut for new:IsDescendantOf(guiBase2dOfTheEventConnection). However, that is then done manually in the code example. Is that an oversight, and would doing

if amISelected then
  -- green
else
 -- red
end

result in the same behaviour?

If not, for what/when would the amISelected parameter be used?

5 Likes

amISelected tells you when exactly the thing you connected on (and not a descendant) is selected, so if you want to apply a style to an item when specifically it is selected you can use something like this very straightforward pattern:

myGui.SelectionChanged:Connect(function(amISelected)
    myGui.FancyBorder.Visible = amISelected
end)

Basically it simplifies the most common case of styling a non-container object like a button while selected rather than you having to build logic around the new / old selected item to do so.

8 Likes

Lets gooooo!! I recently started working on gamepad compatibility on my UI and I was really annoyed by the fact that there was no way to force select something.

Great feature, love it!

3 Likes

This is so amazing. Saves a lot of heartache and Roblox engine quirks for cross-platform compatibility that I’ve had in the past!

2 Likes

It’d be really helpful if the NextSelection properties would use :Select internally. Reason being is so that it’ll select the members of the selection group automatically, which is especially useful if those members are decided on runtime.

2 Likes

Is this not live or something? For me, GuiService:Select prioritized leftmost top instances regardless of SelectionOrder.

Attached a file for reproduction.
SelectionOrderBug.rbxl (41.9 KB)

1 Like

Thanks for the report! You’re right, I did make a mistake with calling Select on generic instances with SelectionOrder. I’ll have a fix coming out soon, but in the meantime, if you call Select on the PlayerGui, it will respect SelectionOrder.

game:GetService(“GuiService”):Select(game:GetService(“Players”).LocalPlayer.PlayerGui)

I’ll be sure to update you when the fix is out.

2 Likes

Hi there! I looked into the issue and it should be fixed now. Using GuiService:Select should now pick the GuiObject with the lowest SelectionOrder. Please let us know if you experience any further issues!

1 Like