Creating a Grid Menu

Creating a Grid Menu

15 min

Special care has to be taken when creating a menu system with gamepad controls in mind. With conventional interfaces, the user can simply click or tap where they want to select. For simple and quick menus, a Articles/Creating a Radial Menu|Radial Menu works well with a gamepad, but with more complicated and conventional menus the user has to navigate a menu by moving a selector from element to element. GuiService automatically tries to determine which element a user wants to select with a gamepad, but sometimes extra configuration is needed for the menu to work correctly.

This tutorial uses GuiService and ContextActionService to implement a simple equipment management menu system.


The code in this tutorial creates a framework for an equipment management system. It creates a button on the screen; if the user presses A with that button selected, the game pops up a model menu showing a simple character model and a grid of items.





The first thing the code does is create the menu. The menu in your game can of course be created and stored before hand, but in this case the menu elements are explicitly created in the script.


The important thing to note about the menu is the hierarchy, as this will be used when defining selection groups. All of the buttons for the character slots are children of CharacterFrame, and all of the grid button are children of ScrollingFrame


Opening the menu

After the menu layout has been defined, the code then binds input to open and close the menu. Gui buttons (such as ImageButtons and TextButtons can accept gamepad input. The event GuiButton/MouseButton1Click will fire if a player presses the A button with the button selected.

The code defines two functions: openEquipmentMenu and closeEquipmentMenu. openEquipmentMenu is bound to the equipmentButton which is in the upper left corner of the screen. When that function is called, GuiService/AutoSelectGuiEnabled is disabled and the value stored for later use. When this property is enabled, the game will automatically choose an element when the select button is pressed. That behavior is needed before the menu is opened so the equipment button can be activated, but while the player is in the menu it should be disabled.

GuiObject/TweenPosition is used to animate the menu opening and closing. The last parameter of this function is a custom callback function which is called when the animation is completed. When the opening animation is finished, this function is used to bind the B button to closeEquipmentMenu and to move the selection to the torso button. In closeEquipmentMenu, after the closing animation is played, the B button is unbound, the selection is cleared and the original value of AutoSelectGuiEnabled is restored.

Navigating Character Slots

Roblox has automatic behavior to help a user with a gamepad navigate Gui elements. When the user presses the Select button on their gamepad, the game will create a selection around a visible GUI element that has GuiObject/Selectable enabled. When the user presses the left thumbstick or the dpad, the game will try to find another GUI element in the direction that was pushed and move the selection there. If there is no element in the direction, then the selection will not change.

The grid in the right of the menu works well with this system (as the elements are always in a cardinal direction from one another), but the elements in the character frame will not work as well with the default system. GuiObjects have several properties you can use to specify which element to switch to (e.g. GuiObject/NextSelectionDown. Using these properties, the directions from each character slot can be set like so:


headFrame.NextSelectionDown = torsoFrame
headFrame.NextSelectionLeft = rightArmFrame
headFrame.NextSelectionRight = leftArmFrame

torsoFrame.NextSelectionUp = headFrame
torsoFrame.NextSelectionLeft = rightArmFrame
torsoFrame.NextSelectionRight = leftArmFrame
torsoFrame.NextSelectionDown = rightLegFrame

rightArmFrame.NextSelectionUp = headFrame
rightArmFrame.NextSelectionRight = torsoFrame
rightArmFrame.NextSelectionDown = rightLegFrame

leftArmFrame.NextSelectionUp = headFrame
leftArmFrame.NextSelectionLeft = torsoFrame
leftArmFrame.NextSelectionDown = leftLegFrame

rightLegFrame.NextSelectionUp = torsoFrame
rightLegFrame.NextSelectionRight = leftLegFrame
rightLegFrame.NextSelectionLeft = rightArmFrame

leftLegFrame.NextSelectionUp = torsoFrame
leftLegFrame.NextSelectionRight = leftArmFrame
leftLegFrame.NextSelectionLeft = rightLegFrame

Notice that in the above code there are several edges that are not defined. For example, the code does not define the behavior for moving right from the left arm. If a user has the left arm selected and presses right, since that behavior was not explicitly defined with GuiObject/NextSelectionRight, the game will attempt to find a selectable GUI element to the right. This is undesirable as the user should be confined to the character slots while in that portion of the menu. While this could be defined by setting the value to nil, a much simpler way is to use a selection group.

In GuiService, a selection group is a set of GUI elements that can be navigated between. There are two ways to define selection groups: GuiService/AddSelectionParent and GuiService/AddSelectionTuple. AddSelectionParent takes two arguments, a name for the selection and a GuiObject. In such a selection group, only the children of the passed in GuiObject can be navigated between. For the other function, AddSelectionTuple, you simply pass in all of the GuiObjects you want to be in the group. In this case, since all of the character slots are children of characterFrame, the simpler function to use is AddSelectionParent.

GuiService:AddSelectionParent("CharacterMenu", characterFrame)

Now, when a user enters the menu via openEquipmentMenu, they will only be able to navigate between the children of characterFrame.

Next, the character slot buttons have to be bound to move the selection to the inventory grid:

Again MouseButton1Click is used to detect when the user presses the A button with one of the character slots selected. When this event fires, onCharacterSlotClicked is called. This function first stores the character slot that was selected for later use, then binds the B button to call exitInventoryMenu, and finally selects the first grid cell in the inventory menu. exitInventoryMenu simply unbinds the B binding that onCharacterSlotClicked sets up and moves the selection back to the inventory slot that was selected before.

Navigating Inventory Grid

The inventory grid is much simpler to navigate than the character frame as the default gamepad selection code works very well with grids. The only things that need to be set up for the inventory grid are the selection group and the event when the user presses A with one of the cells selected.

GuiService:AddSelectionParent("InventoryMenu", inventoryScroll)

-- Player "clicked" on an inventory slot. This is where you would put code to take action
-- with the currentEquipmentSlot and the SelectedObject
local function onInventorySlotClicked()
	print("Character slot:", currentEquipmentSlot)
	print("Inventory cell:", GuiService.SelectedObject)
	-- TODO: Your code here!

for _, child in pairs(inventoryScroll:GetChildren()) do

Again, a selection group is set up with AddSelectionParent. Even though the default gamepad selection code will facilitate moving the selection among the grid, the function still needs to make sure the selection does not move outside the grid.

For every cell in the grid, onInventorySlotClicked is bound to MouseButton1Click. This function is what you would modify to do any custom code such as equipping the item the user selected. At the end of the function exitInventoryMenu is called to move the selection back to the character frame.

Once that function has been bound, the framework for the menu system is complete.

Project Source Code

Below is the complete source for the menu outlined in this article. To work properly, it must be inserted in a LocalScript located in StarterPlayerScripts.

  • inventory
  • grid
  • gui