Parameterized reusable subtrees.
UI prefabs
A UI prefab is a reusable HUD subtree. Instances share its design and supply parameters or local overrides. The editor and runtime use the same loader, including prefabs installed through the package manager.
The prefab document
{
"format_version": 1,
"id": "ui-prefab://controls/menu_button",
"parameters": {
"label": {"type": "string", "default": "Continue"},
"hint": {"type": "string", "default": ""},
"disabled": {"type": "boolean", "default": false}
},
"root": {
"id": "menu_button",
"type": "container",
"size": [320, 90],
"stack": "column",
"alignment": "stretch",
"children": [
{"id": "button", "type": "button", "text": "${label}", "disabled": "${disabled}", "size": [320, 44]},
{"id": "hint", "type": "label", "text": "${hint}", "size": [320, 24]}
]
}
}
Save this under ui/controls/menu_button.ui.prefab.json. Keep format_version and the stable ui-prefab:// ID. Parameter types are string, number, integer, boolean, array and object. Omitted arguments use their declared default. Unknown arguments, missing required arguments and type mismatches reject the instance.
One overrides map
{
"format_version": 1,
"children": [{
"id": "confirm",
"prefab": "ui-prefab://controls/menu_button",
"arguments": {"label": "Confirm", "hint": "Enter to continue"},
"overrides": {
"$root": {"size": [400, 100]},
"hint": {"text_color": "#A3B5C5"}
}
}]
}
argumentsfills the prefab's declared parameters.overridesis one map from a target to its changes.$roottargets the root; other keys are stable prefab-local descendant IDs.- A property object changes that node's values. These edits affect this instance, leaving the shared source unchanged.
- A
childrenarray appends locally authored content under that target. - A
nulltarget removes an inherited descendant and its subtree. Remove the instance itself to delete its root.
"overrides": {
"$root": {"size": [400, 140]},
"hint": null,
"button": {"text": "Launch"}
}
Unknown targets are rejected, not ignored. A local addition looks like this:
"overrides": {
"$root": {
"children": [{"id": "extra_hint", "type": "label", "text": "Local content", "size": [320, 24]}]
}
}
Local additions can contain ordinary nodes or further prefab instances. Their IDs belong to the host document, exactly as written, and must not collide with inherited IDs. Moving an addition between instances preserves its ID. Nesting ordinary children does not create another namespace. $root is reserved as an override target.
Identity and ownership fields (id, prefab, arguments, overrides) cannot be replaced through a property patch. A null property removes that inherited field before defaults and styles apply. Removing the override entry instead restores inheritance; Reset does this.
Stable IDs and nested prefabs
| Local ID | Instance confirm | Instance back |
|---|---|---|
| Root | confirm | back |
button | confirm.button | back.button |
hint | confirm.hint | back.hint |
A nested prefab instance named settings gives its callsign child the local ID settings.callsign in the outer prefab. Use that complete local ID as the override key. IDs remain stable when sibling order changes. Duplicate IDs, missing targets, missing sources and reference cycles reject the candidate document.
Edit instances in the HUD view
Select a root or generated child in Hierarchy or the HUD canvas. Normal Inspector edits create instance overrides. Parameters display inherited defaults even when the instance has no explicit arguments; arrays and objects use the structured value editor.
Instance overrides provides per-property Reset and Reset all node overrides. Removed descendants appear on the owning instance with a Restore action. You can add controls to inherited containers through UI Palette and delete inherited content locally. Changes use the normal validated Undo/Redo, Save and recovery paths.
Layout choices such as docking and explicit anchors take precedence over inherited layout. Canonical/shorthand pairs such as position/at do not mask one another. Selecting a node alone does not create a position override.
Editing a scene's linked HUD edits that HUD document. Entity-prefab overrides belong to their instance in the scene document. Opening the shared prefab source is a separate choice that affects all instances.
Reparent and unpack
Drag a node in Hierarchy onto its new parent, including containers in other prefab instances. Local nodes move in the authored source; inherited nodes record a parent override. IDs and prefab links remain stable. Parent values and action targets in overrides use host-document IDs, such as other.settings.display_page. Cycles are rejected without changing the document.
Deleting an inherited container removes its current descendants; a child moved out of that container survives. Source ownership remains separate from parenting: deleting an entire instance also removes the inherited content it supplies, even if that content was moved elsewhere.
Choose Unpack Prefab in Hierarchy or Inspector to turn the owning instance and its nested prefabs into ordinary editable nodes. It preserves IDs, current authored overrides, actions and asset references. Temporary tab-preview visibility is not baked. Reparented content owned by other instances keeps its own links. Unpack is one Undo step and does not change the shared source. Entity prefabs provide the same explicit Unpack command in scene and prefab views.
Switch tabs while authoring
Tab buttons can use declarative action_effects to show a page and hide the others. Declare them on a container node; the effects stay with that subtree. For example:
{
"id": "settings", "type": "container", "dock": "fill",
"action_effects": {
"audio": {"show": ["audio_page"], "hide": ["display_page"]},
"display": {"show": ["display_page"], "hide": ["audio_page"]}
},
"children": [
{"id": "audio_tab", "type": "button", "text": "Audio", "action": "audio", "size": [120, 40]},
{"id": "display_tab", "type": "button", "text": "Display", "action": "display", "at": [130, 0], "size": [120, 40]},
{"id": "audio_page", "type": "panel", "at": [0, 50], "size": [400, 240]},
{"id": "display_page", "type": "panel", "at": [0, 50], "size": [400, 240], "visible": false}
]
}
Clicking a tab in the HUD canvas selects it and previews its declarative action. The editor runs no gameplay scripts. You can then select, change, add or remove content on that page. Temporary preview visibility stays outside the saved document and Undo history, survives content edits, and resets on reopening or through Reset preview state. Inspector visibility fields still describe authored defaults.
Prefab expansion namespaces locally declared action names and node references. A nested prefab and its parent may contribute show/hide effects to the same action: all hides run before all shows. Global application actions without a local declaration keep their names. Hud.apply_action(qualifiedName) invokes the same effects at runtime. Locally owned actions follow runtime clones and disappear with their owner.
The Orbit UI kit uses this for its tabs and settings pages. Lua-only custom interactions require an equivalent declarative action for authoring preview.
Installed packages and validation
Package manifests declare UI-prefab sources in files. Installed prefabs appear in UI Palette and resolve through verified locked content. Nested package prefabs must also be declared. Local project sources take precedence; conflicting IDs in installed packages fail. Author stable URIs, never cache paths.
demi validate path/to/game
This validates reachable sources and prefab expansion. Fix reported errors before shipping.
Development migration
Older root-only overrides: {"size": [400, 100]} becomes overrides: {"$root": {"size": [400, 100]}}. Use the same map for descendants. Repository examples and Orbit 1.1.0 use this model. Update Orbit through the package manager when updating the engine; older archives remain immutable.