Game UI

A .ui document running in Play Mode

Game UI in Turian is authored like the web or Unity's UI Toolkit: a .ui XML document describes the elements, a .uss style sheet describes how they look, and a C# UiController or data bindings bring them to life. It is drawn by Guinevere, the same toolkit that draws Turian Studio.

Putting a document on screen

  1. Create Assets/UI/menu.ui and Assets/UI/theme.uss (below).
  2. Add a node, then Add Component → UI → UI Document.
  3. Drop menu.ui on its Document field.
Property Meaning
Document The .ui document to render.
StyleSheets Extra .uss sheets applied on top of the document's own.
Mode ScreenSpaceOverlay (drawn over the scene) or WorldSpace (on a quad in the scene).
SortOrder Draw order among screen-space documents.
ScaleMode, ReferenceResolution Constant pixel size, or scale with the screen from a design resolution.
PanelSize Pixel size of a world-space panel.

World-space documents render in Studio's views and CLI renders; exported games do not draw them yet. Add a UI Raycaster component to make one clickable.

A document

<?xml version="1.0" encoding="utf-8"?>
<UI xmlns="https://turian.dev/ui" controller="Usercode.MainMenu">
  <Style src="Assets/UI/theme.uss" />

  <VisualElement name="root" class="screen">
    <Label class="title" text="{Title}" />
    <Button name="play" text="Play" click="OnPlay" />
    <Toggle name="music" label="Music" value="{Music, mode=TwoWay}" />
    <Button name="quit" text="Quit" click="OnQuit" />
  </VisualElement>
</UI>
  • The root is always <UI>; it holds <Style> sheets, optional <Template>s, and one visual element.
  • Common attributes: name (unique id), class (style classes), style (inline declarations), tooltip, enabled.
  • A leaf element's inner text becomes its text: <Label>Main Menu</Label>.

Elements

Element Notes
VisualElement Generic container; lay it out with flex-direction, gap, sizes and alignment.
Label Text.
Button text; raises click.
Image src, width, height.
ImageButton image-normal, image-hover, image-pressed, nine-slice="l,t,r,b"; raises click.
TextField value, placeholder; raises value-changed.
Toggle value, label; raises value-changed.
ScrollView Scrolls its children vertically.
Tabs / Tab <Tab header="…"> per page; raises changed.

Give stateful controls (TextField, Toggle, Tabs) a name so their state is kept between frames.

Templates and lists

<Template name="StatRow">
  <VisualElement class="row">
    <Label text="{label}" />
    <Label binding-text="{path}" />
  </VisualElement>
</Template>

<Instance template="StatRow" label="Gold" path="Player.Gold" />

<ScrollView>
  <Repeat items="{Inventory}" as="item">
    <Label binding-text="{item.Name}" />
  </Repeat>
</ScrollView>

Styling with .uss

--accent: #4a90e2;

.screen  { flex-direction: column; gap: 12; padding: 24; align-items: center; }
.title   { font-size: 32; color: #ffffff; text-shadow: #000000 2 2; }
Button   { width: 220; height: 44; border-radius: 8; background-color: #2a2f3a; }
Button:hover { background-color: var(--accent); }
#quit    { background-color: #6b2b2b; }
  • Selectors: Type, .class, #name, *, compounds such as Button.primary, and groups a, b. Descendant and child combinators are not supported.
  • States: :hover, :active, :focus, :disabled.
  • Variables: declare --name: value; at the top level and use var(--name).
  • Layout: flex-direction, flex-wrap, flex-grow, gap, width/height (pixels or %), min/max sizes, padding, margin, align-items, justify-content, align-self.
  • Box: background-color, border-color, border-width, border-radius.
  • Text: color, font-size, font-family (a project .ttf/.otf or a system font), text-outline, text-shadow, text-inner-shadow, text-gradient.

Code-behind and data binding

namespace Usercode;

public sealed class MainMenu : UiController
{
    public string Title => "My Game";
    public bool Music { get; set; } = true;

    public void OnPlay() => Log.Logger.LogInformation("Play pressed");
    public void OnQuit() => Log.Logger.LogInformation("Quit pressed");
}
  • Every public, parameterless void method is an event handler, referenced by name from click, value-changed, changed, submit, activated or selection-changed.
  • {Path} in an attribute binds it to a property of the controller (or of a separate data context), re-read every frame — no change notifications needed. binding-text="{…}" is the explicit form, and a <Bindings> block can group them.
  • Modes: OneWay (default), TwoWay (text fields and toggles write back), OneTime.
  • Converters: {Health | percent} — built in are not, percent, thousands, upper and lower; register your own with ValueConverters.Register("name", converter).
  • Override OnBind, OnUpdate(float deltaTime) and OnUnbind for setup, per-frame logic and teardown.

Localized text

Label text is looked up in the project's string tables automatically; text-key names an explicit key. See Localization.

Previewing without the game

The CLI renders a document to a PNG without a GPU, binding JSON as its data:

turian-cli ui --file Assets/UI/menu.ui --out menu.png --data '{"Title":"Preview","Music":true}'

Parse errors report the line and column of the offending XML.


← All docs Edit this page