Skip to main content
How game UI is built and driven: the widget model (modelled on Unreal’s UMG), the GUI Designer in the editor, and the scripting side - including the automatic binding that turns a menu into “fields and handlers”. A UI is a .wgui layout asset (a tree of widgets, authored in the GUI Designer) shown through a GUI component on an entity. A level can place the component itself (Auto Show draws it as soon as the game starts), or a script creates one with CreateUI("UI/PauseMenu"). The same layout can be shown by several entities at once; each gets its own copy of the widget state, so a script changing one HUD never touches another.

Widgets

Every widget has a name (scripts and the auto-binder find it by name), a colour and opacity, a visibility, an enabled flag, a z-order among its siblings, and a render transform. Children always draw over their parent; siblings draw in z-order, then authoring order.

Layout: canvas slots and boxes

A widget whose parent is a panel (or the screen) has a canvas slot, laid out like UMG’s:
  • Anchors are two points, min and max, as fractions of the parent’s rectangle. Equal anchors pin the widget to one point: its position is an offset in pixels from that point, its alignment says which point of the widget lands there (0,0 top-left, 0.5,0.5 centre, 1,1 bottom-right), and it keeps its size. Different anchors stretch the widget between them on that axis, and offsets (left / top / right / bottom margins) then apply instead of position and size.
  • A text widget with Size To Content takes the size of its text (only the height when word wrap is on).
So a health bar anchored at (0,1)-(0,1) with alignment (0,1) sits in the bottom-left corner of every screen size; a menu panel anchored (0.5,0.5) with alignment (0.5,0.5) is centred; a top bar anchored (0,0)-(1,0) with left/right offsets 0 spans the width. A child of a box ignores anchors. The box gives each child a slot along its axis: auto slots take the child’s size, fill slots share the remaining space in proportion to their weight; each slot has padding (left, top, right, bottom) and a horizontal / vertical alignment (left / centre / right / fill) inside it. Boxes nest, and a box inside a panel is itself placed with a canvas slot.

Visibility and input

Buttons, check boxes and sliders default to Visible; everything else to Self Hit Test Invisible, so a HUD never swallows a gameplay click while a menu does. Disabled widgets draw at half opacity and ignore the pointer. SetVisible(false) from a script hides the widget and SetVisible(true) puts the visibility it had back.

Render transform

Translation, scale, angle (degrees) and pivot (a fraction of the widget’s rect) transform the widget and its children on screen; opacity multiplies down the tree. Hit-testing follows the transform, so a rotated button is clicked where it is drawn. Layout is computed untransformed (a rotated widget in a box still takes its untransformed slot).

The GUI Designer

Double-click a .wgui in the content browser. The window has the hierarchy on the left, the canvas in the middle and the details on the right; the toolbar adds widgets (+ Add Widget, under the selected one), duplicates and deletes, picks the preview resolution, fits / zooms, and saves (Save, or the editor’s Save All once the layout has unsaved changes). Canvas. The layout is laid out and drawn by the runtime’s own code, at the chosen preview size, so what you see is what the game draws (fonts included). Click selects, drag moves, the eight handles resize; Alt+click cycles through the widgets stacked under the cursor, and a plain click keeps the selection when it is somewhere in that stack. Arrow keys nudge by a pixel (Shift: ten), Delete deletes, Ctrl+D duplicates, Escape deselects. The mouse wheel zooms around the cursor, middle-drag pans, Fit goes back to fit-to-window. The move gizmo. The selected widget always shows a gizmo drawn on top of everything: a white square at its centre moves it freely, the red arrow moves it along X only, the green arrow along Y only. The gizmo and the resize handles take priority over whatever is under the cursor, so a widget that sits under a larger one (a full-screen damage flash, a background panel) can be dragged without selecting the one on top. Moving a stretched widget shifts its offsets; moving a box child does nothing (the box places it - reorder it in the hierarchy). Hierarchy. Drag a row onto another to reparent it (dropping below the tree makes it a root); the right-click menu adds children, duplicates, deletes, changes z-order and makes a widget a root. The o toggle hides a widget in the designer only (the game still shows it); the L toggle locks it, so canvas clicks pass through it - the other way around the overlap problem. Details. Slot (Canvas Panel) has UMG’s anchor picker (nine points, the stretch rows / columns, full stretch) - picking a preset keeps the widget where it is on screen and converts its position or offsets - plus position / size or offsets, alignment, z-order and size-to-content. Slot (Horizontal/Vertical Box) replaces it for box children. Appearance holds the kind-specific look, Behavior the visibility / enabled / designer flags, Render Transform the transform and opacity. Layouts are saved with Save, and by the editor’s Save All while they have unsaved changes; older .wgui files (single anchor, hit_test flag) load unchanged and are written in the new form the next time they are saved.

Scripting

Scripts see a UI as the entity that carries the GUI component, and widgets as handles (Widget@ and its kinds). Names are PascalCase as in UMG.
Widget lookup by name is exact first, then case-insensitive ignoring _, - and spaces, so FindText(hud, "health_text") finds HealthText.

Auto-binding widgets to a behaviour

A behaviour that calls CreateUI (or BindWidgets(ui) for a UI it didn’t create - one placed in the level) gets its widgets by declaring them, the way UMG’s BindWidget and Blueprint event nodes work:
Events and their handler signatures: OnClicked_X(), OnPressed_X(), OnReleased_X(), OnHovered_X(), OnUnhovered_X() (any widget that takes the pointer), OnCheckStateChanged_X(bool), OnValueChanged_X(float). A handler may also take no argument. The explicit BindOnClicked("MethodName") family still works and accepts a global function name too. CreateUI binds silently (a field with no matching widget stays null, so a shared class works with several layouts); BindWidgets reports every miss and every kind mismatch with a warning. Handlers run with the full script API - self() is the entity whose behaviour bound them - after the frame’s UI update, so a handler may hide or destroy the UI it came from.

Widget API

Every kind has the Widget methods:
plus per kind:
cast<TextWidget@>(w) and friends convert a Widget@ to its kind (null when it isn’t one). Handles are valid for as long as the UI entity exists; keep them in fields, never across DestroyUI. Changes made through the API affect that UI instance only - the .wgui on disk is never touched. Anything set in the designer can be changed at run time this way, since the instance holds its own copy of the layout.

Input modes

Unchanged from before and documented in the Scripting reference: SetInputModeGameOnly(), SetInputModeGameAndUI(ui), SetInputModeUIOnly(ui), GetInputMode(), GetFocusedUI(), SetFocusedUI(), IsPointerOverUI(), GetHoveredWidget(). In the two UI modes every shown UI is hit-tested topmost-first; a widget being pressed captures the pointer until release; a slider follows the pointer while held.

Files