ButtonGroup in Godot – Complete Guide

When creating games or applications, user interfaces (UI) play a critical role in user interaction. An essential part of any UI toolkit is the ability to group together related buttons that present mutually exclusive options, much like the radio buttons you might find in a survey form. This is where the concept of a ButtonGroup shines, particularly within the Godot 4 Engine.

What is ButtonGroup?

What is ButtonGroup?

ButtonGroup is a class provided by Godot 4 that allows developers to link together several buttons such that only one button can be active at a time. This class inherits from Resource, which means it’s a datablock that can be saved to a disk. When buttons are part of a ButtonGroup, they behave like radio buttons found in web forms or applications.

What is it for?

By using ButtonGroup, game developers can ensure that when they have a set of options where only one should be chosen, they can easily manage this logic. For instance, ButtonGroups are great for settings like difficulty levels in a game, character selection screens, or even menu navigation where one item should be highlighted at a time.

Why should I learn it?

Understanding how to implement a ButtonGroup is fundamental for creating intuitive and user-friendly interfaces. By mastering ButtonGroups, you’ll ensure your game users do not experience confusion with your UI, making their gameplay seamless and enjoyable. This knowledge bridges the gap between knowing how to place buttons and understanding how they interact within a user interface context, a key skill for any aspiring or experienced game developer.

CTA Small Image

Creating a Simple ButtonGroup

To start using a ButtonGroup in Godot 4, we first need to add a series of buttons that will become part of this group. Here’s how you can create a ButtonGroup and add RadioButtons to it:

var button_group = ButtonGroup.new()

func _ready():
    var radio_button1 = RadioButton.new()
    radio_button1.text = "Option 1"
    radio_button1.button_group = button_group
    var radio_button2 = RadioButton.new()
    radio_button2.text = "Option 2"
    radio_button2.button_group = button_group
    var radio_button3 = RadioButton.new()
    radio_button3.text = "Option 3"
    radio_button3.button_group = button_group

In this example, we create a new ButtonGroup and three RadioButtons. Each RadioButton is then assigned to the same ButtonGroup, ensuring only one can be selected at a time. Next, we add the buttons to the node tree with the add_child() method.

Connecting Buttons to Signals

Godot’s signal system allows for event-driven programming. We can connect the buttons’ pressed() signal to know when a user has made a selection. Here’s how we can set that up:

func _ready():
    for i in get_children():
        if i is RadioButton:
            i.connect("pressed", self, "_on_RadioButton_pressed", [i])

Using the _on_RadioButton_pressed callback function, we can implement the logic that should happen once a button is selected.

func _on_RadioButton_pressed(button):
    print("Selected: " + button.text)

This simplistic function prints out the text of the selected button, providing us with immediate feedback regarding the user’s choice.

Changing Button State Programmatically

Sometimes you may need to change the selected button via code. You can do this by accessing the buttons inside the ButtonGroup directly. Here’s an example where we select the second button as the default choice:

func _ready():
    var buttons = button_group.get_buttons()
    buttons[1].pressed = true

The get_buttons() method returns an array of all the buttons within the group, and the pressed property allows us to change the button’s state.

Utilizing ButtonGroup’s Methods

ButtonGroup also provides a method to get the currently pressed button directly. Consider this example where we might need to retrieve the selected option on the fly:

func get_selected_option():
    var pressed_button = button_group.get_pressed_button()
    if pressed_button:
        return pressed_button.text
    return "No option selected"

This function checks which button is currently pressed and returns its text. If no buttons are pressed, it returns a default message. It’s a quick way to get the state of the ButtonGroup at any given moment.

Keep these examples as a reference as we delve further into Godot 4’s UI capabilities, exploring more advanced features and customizations in our next tutorial segments. Understanding these basics is crucial for creating intuitive and responsive game interfaces.

Building upon the basics, let’s dive into more advanced utilization of ButtonGroups in Godot 4. We’ll explore additional functionalities and provide code examples that will empower you to use ButtonGroups more effectively within your next game project.

One common task might be to disable a button within a ButtonGroup. This can be essential when, for instance, an option is not available due to the game’s state:

func disable_button_at_index(index):
    var buttons = button_group.get_buttons()
    if index >= 0 and index < buttons.size():
        buttons[index].disabled = true

In the code snippet above, we’ve created a function that disables a button based on its index within the ButtonGroup’s array of buttons.

What if we want to toggle the visibility of a button based on certain conditions? Below is an example of how you can show or hide a button:

func toggle_button_visibility(button, is_visible):
    button.visible = is_visible

This function simply accepts a button and a boolean is_visible parameter to control the button’s visibility.

Sometimes, game design calls for dynamically creating button choices, for example, when you unlock levels or modes. Here’s how you could dynamically add buttons to a ButtonGroup:

func add_button_to_group(text):
    var new_button = RadioButton.new()
    new_button.text = text
    new_button.button_group = button_group

This function creates a new RadioButton, sets its text, assigns it to the existing ButtonGroup, and adds it to the parent node. You could call this method whenever a new option becomes available.

ButtonGroups are also useful when you need to iterate through the choices to apply a batch operation. For example, if we want to set a common property across all buttons:

func set_buttons_focus_mode(mode):
    var buttons = button_group.get_buttons()
    for button in buttons:
        button.focus_mode = mode

This function takes a focus mode and applies it to each of the buttons in the ButtonGroup, making batch updates efficient and less error-prone.

Sometimes, it is necessary to respond dynamically to the changing state of a ButtonGroup, perhaps to update other UI elements or game logic. We can utilize the pressed signal for this:

func _ready():
    for button in button_group.get_buttons():
        button.connect("pressed", self, "_on_Button_pressed")
func _on_Button_pressed():
    var selected_text = get_selected_option()

func update_ui_based_on_selection(selection):
    # Your logic to update UI based on selection
    print("UI updated to reflect the selection: " + selection)

This sequence of functions showcases how to connect all buttons in the group to a signal, handle the signal when a button is pressed, and then perform a UI update based on the current selection.

Lastly, you may want to clear all selections within a ButtonGroup. Godot’s Button nodes have a pressed property that can be set to false to accomplish this:

func clear_button_group_selections():
    var buttons = button_group.get_buttons()
    for button in buttons:
        button.pressed = false

This function iterates through each button and sets the pressed state to false, effectively clearing all selections.

Through these various functionalities and the combined knowledge of basic and advanced ButtonGroup usage, you can create a highly interactive and responsive UI for your players. Remember, experimenting with these elements and applying them to your own game’s context will deepen your understanding and proficiency with Godot’s robust UI toolkit.

Continuing with our exploration of the ButtonGroup in Godot 4, let’s look at responding to user input in more complex ways, such as changing scenes or updating game settings. We’ll provide a few examples that illustrate these interactions, amplifying the potential of ButtonGroups in your game development.

Example 1: Suppose you want to use a ButtonGroup for a level selection screen. Upon selecting a level, the game should change to the appropriate scene:

func _on_Button_pressed(button):
    var level_name = button.name
    var path_to_level_scene = "res://levels/" + level_name + ".tscn"

In the function above, we derive the scene path from the button’s name property and use change_scene() on the SceneTree to switch to the selected level.

Example 2: You might want to adjust game settings like audio volume with buttons. A ButtonGroup can switch between predefined volume levels:

func _on_Button_pressed(button):
    var volume_level = button.get_meta("volume_level")
    AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), volume_level)

Here, each button has a meta property storing the associated volume level. When a button is pressed, it sets the volume level on the Master audio bus to this value.

Example 3: Sometimes, you’ll have a need to reset selections within a ButtonGroup to their default state. This may happen during game initialization or when a user selects the ‘Reset to Default’ button:

func reset_to_default():
    var default_button = button_group.get_buttons()[0]
    default_button.pressed = true
    # Additional default settings reset logic if needed.

This function is designed to select the first button, often used to set the default choice.

Example 4: You can use the information stored within ButtonGroup to populate in-game elements dynamically. For instance, if you want to display the current selection in a Label:

func _ready():

func _on_Button_pressed():

func update_label_with_current_selection():
    var selection = get_selected_option()
    $SelectionLabel.text = "Current Selection: " + selection

In the above code, the Label node’s text is updated every time a button is pressed to reflect the current selection.

Example 5: ButtonGroups can also be used for complex UI navigations, such as changing tabs within a game. Here’s how a ButtonGroup might control a TabContainer:

func _on_Button_pressed(button):
    var tab_index = button.get_meta("tab_index")
    $TabContainer.current_tab = tab_index

Each button has a meta property with an index connected to a particular tab in a TabContainer, allowing the selection to switch tabs directly.

Example 6: To enhance usability, you might want to implement navigation between buttons using a keyboard. This can be done by binding keys to select buttons:

func _input(event):
    if event is InputEventKey and event.pressed:
        var buttons = button_group.get_buttons()
        if event.scancode == KEY_DOWN:
            select_next_button_in_group(buttons, true)
        elif event.scancode == KEY_UP:
            select_next_button_in_group(buttons, false)

func select_next_button_in_group(buttons, is_down):
    var current_button = button_group.get_pressed_button()
    var index = buttons.find(current_button)
    var next_index = (index + (is_down ? 1 : -1)) % buttons.size()
    buttons[next_index].pressed = true

This setup allows a player to use the arrow keys to navigate through the buttons in a ButtonGroup, enhancing the user experience with keyboard support.

These examples illustrate the versatile ways ButtonGroups can be used in Godot 4 to create responsive and interactive UIs. From level selections and settings adjustments to dynamic UI updates and keyboard navigation, incorporating ButtonGroups effectively will certainly elevate the player’s experience. Remember to always consider the context of your game and the best way to integrate UI elements cohesively to create an engaging and seamless interaction for your users.

Continuing Your Game Development Journey

Mastering the use of ButtonGroup in Godot 4 is just the beginning of your game development adventure. To further expand your skills and knowledge, our Godot Game Development Mini-Degree is the perfect next step. It covers an array of essential topics beyond UI elements, including 2D and 3D game mechanics, scripting with GDScript, and creating immersive gameplay experiences across different genres. This comprehensive program will guide you through the intricacies of Godot, enabling you to build cross-platform games from the ground up.

Whether you’re brand new to game development or looking to sharpen your existing talents, we’ve tailored our courses to fit your learning pace and style. And don’t stop there! For a broader range of topics and projects, explore our full collection of Godot courses. We regularly update our curriculum to stay in step with the latest industry standards, ensuring that you’re learning the most up-to-date techniques and best practices.

With Zenva, you’ll not only gain the technical know-how but also the confidence to turn your creative visions into playable realities. Take your next step towards professional game development with us, and transform your passion for games into a flourishing career.


In unlocking the power of ButtonGroup in Godot 4, you’ve equipped yourself with a vital tool to curate professional and user-friendly interfaces in your games. This is just a glimpse into the vast capabilities that Godot offers, and your journey forward is filled with endless possibilities. At Zenva, we are dedicated to providing you with the resources, guidance, and support you need to excel.

Embrace the challenge and continue to expand your horizons with our Godot Game Development Mini-Degree, where every lesson is a step towards mastery. Dive in and let Zenva be the wind beneath your wings as you soar to new heights in your game development career. Create, innovate, and bring your dreams to life – one line of code at a time.

Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.