Skip to content

Godot UI

Created: 2020-07-12 21:43:58 -0700 Modified: 2020-07-12 21:47:11 -0700

  • If you’re seeing a Control node in world space, then it’s likely because you didn’t use a canvas layer (reference). Don’t make your UI a child of a Camera (reference).

In general, if you find yourself trying to write code to detect if the mouse is within a rectangle yourself, then you’re likely doing things wrong.

E.g. I originally tried to use global_position and global_scale to figure out a Rect2 so that I could call “has_point” on it with event.position. However, this didn’t take the Camera2D’s bounds into account, so it would mess up whenever I moved the camera, and probably would have been a pain to maintain anyway.

Instead, the “right” way to do things is to add an Area2D and CollisionShape2D to your object, then use the Area2D’s input_event signal:

func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
if event is InputEventMouseButton and event.pressed:
# do something

Then, the issue I ran into was that my CanvasLayer was consuming clicks (reference). I had a Control under the CanvasLayer whose mousefilter I needed to set to IGNORE. I don’t fully understand why that still lets child Controls consume events, but children _can still get mouse events, so everything worked as intended.

  • Add your TTF font to res://assets/fonts (not required, but a good convention)
  • Set it as font data for something like a label using the “Custom Fonts” property:

Spritesheet sprite in a TextureRect

Section titled Spritesheet sprite in a TextureRect
  • To use a single sprite from a spritesheet as a TextureRect, use an AtlasTexture.
    • Make a new TextureRect
    • In the Inspector, make a new AtlasTexture
    • Expand the AtlasTexture so that you can see the “Atlas” property
    • Drag a spritesheet onto the “Atlas”
    • Set the region below it

  • If you end up using the AtlasTexture a lot, you can save it as a file and share it between controls.
  • Note: I had found a much more complicated way of doing this with a ViewportTexture in the TextureRect, then a sibling Viewport in the scene with a Sprite child of that Viewport, but then the Sprite size had to be doubled for some reason and I couldn’t figure out why.

Centering an AtlasTexture sprite in a layout

Section titled Centering an AtlasTexture sprite in a layout

To achieve something like this:

…my scene tree looks like this:

  • Panel
    • GridContainer
      • TextureRect
      • CenterContainer
        • Label

The TextureRect has “Keep Aspect Centered”:

Center elements in a GridContainer

Section titled Center elements in a GridContainer

This is sort of like “justify-content: space-between” in CSS:

How I accomplished this was with this scene tree:

  • Container
    • CenterContainer
      • Label (“Row 1”)
    • GridContainer (or HBoxContainer if you want)
      • Label (“Row”)
      • Label (“Two”)
      • Label (“Here”)

The GridContainer has 3 columns and has the “fill” and “expand” size flags for “Horizontal” (the scripting constant for this is SIZE_EXPAND_FILL). Each child label has the same horizontal size flags and also “Align: Center”.

To get closer to “justify-content: space-between”, I think you’d just set the middle label to SIZE_EXPAND_FILL, not the left/right labels.

Overlapping problems with containers

Section titled Overlapping problems with containers

I kept running into issues like this:

This is a scene tree that looks like this:

  • VBoxContainer
    • CenterContainer
      • HBoxContainer
        • Item
    • CenterContainer
      • HBoxContainer
        • Item
        • Item
    • CenterContainer
      • HBoxContainer
        • Item
        • Item

…where “Item” was itself another scene whose root node was a Panel:

My issue ended up being that I didn’t set a minimum size on the Panel in Item. Without that, a CenterContainer can’t know how big the Panel should be, so it sets the size to (0, 0).

Alternatively, you could be running into this issue where dynamically laying out a container’s children isn’t straightforward.

A MarginContainer is like the “padding” style in HTML; it puts extra space inside the container. This is not to be confused with the “Margin” properties on all Control nodes! MarginContainer’s properties are here:

Here’s a MarginContainer with a Panel as a child:

As you can see, the panel is “pulled away” from each edge by 25 pixels.