ziva-sh/godot-control-layout

Construit des interfaces utilisateur Godot 4 qui se redimensionnent au lieu de se couper ou de se chevaucher — conteneurs vs ancres, présélections d'ancres depuis le code, indicateurs de taille, custom_minimum_size, marges du MarginContainer, retour à la ligne des Labels, mouse_filter, HUDs avec CanvasLayer, noms de surcharge de thème Godot 4 (add_theme_color_override et autres) et les paramètres d'affichage/fenêtre/étirement pour plusieurs résolutions. À utiliser lors de la création ou de la correction de menus, HUDs, boîtes de dialogue et écrans d'inventaire, lorsque des éléments UI sont coupés, de taille nulle, qui se chevauchent ou sont ignorés.

Qu'est-ce que godot-control-layout ?

Cette compétence fournit un guide complet pour créer des interfaces utilisateur Godot 4 qui se redimensionnent correctement, évitant les problèmes courants tels que le clipping ou le chevauchement. Elle couvre les trois erreurs principales : positionner manuellement les enfants d'un conteneur, ancrer un contrôle sans réinitialiser ses décalages (offsets), et laisser les paramètres d'étirement par défaut. Elle explique comment utiliser les conteneurs (VBoxContainer, HBoxContainer, etc.) avec des indicateurs de taille et custom_minimum_size, et comment ancrer uniquement le nœud racine de chaque UI avec set_anchors_and_offsets_preset(). La compétence détaille également la création de HUD avec CanvasLayer, la gestion du texte pour éviter le clipping (autowrap_mode, text_overrun_behavior), le filtre de souris (mouse_filter) pour contrôler la réception des clics, et les noms de surcharge de thème Godot 4. Enfin, elle couvre les paramètres de résolution et d'étirement (display/window/stretch) avec leurs équivalents en code, ainsi qu'une checklist de dépannage pour remédier aux UI mal affichées.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ziva-sh/godot-skills/tree/HEAD/skills/godot-control-layout

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Disposition des contrôles dans Godot 4

La plupart des interfaces Godot cassées proviennent de trois erreurs : positionner manuellement les enfants d'un conteneur, ancrer un contrôle sans réinitialiser ses décalages (offsets), et laisser les paramètres d'étirement par défaut. Disposez avec des conteneurs, ancrez uniquement la racine de chaque UI, et laissez les tailles minimales piloter le reste.

Les conteneurs possèdent leurs enfants

Un enfant d'un Container (VBoxContainer, HBoxContainer, GridContainer, MarginContainer, PanelContainer, CenterContainer, ScrollContainer, FlowContainer) ne peut pas conserver ses propres position, size ou ancres : le conteneur les écrase lors du tri suivant. Contrôlez la part d'espace d'un enfant avec :

  • size_flags_horizontal / size_flags_vertical : Control.SIZE_FILL (par défaut), SIZE_EXPAND_FILL pour prendre l'espace libre, SIZE_SHRINK_CENTER / SIZE_SHRINK_END pour rester petit et aligné.
  • size_flags_stretch_ratio : part de l'espace libre entre les frères en expansion.
  • custom_minimum_size : la plus petite taille que l'enfant accepte. Les conteneurs ne réduisent jamais un enfant en dessous de sa taille minimale combinée, ce qui évite que le texte et les boutons soient coupés.

Choisissez le conteneur selon la disposition : lignes ou colonnes (VBoxContainer, HBoxContainer), une grille (GridContainer avec columns), du padding (MarginContainer, padding défini par les constantes de thème margin_left, margin_top, margin_right, margin_bottom), un arrière-plan (PanelContainer, style box panel), un contenu long (ScrollContainer avec un enfant ayant SIZE_EXPAND_FILL), des éléments qui se répartissent sur plusieurs lignes (HFlowContainer).

Ancres pour la racine uniquement

Les ancres placent un contrôle par rapport au rectangle de son parent et sont ce que vous utilisez pour le nœud UI le plus externe. Depuis le code, utilisez set_anchors_and_offsets_preset(). set_anchors_preset() ne déplace que les ancres et conserve les anciens décalages, donc un contrôle créé dans le code reste à la taille (0, 0).

Présélections : 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, et les présélections centre-bord comme PRESET_CENTER_TOP.

HUD sur un CanvasLayer

Un Control sous un Node2D est dessiné dans l'espace mondial et se déplace avec la Camera2D. Placez le HUD sous un CanvasLayer pour qu'il reste à l'écran ; layer ordonne plusieurs calques de canevas (le plus élevé dessine par-dessus).

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

Construit dans l'éditeur, la même arborescence est : CanvasLayer > MarginContainer (Disposition: Plein écran) > VBoxContainer > HBoxContainer > Label, Control (Expansion), Button.

Texte qui ne se coupe pas

  • Un Label avec autowrap_mode défini a besoin d'une largeur quelque part. À l'intérieur d'un HBoxContainer avec les indicateurs par défaut, il se réduit à 1 pixel de large et devient très haut. Donnez-lui SIZE_EXPAND_FILL ou un custom_minimum_size.x.
  • Pour un texte sur une seule ligne dans un espace fixe, définissez text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS (ou clip_text = true).
  • Préférez add_theme_font_size_override("font_size", n) plutôt que de redimensionner un contrôle : scale ne change pas la taille réservée par les conteneurs.
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)

Clics qui n'atteignent pas le jeu

mouse_filter détermine si un contrôle consomme les entrées de la souris. Les valeurs par défaut diffèrent : un simple Control, ColorRect et Panel utilisent MOUSE_FILTER_STOP, les conteneurs utilisent MOUSE_FILTER_PASS, Label utilise MOUSE_FILTER_IGNORE. Un Control ou ColorRect plein écran avec STOP avale chaque clic, donc _unhandled_input() dans le jeu ne le voit jamais. Définissez mouse_filter = Control.MOUSE_FILTER_IGNORE sur les superpositions, les espaceurs et les rectangles décoratifs.

Surcharges de thème (noms Godot 4)

Godot 3Godot 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)
(la taille de police vivait dans 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 dans .tscntheme_override_colors/font_color

Supprimez une surcharge avec le remove_theme_*_override(name) correspondant. Pour un aspect cohérent, attribuez une ressource Theme au contrôle racine au lieu de surcharger chaque nœud ; les enfants l'héritent.

Résolution et étirement

Définissez ces paramètres dans Paramètres du projet > Affichage > Fenêtre (ils sont stockés dans project.godot) :

  • display/window/size/viewport_width et viewport_height : la résolution de base pour laquelle vous concevez (par défaut 1152 x 648).
  • display/window/stretch/mode : canvas_items pour la plupart des jeux (UI et 2D se mettent à l'échelle en douceur), viewport pour le pixel art, disabled pour les outils. Le mode 2d de Godot 3 est canvas_items dans Godot 4. La valeur par défaut est disabled ; les projets créés dans Godot 4.7 ou ultérieur démarrent avec canvas_items.
  • display/window/stretch/aspect : expand permet à l'UI ancrée d'utiliser l'espace supplémentaire sur les écrans plus larges ou plus hauts ; keep ajoute des barres noires. La valeur par défaut est keep ; les projets créés dans Godot 4.7 ou ultérieur démarrent avec expand.
  • display/window/stretch/scale_mode : integer pour un pixel art net.

Au moment de l'exécution, les mêmes paramètres se trouvent sur la fenêtre : get_window().content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS et get_window().content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND.

Checklist lorsque l'UI est coupée ou mal placée

  1. Le contrôle est-il un enfant d'un conteneur avec une position ou size définie manuellement ? Utilisez les indicateurs de taille et custom_minimum_size.
  2. La racine a-t-elle été ancrée avec set_anchors_preset() uniquement ? Utilisez set_anchors_and_offsets_preset().
  3. Un Label avec retour à la ligne a-t-il une largeur ?
  4. Le HUD est-il sous un CanvasLayer plutôt que sous un Node2D ?
  5. display/window/stretch/mode est-il toujours disabled alors que la taille de la fenêtre change ?
  6. Un contrôle invisible plein écran avec MOUSE_FILTER_STOP se trouve-t-il au-dessus ?

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

ziva-sh/godot-4-api-pitfalls

Fixes Godot 3 APIs and GDScript syntax that AI assistants write into Godot 4 projects, such as KinematicBody2D, move_and_slide(velocity), export, onready, setget, yield, string connect(), instance(), rand_range, the Tween node, File, change_scene, Spatial, rect_* properties, Pool*Array, OS time functions and the old TileMap API. Use when writing, reviewing or porting GDScript for Godot 4.x, when a script fails with "not declared", "not found in base", "was removed in Godot 4" or "is not present

ziva-sh/godot-scene-organization

Structures Godot 4 projects so scenes stay reusable and references do not break - when to make a scene, a script or a Resource, instancing with @export PackedScene, preload and load, @onready and %UniqueName node access, _ready order, groups, autoloads, class_name, and resource UIDs with the .uid files added in Godot 4.4, including how to move or rename files safely. Use when creating new scenes or scripts, deciding how nodes find each other, adding global state or an event bus, moving or renami

ziva-sh/godot-signals

Godot 4 signal patterns and their failure modes - declaring typed signals, emit(), connect() and disconnect() with Callables, bind() and unbind(), lambdas, CONNECT_ONE_SHOT and CONNECT_DEFERRED, await on signals and timers, editor-made connections stored in .tscn files, and the call down, signal up rule. Use when wiring UI buttons, timers, Area2D or body events and custom game events, when replacing Godot 3 connect("signal", self, "method") or yield code, or when debugging "already connected", "

ziva-sh/godot-tilemaplayer

Correct Godot 4.3+ TileMapLayer code - set_cell(coords, source_id, atlas_coords), reading cells and custom data, erasing, coordinate conversion, terrains, tile collision through TileSet physics layers, finding the tile a body hit, and migrating from the deprecated TileMap node and its layer-index API. Use when placing, reading or erasing tiles from GDScript, building a TileSet in code, generating levels procedurally, handling tile collisions, or porting TileMap code from Godot 3 or Godot 4.0 to

Skills associés