Changing a view at runtime
A view carries its layout and its stylesheet as integer tables and re-runs them in Udon. That is what lets a baked panel change shape in-world without parsing anything there.
Most of the time you set a binding and you are done. This page is everything bindings cannot express.
Addressing an element
Generated E_ID constants are element indices. The selector helpers return the same integers, so code that is not written against one specific view still works:
int card = GetElement("#card"); // -1 when there is no such id
int[] chips = GetElements(".chip"); // by effective runtime class
int[] buttons = GetElements("button"); // by tagShowing and hiding
SetActiveElem(E_DETAILS, false); // hides it and frees its space; siblings move up
SetDisplay(E_DETAILS, DISPLAY_NONE);This is the code equivalent of if="{flag}". Use the binding when the condition is part of your state, and SetActiveElem when it is not.
Text and icons
SetText(card, "any string"); // escaped for you
SetIcon(GetElement("#close"), "check");
SetBackground(card, "Rounded16");
SetMaterial(card, "GlowHot"); // or null to clear
string s = GetText(card);An icon, sprite or material must be known to the view: either used somewhere in its markup or sheets, or preloaded on the root element.
<view name="Panel" icons="check star" sprites="Rounded16" materials="GlowHot" utilities="p-4 p-8">Anything not known that way cannot be swapped in, because the asset reference was never baked.
Text you set by hand is overwritten on a language change
_Retranslate repaints every keyed text. Re-apply your own strings in OnRetranslate.
Classes
Every class any of the view's sheets defines gets a C_NAME constant:
AddClass(E_CARD, C_WARM);
RemoveClass(E_CARD, C_WARM);
ToggleClass(E_CARD, C_WARM);
bool on = HasClass(E_CARD, C_WARM);
AddClassNamed(E_CARD, "warm"); // by name, for generic code
SetClasses(E_CARD, "card warm"); // replaces the API-added setChanging a class re-resolves that element through the cascade and re-resolves its descendants, so inherited properties follow, and applies each changed property through the element's own transition.
You can also mix bindings into the class attribute, which is often tidier than calling AddClass:
<panel class="card {tone}"/>Tone = "warm"; // now class="card warm"
Tone = ""; // back to just cardStyle values
Set one property on one element, without a class:
SetOpacity(card, 0.5f);
SetColor(card, Color.red); // text colour
SetBackgroundColor(card, c);
SetIconColor(card, c);
SetScale(card, 1.1f);
SetFontSize(card, 22f);
SetTextAlign(card, 1); // 0 left, 1 center, 2 right
SetStyleValue(card, PROP_GAP, new Vector4(12, 0, 0, 0)); // any cascade property by idA value set this way holds until the next class change on that element re-resolves it.
Elements with an id get a CanvasGroup at bake, so SetOpacity always has something to fade.
The declarative equivalent is a bound style value, which is usually what you want:
<panel class="fill" style="width: {progress}"/>
<panel id="stage" style="flex-direction: {dir}; justify: {justify}; gap: {gap}"/>Progress = "50%"; // lengths and enums are strings
Gap = "16";Position and size
SetPosition(E_BADGE, -6f, -6f, float.NaN, float.NaN); // left, top, right, bottom; NaN = unset
SetSize(E_CARD, 120f, 40f);SetPosition needs the element to be position: absolute (or anchored).
Read the solved rectangle back:
float x = LayoutX(E_CARD), w = LayoutW(E_CARD);Batching
Every mutation marks the layout dirty; solves are coalesced to one per frame. For a burst:
SetAutoRelayout(false);
/* many changes */
SetAutoRelayout(true); // one solveA setter schedules the solve for the next frame; Relayout() runs it now if anything is dirty, RelayoutNow() runs it now regardless.
Creating elements
Nothing is instantiated in-world. A <template> bakes max parked copies; CreateElement links one under a container. A pool and a <slot> are the same idea with less markup.
<view name="Chat" width="400" height="300" pool="text:8 panel:4">
<panel id="log" class="card"/>
<template id="line" max="32">
<panel class="row">
<icon name="user"/>
<text text="{item.who}" class="muted"/>
<text text="{item.msg}"/>
</panel>
</template>
<slot id="cards" views="PlayerCard" max="8"/>
</view>int line = CreateLine(E_LOG); // generated per template; -1 when all 32 are in use
SetLineWho(line, "you"); // {item.x} setters take the created element
SetLineMsg(line, "hello");
int note = CreateElementByTag("text", E_LOG, "muted", 0); // from the pool, with classes, at index 0
SetText(note, "(new message)");
AuiView card = CreateCardsPlayerCard(E_LOG); // a parked nested view; cast to PlayerCard
MoveElement(line, E_LOG, 0); // reorder or reparent
DestroyElement(note); // back to the poolRules worth knowing:
- A
<template>must be a direct child of<view>; instances can go under any container. - Only
panel,text,iconandspacercan be pooled. Anything with wiring needs a template. - Handlers inside a template receive the instance element, and
InstanceElem(T_LINE, row)/InstanceRow(T_LINE, elem)convert between the two. CreateElementreturns-1when the pool is exhausted. Always check.- Batch a burst of creates with
SetAutoRelayout(false).
Modals
A <modal> sits directly under <view>, takes no space in the flow, and draws over a dimming backdrop.
<modal id="confirm" enabled="{asking}" onclose="ResetCancelled">
<text key="settings.reset.sure"/>
<panel class="row">
<button onclick="DoReset" key="yes" class="primary"/>
<button onclick="CloseConfirm" key="no"/>
</panel>
</modal>public void AskReset() { Asking = true; } // the binding opens it
public void DoReset() { /* ... */ CloseConfirm(); }
OpenConfirm(); CloseConfirm(); bool open = IsConfirmOpen();
Back(); // closes an open select, else the top dismissable modal; true if it consumedA click on the backdrop closes it unless dismissable="false". Style the backdrop with modal::backdrop { background: rgba(0,0,0,0.6); }. Later modals stack on top; a select's dropdown always draws above a modal.
Showing the view itself
Show(); Hide(); Toggle();
ShowInstant(); HideInstant();
bool v = IsVisible();The transition is the show style property or the show attribute on <view> (none | fade | fade-scale | fade-slide). Hide() leaves an open modal open behind the scenes, so the view returns the way you left it.
Two things that cannot change in-world
Udon has no API for them:
- A text's font weight. A class that sets one is applied at bake; if it could only ever be reached at runtime you get AUI212.
- New materials.
shader()materials are created at bake time. You can swap between baked ones withSetMaterial.
Cost
A roughly 40-element view solves in about a millisecond in the editor's Udon interpreter, once per frame at most, and only when marked dirty. A view nothing touches costs nothing.
Next
- Theming for changing the palette everywhere at once.
- View API for the complete signature list.
- The gallery for a worked example of templates and modals.