Skip to content

Typed HUD bindings ​

Bind UI to declared component values or GameState through the UI Inspector's Data bindings card. Use [Computed] for a scalar derived from private state, an immutable definition record, or several existing values. The UI reads that value directly; displaying a price does not require another mutable price field.

Author the shared balance asset ​

In Variables, create Coins, Number, Session, default 50. Save & Apply this project Behavior Component, then attach Repair Station to a scene object named Station. The script symbol Game.Coins comes from the declared variable; use completion if an existing variable has a different script alias.

csharp
record RepairBalance { float RepairCost; float RepairAmount; }

class RepairStation : Component {
    [Property] DataAsset<RepairBalance> Settings;
    [Property] public float Hp = 50;
    RepairBalance Balance;
    bool ready;

    async void Start() {
        ready = false;
        Balance = await Definitions.Load<RepairBalance>(Settings);
        ready = Balance.RepairCost >= 0 && Balance.RepairAmount > 0;
    }

    [Computed] public bool Ready() { return ready; }

    [Computed("repairCost")] public float RepairCost() {
        if (!ready) return 0;
        return Balance.RepairCost;
    }

    [Computed] public bool CanRepair() {
        return ready && Hp < 100 && Game.Coins >= RepairCost();
    }

    public void Repair() {
        if (!CanRepair()) return;
        Game.Coins -= RepairCost();
        Hp = Math.Min(100, Hp + Balance.RepairAmount);
    }
}

Open Script Studio → Definitions, choose RepairBalance, and click New definition. Name it BasicRepair, set RepairCost = 20 and RepairAmount = 25, then click Save definition revision. Select BasicRepair.opdef in Station's Settings component property. Double-click that asset in the library whenever you need to edit its typed fields.

Settings is an asset handle. Balance is the immutable revision captured by this instance when loading finishes. Hp and Coins change during gameplay. Saving another BasicRepair revision does not rewrite this captured Balance; restart Play or explicitly load again to capture new tuning. An unsuccessful load leaves Ready false and reports a script task error. The Ready guard keeps the button unavailable during loading and after invalid tuning or a failed load.

Connect the UI ​

Open the UI workspace and select a text element or button. For each target in Data bindings, set Source → Component, Object → Station, and Component → Repair Station, then choose the appropriate Value. Labels marked (computed) are checked getters; opening the picker does not call them.

UI element / targetValueFormatTemplate
Price text / TextRepair Cost (computed)Integer (rounded){value} coins
Price text / VisibleReady (computed)Raw—
Repair button / LabelRepair Cost (computed)Integer (rounded)Repair · {value} coins
Repair button / EnabledCan Repair (computed)Raw—
Health bar / ValueHpRaw—

Set the health bar's authored Maximum to 100. Bind its Maximum too if your component exposes a changing limit. A bar uses numeric Value/Maximum; do not format its value as percent text. For a separate percentage label, expose a computed ratio, choose Percent (ratio × 100), and use a text target.

For a currency label, choose Source → GameState, Value → Coins, Format → Integer (rounded), and Template → {value} coins. The picker stores the variable's stable ID, so a display-name change does not select an unrelated variable. The example's Game.Coins is the script-facing symbol for that same variable.

Connect the repair button's click through its typed action to Station's Repair method; see direct UI actions. Data bindings read values; action bindings issue commands. Keep the CanRepair check inside Repair as well, since the current balance can change after the UI reads it.

With the example values, Play starts at 50 HP and 50 Coins. After loading, the label reads “Repair · 20 coins” and the button enables. The first repair produces 75 HP and 30 Coins; the second produces 100 HP and 10 Coins, disabling the button. No per-frame copying into UI variables is needed.

Available values and formatting ​

Sources are scalar component fields offered by the picker, [Computed] scalar values, and Number/Boolean/Text GameState fields. The current picker does not traverse Balance.RepairCost or other nested record paths. Expose the intended scalar through [Computed] instead. It does not bind arbitrary method calls, object references, maps or lists.

FormatBehavior
RawPreserves the scalar; text targets convert it to text.
Integer (rounded)Rounds a numeric source to a whole number.
Percent (ratio × 100)Rounds a numeric ratio times 100 and appends %; 0.75 becomes 75%.
TextConverts a scalar to text.

Templates apply to text and button-label targets and replace literal {value}. {value} coins is a template; it is not a script expression. Only formats compatible with the target are offered. Enabled/Visible require booleans; Value/Maximum/Minimum/Opacity require numeric output. Choose Source → None to remove a binding. Existing Expression bindings remain selectable.

Computed contract and identity ​

A computed method must be public, synchronous, have zero parameters, and return int, float, bool or string. [Computed] uses the method name as its binding key. [Computed("repairCost")] gives it an explicit stable key, allowing the method's source name to change without changing the saved UI identity. Script Studio's semantic rename preserves an existing computed key; keep explicit keys stable when manually renaming code.

Computed methods may read their component's fields, captured records, GameState and Transform values, use deterministic Math, call other computed methods or pure modules, and assign local variables. They cannot mutate gameplay state, emit events, invoke actions, await, load definitions or query Scene objects. They are display data and do not become Flow actions. Perform loading and writes in lifecycle methods or commands, then expose the resulting scalar read.

Missing sources and debugging ​

Component bindings retain the owning scene ID, object ID, component ID, member key and scalar type. During Play they resolve the current runtime object, rather than retaining an authoring instance. A renamed display label does not require rebinding; a removed/replaced source or changed type does.

The Inspector reports missing, disabled, out-of-scene and type-changed sources. Repair the selection in Data bindings. An unavailable or invalid read clears text to an empty string, numeric targets to zero and boolean targets to false; it does not display the previous valid value. Any failed typed binding on a widget, including its label, also dims it and blocks interaction until the source recovers or the binding is repaired. This failure state is runtime-only. A false Enabled binding blocks pointer and keyboard activation, including sliders. Ready/CanRepair booleans make loading and gameplay availability explicit without relying on display text.

Bindings persist with the UI document and participate in undo/redo and game export/import. An .opdef selected on the component exports as a referenced asset; its nested asset dependencies travel with it. See the typed gameplay workflow for reusable wave and upgrade definitions and their state lifetimes.