Loading...
Loading...
Design and build game UI/UX — HUDs, menus, and overlays — that survive every screen: anchor- based responsive layout, resolution/aspect scaling and safe areas, keyboard/gamepad focus navigation, a screen/menu state stack, and event-driven (not polled) HUD updates. Engine- neutral patterns that pair with the detected engine's UI skill. Use when the user mentions HUD, health bar, main menu, pause menu, settings screen, UI layout, anchors, UI scaling, aspect ratio, safe area, controller/keyboard menu navigation, or wiring UI to game state.
npx skill4agent add gamedev-skills/awesome-gamedev-agent-skills game-ui-uxgodot-ui-controlgame-feeldialogue-systemsreferences/input-systemscard-game(x, y)health_changedscore_changed# Godot 4.x. Anchor a HUD label to the TOP-LEFT; let a container flow a row of hearts.
func _ready() -> void:
$Score.set_anchors_preset(Control.PRESET_TOP_LEFT) # sticks to the corner at any size
# An HBoxContainer auto-lays-out children left-to-right; never position hearts by hand.
for i in lives:
$Hearts.add_child(make_heart()) # HBoxContainer spaces them for you
# Unity 6 uGUI: set RectTransform anchors to the corner; use a HorizontalLayoutGroup.
# RIGHT: anchors + layout groups. WRONG: rect.anchoredPosition = new Vector2(640, 360) (1080p-only).# Godot 4.x — Project Settings > Display > Window > Stretch:
# Mode = "canvas_items", Aspect = "expand", reference size e.g. 1920x1080.
# UI scales to the window; "expand" reveals extra space you anchor HUD corners into.
# Unity 6 — Canvas > CanvasScaler:
# UI Scale Mode = "Scale With Screen Size", Reference Resolution = 1920x1080,
# Match = 0.5 (blend width/height) — pick 1.0 if your HUD is height-critical.# Godot 4.x. Inset a margin container to the OS-reported safe rect (phones, TVs).
func _apply_safe_area() -> void:
var safe: Rect2i = DisplayServer.get_display_safe_area()
var win := DisplayServer.window_get_size()
$Margin.add_theme_constant_override("margin_left", safe.position.x)
$Margin.add_theme_constant_override("margin_top", safe.position.y)
$Margin.add_theme_constant_override("margin_right", win.x - safe.end.x)
$Margin.add_theme_constant_override("margin_bottom", win.y - safe.end.y)
# Unity 6: read Screen.safeArea (Rect in pixels) and set a panel's anchorMin/anchorMax to
# safeArea.position / (position+size) normalized by Screen.width/height.# Godot 4.x. Give each screen a default focus and wire neighbors so a stick/d-pad walks it.
func _on_screen_shown() -> void:
$PlayButton.grab_focus() # always focus SOMETHING on open
$PlayButton.focus_neighbor_bottom = $SettingsButton.get_path()
$SettingsButton.focus_neighbor_top = $PlayButton.get_path()
# Unity 6: EventSystem.SetSelectedGameObject(playButton) on enable; set each Selectable's
# Navigation (Explicit or Automatic). RIGHT: a control is focused on open. WRONG: nothing
# selected → the gamepad does nothing and the player is stuck.# RIGHT: HUD reacts to a signal; it updates only when health actually changes.
func _ready() -> void:
player.health_changed.connect(_on_health_changed) # emitted by gameplay
func _on_health_changed(current: int, max: int) -> void:
$HealthBar.value = float(current) / max
# WRONG: func _process(dt): $HealthBar.value = player.hp / player.max_hp # polls every frame,
# couples UI to the player's internals, and runs work even when nothing changed._processUpdateisPausedinSettingsreferences/references/layout-and-flow.mdgodot-ui-controlgame-feeldialogue-systemsinput-systemsrpgcard-gametower-defensevisual-novel