Options menu with graphics settings in Godot

I am currently working on the 20 Games Challenge, which consists of the challenge to build 20 small games to learn about the various aspects of game development. I enjoy the concept of having a fixed scope and work on small-ish projects that actually get finished.

My own approach differs a bit though, as I lose track and get stuck on certain details that weren’t even part of the challenge in the first place. I’m currently on game 3 and build a clone of frogger. My game differs from the challenge in that it is in 3D and I wanted to build an options menu for setting various settings at runtime, that are by default only accessible through the Godot editor. An example of such a setting is MSAA 3D (3D Multisample Anti-Aliasing).

The options menu for my game

The selection of an options in the OptionButton triggers a signal, which was handled similar to this:

# [...]

func _ready() -> void:
    _setup_options()
    # [...]

func _setup_options() -> void:
    %msaa.clear()
    %msaa.add_item("Disabled", Viewport.MSAA_DISABLED)
    %msaa.add_item("2×", Viewport.MSAA_2X)
    %msaa.add_item("4×", Viewport.MSAA_4X)
    %msaa.add_item("8×", Viewport.MSAA_8X)
    %msaa.select(get_viewport().msaa_3d)

func _on_msaa_item_selected(index: int) -> void:
    get_viewport().msaa_3d = (index as MSAA)
    save_settings()

# [...]

Selecting an option did however not have an effect, I could not observe any changes in the anti-aliasing in the game, the FPS stayed constant and the load on the graphics card didn’t change. I compared my code with the “graphics_settings” project from the godot-demo-projects repository and could not find an obvious difference. The code in _on_msaa_option_button_item_selected also called get_viewport().msaa_3d = .... index was the correct value from the Viewport.MSAA-enum, that I even cast to MSAA1.

After quite a lot of debugging and by eliminating all other options I finally found the issue: My options dialog was, in contrast to the demo project, in its own Window. The class Window inherits from Viewport, which caused my code to only change the settings of the options window itself. My fix was to replace get_viewport() with my own function that determined the root viewport like this:

func get_root_viewport() -> Viewport:
    return get_tree().get_root().get_viewport()

  1. Which was not even necessary. It only saves the verbose code (if index == ...: get_viewport().msaa_3d = ... elif ...), but by using the index you lose the type information and only “implicitly” pass important information. A cleaner solution is to use item metadata (set_item_metadata/get_item_metadata) on the option entries, as Variants can be used there.