npx skills add ...
npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill game-ui-ux
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 skills add gamedev-skills/awesome-gamedev-agent-skills --skill game-ui-ux
Build HUDs and menus that stay correct on a phone, an ultrawide monitor, and a TV across a gamepad and a mouse. This skill owns the engine-neutral UI architecture — responsive layout, scaling, focus navigation, screen flow, and how UI talks to game state — and defers the concrete widget API to the engine UI skill.
When not to use: for the engine's concrete UI nodes/components and styling, use
godot-ui-control or Unity UI (UGUI/UI Toolkit). For visual punch (button pop, damage
numbers, shake) use game-feel. For branching conversation UI use dialogue-systems. For
translating UI strings, that is localization (see references/ and input-systems for
rebinding screens). For card/board layout specifics, the card-game genre composes this skill.
(x, y) positions break at the first new resolution.health_changed,
score_changed, etc. and updates only when they fire — it does not read game state every
frame._process/Update. Couples UI to internals and wastes work. Push
updates via signals/events.isPaused, inSettings, …) becomes unmanageable. Use a
screen stack with push/pop.references/).references/layout-and-flow.md.godot-ui-control, Unity UI (UGUI/UI Toolkit) — the concrete widgets, themes, and styling.game-feel — button pops, transitions, and HUD juice that ride on top of this layout.dialogue-systems — conversation/choice UI that lives inside this UI shell.input-systems — device switching, rebinding screens, and accessible controls.rpg, card-game, tower-defense, visual-novel — UI-heavy genres that compose this skill.# Godot 4.7 — 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.3 LTS — 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.7. 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.3 LTS: 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.7. 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.3 LTS: 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.