Imported from thruthesky/gohud (
skills/gohud/SKILL.md). Install upstream withnpx skills add thruthesky/gohud --skill gohud. Copyright stays with the author (MIT).
gohud — game UI for Godot 4.6+
gohud is a pure-GDScript HUD & UI kit. Every class is global once the folder sits at res://addons/gohud/;
widgets are made with .new() + add_child() and styled by one theme, one skin and one icon set.
This skill carries the whole API (references/), runnable screen templates (assets/templates/) and a
preview launcher (scripts/gohud_preview.py).
Arguments given: $ARGUMENTS
1. Route the request
| First word of the arguments | Do |
|---|---|
preview |
§6 — launch the preview with the remaining arguments |
features |
Read references/features.md and present it grouped (one line of code per feature). If an area follows (features theming), also read that area's reference and go deeper |
update |
Read references/setup.md §7 and follow commands/update.md — update the add-on in the project and this skill, then verify the two agree |
| anything else / empty | §2 — build or change UI with gohud |
Reply in the language the user writes in; keep code identifiers as they are.
2. Workflow for building UI
- Check the project.
test -f project.godot,test -f addons/gohud/plugin.cfg,godot --version(needs 4.6+). Note the gohud version (version=inplugin.cfg) — rules 4, 5 and 7 differ for 1.0.3 and older. Missing add-on → install it (references/setup.md§1), thengodot --headless --path . --import. - Pick the look first.
GoUi.use_preset(GoThemePresets.SCIFI_DARK)(or the project setting) before any widget is built. Nodes keep the theme they were built with; switching later means rebuilding the screen. - Choose the widget for the job — table in
references/surfaces.md§7: blocking question →GoDialogs, list over the game →GoSheet, side panel on a wide screen →GoDrawer, window →GoSurface, full-screen menu/login/settings → root Control +GoForm, message with an Undo button →GoSnackbar, fixed notice panel the HUD owns →GoNotice, optional question →GoPromptCard, info card next to a slot →GoPopover, HUD pieces →GoHudAnchor. Lists and forms: sortable/selectable rows →GoTable, pages →GoPagination, searchable picker →GoCombobox, a field that can show an error →GoField, input welded to a button →GoInputGroup. Waiting →GoSpinner(GoSpinner.busy(button, true)also blocks the double press). A list the player scans (quests, mail, a shop) → rule 15 in §3 and the recipe inreferences/recipes.md§16. - Start from a template when one fits (§5): copy it into the project (e.g.
res://ui/), rename, adjust, wire its signals. Otherwise compose withGoStylefactories (references/style.md). - Follow the rules in §3. Look up exact signatures in the references before using a member you are not sure of — do not guess APIs.
- Verify without a window:
python3 ${CLAUDE_SKILL_DIR}/scripts/gohud_preview.py res://ui/my_screen.tscn --check(orgodot --headless --path . --quit-after 120 res://ui/my_screen.tscnand scan forSCRIPT ERROR,Parse Error,ERROR: Failed). Open a visible window only when the user asks to see it (§6).
3. Rules that prevent the real bugs
- Sizes and colours come from tokens, never literals:
GoUi.metric(GoTheme.GAP),GoUi.color(GoTheme.MUTED),GoStyle.*factories. Literals break when the preset, breakpoint or touch size changes. - Text vs keys.
GoStyle.label()/button()show text as written;label_key()/button_key()hold translation keys.list_button(),foldable(),section(),toggle(),checkbox()translate by default — passtranslate = falsefor literal strings. - Surfaces go in a
CanvasLayer, and the owner closes them.GoSurfaceonly emitsclose_requested. Layers: HUD 5 ·GoSheet10 · your popups 50 ·GoDialogs100. - Per-page sheet buttons go through
sheet.add_footer(button)— the nextopen()removes them.open()only hidesfooter(), so children added withfooter().add_child()stay (right for a sheet-wide snackbar, wrong for a Close button re-added on every open). gohud 1.0.3 and older have noadd_footer(): remove your footer children first. - Assemble
GoForm → GoScroll → columnbefore the form enters the tree._readyruns insideadd_child; a scroll added later is never found (no keyboard follow), and%BackButton(Android Back routing) is looked up once there. In code: name the buttonBackButton, add it, setback.owner = formandback.unique_name_in_owner = true, thenadd_child(form). gohud 1.0.3 and older clear that owner when the scroll moves — there, own the whole branch from a holder (owner = holderon every descendant; the templates do this, and it works on every version). - HUD root:
mouse_filter = MOUSE_FILTER_IGNORE. Transient anchors (toasts, prompts, joystick) usereserve_space = false; toasts atTOP_CENTERuseavoid_peers = true. Put aGoStyle.floating()panel behind HUD text that floats over content. awaitdialogs.{placeholders}in the title and body are filled only fromargs(gohud 1.0.3 and older format only the body — build the title string yourself there). Irreversible confirms usedestructive = true(last argument).- Bars use fill tokens (
GoTheme.DANGER_FILL,INFO_FILL,WARNING_FILL,SUCCESS_FILL); text colours look dull as fills on light themes. - Touch: icon-only buttons get a tooltip (
GoStyle.icon_button(icon, action, -1, &"Menu")) — it is also the accessible name. Side-by-sideGoIconButtons /GoSlots needtouch_peers. - Config: after changing a plain
GoConfigfield in code callGoUi.refresh();theme/iconsrefresh themselves.use_preset()clears explicittheme/skin/icons— set overrides after it. - Gameplay input pauses while a window is open:
if GoSurface.is_any_open(): return. - Widgets that draw text inside a non-container parent must not wrap. A
Labelwith autowrap laid out at zero width freezes its minimum height at 1 dp and the text disappears while the panel still paints — this is why table cells, key caps and code cells setAUTOWRAP_OFF. When you place a child by anchors, setoffset_*, notposition:Control.positionis parent-space and ignores the anchors. - Containers are 80% opaque; things you press are not. Panels (
GoSurface/GoSheet/GoDialogs/cards/ HUD panels/alerts/snackbars) fade their face only — never usemodulate.afor this, it fades the text too. Five layers decide the value, most specific first: thealphaargument or field at that call →GoConfig.container_alpha_overrides[GoTheme.BOX_*]→metric_overrides[<kind>_alpha]→GoConfig.container_alpha→ themeGoHud/constants/<kind>_alpha. 🔑 Every one of them is a ratio0.0–1.0(negative = not set), including@exportfields such asdialogs.alphaanddrawer.alpha— same units everywhere, so there is nothing to memorise per widget. 🛑 The two exceptions are the theme's constants andmetric_overrides, which are percent integers (80) because aThemeconstant cannot hold a float. CallGoUi.refresh()after changing it from code. Read the resolved value withGoUi.surface_alpha(variant); apply it to a panel gohud did not build withGoStyle.fade_panel(node)(afteradd_child). AGoSkinsubclass overridingsurface_box/floating_box/alert_boxmust carry thealphaparameter or the script will not parse. 🔑 Drag it before you argue about the number: the opacity lab (examples/gallery/opacity_lab.gd) is hosted by the gallery, the guided tour (chapter 16), the home screen and the medieval example — it shows the four ways to set the value on one screen, over a pattern, because a value check cannot tell you whether the text is still readable. Details:references/theming.md§4. - Never hand-edit gohud's generated themes; recolour through
color_overrides, a Theme copy in your project, a project-localGoThemePreset, or the JSON theme tools (references/theming.md). - Lists must scan — a list the player reads (quests, mail, a shop, a roster) is judged by numbers, not taste.
① Space outside a row > space inside it ≥ space between its lines: rows at least
GAP_SMALL(8) apart (GAP12 when there is room), a two-line row padded 8 above and below and 12 at the sides, its two linesGAP_TINY(4) apart —list_button()already pads its row this way. A cramped panel that used 4 for all three ran its rows together. ② The heading must be a size above the row titles. Rows areROLE_BODY(16) with aROLE_CAPTION(13) second line inMUTED; the heading the player reads first isROLE_SUBTITLE(22). AGoSurfaceorGoSheetshort of height (orcompact) drops its own title tobody, so a list under it becomes one size — put that heading in the body instead. ③ Say a thing once: no header line repeating the first row. ④ Warning colours and warning icons only on rows where something is wrong; a level gate is aLOCKandMUTEDtext (nevermodulate.a— rule 13). A badge that repeats on every row carries nothing — drop it; an icon that states each row's own state (a lock, a check) may repeat. ⑤ A value at a row's end (0 / 1, a price) is a label withSIZE_SHRINK_END—GoStyle.label()expands by default and would split the width with the title.list_button()has no text slot at the end: build that row yourself and keep its padding andGoStyle.fit_content_height(). ⑥ Your spacing does not survive everywhere:GoStyle.form()(everyGoForm) sets each box'sseparationtoGAPunless it carriesset_meta(&"go_own_spacing", true), and awrap_row()forces everything inside it — all the way down — to natural width with wrapping off, so never put a card or a row with an expanding title in one. See it before you build it: the gallery's List rows section opens a lab (examples/gallery/list_lab.gd) with a cramped quest panel next to the same panel built by this rule, each measured from its own nodes. Recipe:references/recipes.md§16.
More traps with their causes: references/pitfalls.md.
4. Minimal screen
extends Control
var dialogs: GoDialogs
func _ready() -> void:
GoUi.use_preset(GoThemePresets.DEFAULT_DARK)
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
theme = GoUi.theme()
RenderingServer.set_default_clear_color(GoUi.color(GoTheme.BACKGROUND))
var form := GoForm.new()
var scroll := GoScroll.new()
var column := GoStyle.column()
form.add_child(scroll)
scroll.add_child(column)
column.add_child(GoStyle.label("Hello gohud", GoTheme.ROLE_TITLE))
column.add_child(GoStyle.button("Open dialog", _on_open, GoStyle.Tone.PRIMARY))
add_child(form)
dialogs = GoDialogs.new()
add_child(dialogs)
func _on_open() -> void:
if await dialogs.confirm("Delete save", "This cannot be undone.", "Delete", "", "", {}, true):
print("deleted")
5. Templates — assets/templates/
Each is a complete, headless-tested script with no scene file. Copy, then connect signals.
| File | Extends | Builds | Public API |
|---|---|---|---|
main_menu.gd |
Control | Title, Continue / New game / Settings / Quit rows, confirm dialogs, a Continue button that becomes a spinner | signals continue_requested new_game_requested settings_requested quit_confirmed · build() · set_loading(waiting) |
game_hud.gd |
CanvasLayer (5) | HP/MP/XP panel, menu button with an unread badge, 4 quick slots, FOLLOW joystick, toast, prompt card, snackbar | signals menu_requested slot_used(index) move_input(vector) · set_health/set_mana/set_experience(v, max) · toast(msg, tone) · await say(msg, actions, tone) · set_unread(count) · ask(title, subtitle, accept_text, accept, decline_text, decline) |
pause_menu.gd |
CanvasLayer (50) | Centred GoSurface, pauses the tree, Escape opens/closes, a key cap that reads the real binding, quit confirm | signals resumed settings_requested quit_to_title_requested · open() resume() toggle() is_open() |
inventory_sheet.gd |
Node | GoSheet with search + category filter, long-press menu on each row, detail page with Back, Use / Drop with Undo | signals item_used(item) item_dropped(item) · items · open() show_list() show_item(item) |
settings_menu.gd |
Control | GoForm, foldable Display/Audio/Controls/Language sections built from GoField (so a row can show its own error), draft + Save/Reset, discard check |
signals closed(saved) settings_changed(values) · settings · save() request_back() reset_to_defaults() |
Wiring them together and more screens (login, shop, quest log, character sheet, dropdown menus, tutorial tour):
references/recipes.md.
6. Preview — /gohud preview (plugin: /gohud:preview)
Run from the user's project folder so their addons/gohud is used (otherwise the bundled copy or a clone):
python3 ${CLAUDE_SKILL_DIR}/scripts/gohud_preview.py <arguments> # Windows: python
If ${CLAUDE_SKILL_DIR} is not expanded, use the first existing path of
${CLAUDE_PLUGIN_ROOT}/skills/gohud/scripts/gohud_preview.py, ~/.claude/skills/gohud/scripts/gohud_preview.py,
.claude/skills/gohud/scripts/gohud_preview.py.
| Arguments | Opens |
|---|---|
(none) / gallery · --preset scifi_dark · --phone · --size 1920x1080 |
Every widget, preset picker, icon set; List rows → Readable lists opens the before/after list lab |
medieval |
Character sheet, satchel, quest journal |
icons |
Buttons from the 1,000-icon library: toolbar, text + icon, toggles, menu rows, segmented, a group, live search |
demo · demo --explore hud |
23-chapter guided tour / one chapter (list shows keys) |
res://ui/main_menu.tscn |
A scene of the user's project, inside that project |
list · --check · --dry-run · --godot PATH |
Keys · headless smoke test · print command · Godot binary |
gohud's examples run in a sandbox project under the user cache, so the user's project is not modified. The script
detaches and prints the process id and log path. Exit 2 = Godot 4.6+ not found or bad arguments; exit 1 = import
or launch error (it prints the error lines). A visible window is what /gohud preview is for; for your own checks
use --check.
7. References — read the one you need
| File | Read when |
|---|---|
references/features.md |
/gohud features, or "what can gohud do" — catalogue of every feature with one-line code |
references/setup.md |
Installing, updating (§7 — add-on and skill, /gohud update), enabling the plugin, every GoConfig field and default, boot order, layers, project settings, headless verification, gohud's tool commands |
references/surfaces.md |
GoSurface (placements, anchored menus, sub-pages), GoSheet, GoDialogs (layouts, destructive, args), GoForm, GoScroll, subclass hooks, which widget to use |
references/hud.md |
GoHudAnchor spots and avoidance, GoBar, GoSlot, GoJoystick, GoIconButton, GoNotice, GoPromptCard, GoCoachMark, composing a HUD |
references/style.md |
Every GoStyle factory signature: structure, text, buttons and tones, inputs, select/dropdown/segmented/tabs, cards, chips, tables, styleboxes, helpers |
references/theming.md |
Presets and resolution order, all tokens, container opacity (§4), overrides, JSON themes (new_theme.py/make_theme.py), skins and dials, custom StyleBoxes, project-local presets, contrast |
references/platform.md |
Icons — the 84 default names, the game set (187) and the icon library (1,000) with GoUi.add_icons(), search and groups, custom icon sets/fonts/folders, localization and RTL, sound and haptics, accessibility, safe area, breakpoints, dp scale, Android Back |
references/recipes.md |
Full screens and wiring: game scene with HUD + pause + inventory, login, shop, quest log, a quest list that scans (§16), character sheet, context menu, tutorial, theme switcher |
references/pitfalls.md |
Symptoms → cause → fix for layout, text, input, theme and lifecycle traps |
Web (same content, with screenshots): overview https://thruthesky.github.io/gohud/ · install https://thruthesky.github.io/gohud/install.html · AI skill https://thruthesky.github.io/gohud/ai.html · widgets https://thruthesky.github.io/gohud/widgets.html · theming https://thruthesky.github.io/gohud/theming.html