Layout de controle no Godot 4
A maioria das UIs quebradas no Godot vem de três erros: posicionar filhos de um container manualmente, ancorar um controle sem redefinir seus offsets e deixar as configurações de esticamento (stretch) nos valores padrão. Organize com containers, ancore apenas a raiz de cada UI e deixe os tamanhos mínimos guiarem o resto.
Containers possuem seus filhos
Um filho de qualquer Container (VBoxContainer, HBoxContainer, GridContainer, MarginContainer, PanelContainer, CenterContainer, ScrollContainer, FlowContainer) não pode manter sua própria position, size ou âncoras: o container as sobrescreve na próxima organização. Controle a participação de um filho no espaço com:
size_flags_horizontal/size_flags_vertical:Control.SIZE_FILL(padrão),SIZE_EXPAND_FILLpara ocupar espaço extra,SIZE_SHRINK_CENTER/SIZE_SHRINK_ENDpara ficar pequeno e alinhado.size_flags_stretch_ratio: proporção do espaço extra entre irmãos que se expandem.custom_minimum_size: o menor tamanho que o filho aceita. Containers nunca encolhem um filho abaixo de seu tamanho mínimo combinado, que é como textos e botões evitam ser cortados.
Escolha o container pelo layout: linhas ou colunas (VBoxContainer, HBoxContainer), uma grade (GridContainer com columns), preenchimento (MarginContainer, preenchimento definido por constantes de tema margin_left, margin_top, margin_right, margin_bottom), um fundo (PanelContainer, style box panel), conteúdo longo (ScrollContainer com um filho que tenha SIZE_EXPAND_FILL), itens que quebram linha (HFlowContainer).
Âncoras apenas para a raiz
Âncoras posicionam um controle em relação ao retângulo do seu pai e são o que você usa para o nó de UI mais externo. Em código, use set_anchors_and_offsets_preset(). set_anchors_preset() apenas move as âncoras e mantém os offsets antigos, então um controle criado em código permanece com tamanho (0, 0).
Presets: PRESET_FULL_RECT, PRESET_TOP_LEFT, PRESET_TOP_RIGHT, PRESET_BOTTOM_LEFT, PRESET_BOTTOM_RIGHT, PRESET_CENTER, PRESET_TOP_WIDE, PRESET_BOTTOM_WIDE, PRESET_LEFT_WIDE, PRESET_RIGHT_WIDE, PRESET_HCENTER_WIDE, PRESET_VCENTER_WIDE, e os presets de centro-borda como PRESET_CENTER_TOP.
HUD em um CanvasLayer
Um Control sob um Node2D é desenhado no espaço do mundo e se move com a Camera2D. Coloque o HUD sob um CanvasLayer para que ele permaneça na tela; a propriedade layer ordena várias camadas de canvas (a maior desenha por cima).
extends CanvasLayer
var score_label: Label
func _ready() -> void:
var margin := MarginContainer.new()
margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
for side in ["margin_left", "margin_top", "margin_right", "margin_bottom"]:
margin.add_theme_constant_override(side, 16)
add_child(margin)
var column := VBoxContainer.new()
margin.add_child(column)
var top_bar := HBoxContainer.new()
column.add_child(top_bar)
score_label = Label.new()
score_label.text = "Score: 0"
score_label.add_theme_font_size_override("font_size", 24)
score_label.add_theme_color_override("font_color", Color.GOLD)
top_bar.add_child(score_label)
var spacer := Control.new()
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE
top_bar.add_child(spacer)
var pause_button := Button.new()
pause_button.text = "Pause"
pause_button.custom_minimum_size = Vector2(96, 40)
top_bar.add_child(pause_button)
func set_score(value: int) -> void:
score_label.text = "Score: %d" % value
Construída no editor, a mesma árvore é: CanvasLayer > MarginContainer (Layout: Full Rect) > VBoxContainer > HBoxContainer > Label, Control (Expand), Button.
Texto que não recorta
- Uma
Labelcomautowrap_modedefinido precisa de uma largura de algum lugar. Dentro de umHBoxContainercom flags padrão, ela colapsa para 1 pixel de largura e fica muito alta. Dê a elaSIZE_EXPAND_FILLou umcustom_minimum_size.x. - Para texto de uma linha em um espaço fixo, defina
text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS(ouclip_text = true). - Prefira
add_theme_font_size_override("font_size", n)em vez de escalar um controle:scalenão altera o tamanho que os containers reservam.
extends VBoxContainer
func add_message(text: String) -> void:
var label := Label.new()
label.text = text
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
add_child(label)
func add_title(text: String) -> void:
var label := Label.new()
label.text = text
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
label.custom_minimum_size = Vector2(0, 32)
add_child(label)
Cliques que não alcançam o jogo
mouse_filter decide se um controle consome entrada do mouse. Os padrões diferem: Control, ColorRect e Panel comuns usam MOUSE_FILTER_STOP, containers usam MOUSE_FILTER_PASS, Label usa MOUSE_FILTER_IGNORE. Um Control ou ColorRect em tela cheia com STOP engole todos os cliques, então _unhandled_input() no jogo nunca os vê. Defina mouse_filter = Control.MOUSE_FILTER_IGNORE em sobreposições, espaçadores e retângulos decorativos.
Overrides de tema (nomes Godot 4)
| Godot 3 | Godot 4 |
|---|---|
add_color_override("font_color", c) | add_theme_color_override("font_color", c) |
add_font_override("font", f) | add_theme_font_override("font", f) |
| (tamanho da fonte ficava em DynamicFont) | add_theme_font_size_override("font_size", 24) |
add_stylebox_override("normal", sb) | add_theme_stylebox_override("normal", sb) |
add_constant_override("separation", 8) | add_theme_constant_override("separation", 8) |
add_icon_override("icon", tex) | add_theme_icon_override("icon", tex) |
custom_colors/font_color no .tscn | theme_override_colors/font_color |
Remova um override com o remove_theme_*_override(nome) correspondente. Para uma aparência consistente, atribua um recurso Theme ao controle raiz em vez de sobrescrever cada nó; os filhos o herdam.
Resolução e esticamento
Defina estas opções em Project Settings > Display > Window (elas são armazenadas em project.godot):
display/window/size/viewport_widtheviewport_height: a resolução base para a qual você projeta (padrão 1152 x 648).display/window/stretch/mode:canvas_itemspara a maioria dos jogos (UI e 2D escalam suavemente),viewportpara pixel art,disabledpara ferramentas. O modo2ddo Godot 3 écanvas_itemsno Godot 4. O padrão da configuração édisabled; projetos criados no Godot 4.7 ou posterior iniciam comcanvas_items.display/window/stretch/aspect:expandpermite que a UI ancorada use espaço extra em telas mais largas ou altas;keepadiciona barras pretas. O padrão ékeep; projetos criados no Godot 4.7 ou posterior iniciam comexpand.display/window/stretch/scale_mode:integerpara pixel art nítida.
Em tempo de execução, as mesmas configurações estão na janela: get_window().content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS e get_window().content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND.
Checklist quando a UI está cortada ou mal posicionada
- O controle é filho de um container com
positionousizedefinidos manualmente? Use flags de tamanho ecustom_minimum_size. - A raiz foi ancorada apenas com
set_anchors_preset()? Useset_anchors_and_offsets_preset(). - Uma
Labelcom quebra de linha tem uma largura? - O HUD está sob um
CanvasLayerem vez de umNode2D? display/window/stretch/modeainda estádisabledenquanto o tamanho da janela muda?- Um controle invisível em tela cheia com
MOUSE_FILTER_STOPestá por cima?