Build your first Tick UI screen¶
A guided walk-through of building a small but realistic screen from scratch. By the end, you have a working settings panel with a heading, a scrollable list of toggle rows, hover animations, and a modal confirmation dialog.
Tutorials are learning-oriented. This page walks through the steps in order; each step builds on the previous one. If you want the procedural recipe for a single feature, read the matching task page instead.
What you need¶
Before starting, complete:
- Install Tick UI.
- Set up Tick UI. The
tutorial assumes you have a
MyGameUIMonoBehaviour with an emptyDoUIbody, attached to a GameObject with the URP or BIRP injector wired.
The finished screen looks like a centred dark panel with a heading at the top, three toggle rows in the middle, and an Apply button at the bottom. Clicking Apply opens a confirmation modal.
Step 1: draw the panel background¶
Open MyGameUI.cs and edit DoUI. Replace the body with:
protected override void DoUI(UI ui)
{
ui.BeginFrame(new Vector2Int(Screen.width, Screen.height));
ui.Fill(Color.clear);
using (ui.Auto().Center(480, 360))
{
ui.FillRounded(UI.FromHex("#1A1A1A"), 12);
}
ui.EndFrame();
}
Press play. A centred dark rounded panel appears. The
ui.Auto().Center(480, 360) scope pushes a centred rect; the
FillRounded call fills it with a rounded background.
For the Auto() scope and the Center helper, refer to
Layout.
Step 2: add a heading¶
Inside the Auto().Center(480, 360) scope, add a top strip with
text:
using (ui.Auto().Top(48).PadHorizontal(20))
{
ui.Text("Settings", "roboto_mono", 24, Color.white,
TextAlign.MiddleLeft);
}
The Top(48) push gives a 48-pixel-tall strip at the top of the
panel; PadHorizontal(20) insets it left and right by 20 pixels;
Text renders the heading.
Press play. A Settings heading appears at the top of the panel.
Step 3: add a row of toggles¶
Below the heading, lay out three toggle rows. Use a series layout so the rows share equal height:
using (ui.Auto().Top(48).Bottom(56).Pad(20))
{
ui.PushSeriesVertical(40, 8);
{
DrawToggleRow(ui, "Vsync"); ui.Next();
DrawToggleRow(ui, "Show FPS"); ui.Next();
DrawToggleRow(ui, "Reduced motion");
}
ui.Pop();
}
Top(48).Bottom(56) carves out the middle of the panel between
the heading and the future Apply button; Pad(20) insets that
middle area. PushSeriesVertical(40, 8) lays out three
40-pixel-tall rows with an 8-pixel gap.
Add a DrawToggleRow helper to the same MonoBehaviour:
private void DrawToggleRow(UI ui, string label)
{
ui.FillRounded(UI.FromHex("#2A2A2A"), 5);
using (ui.Auto().PadHorizontal(12))
{
ui.Text(label, "roboto_mono", 14, Color.white,
TextAlign.MiddleLeft);
}
}
Press play. The three rows appear stacked inside the panel.
For the schema layouts used here, refer to Schemas.
Step 4: animate the row hover¶
Add hover feedback by wrapping each row in a PushAnimate scope.
Update DrawToggleRow:
private void DrawToggleRow(UI ui, string label)
{
var ctx = ui.PushAnimate("row")
.Init().Color().Set(UI.FromHex("#2A2A2A")).Done()
.Enter().Color().Blend(UI.FromHex("#2A2A2A"),
UI.FromHex("#3A3A3A"), 0.15f).Done()
.Leave().Color().Blend(UI.FromHex("#3A3A3A"),
UI.FromHex("#2A2A2A"), 0.15f).Done()
.BoolWhileTrue("hover", ui.IsHovered())
.Apply();
ui.FillRounded(ctx.GetColor(), 5);
using (ui.Auto().PadHorizontal(12))
{
ui.Text(label, "roboto_mono", 14, Color.white,
TextAlign.MiddleLeft);
}
ui.PopAnimate();
}
Press play. Each row tints darker on hover and back on leave.
At this point all three rows share the same context ID
("row"). Wrap the call site in PushID to give each row its
own state:
ui.PushID("vsync"); DrawToggleRow(ui, "Vsync"); ui.PopID(); ui.Next();
ui.PushID("show_fps"); DrawToggleRow(ui, "Show FPS"); ui.PopID(); ui.Next();
ui.PushID("reduced_motion"); DrawToggleRow(ui, "Reduced motion"); ui.PopID();
For the identity model, refer to IDs. For the animation context lifecycle, refer to Animation.
Step 5: add an Apply button¶
Reserve the bottom 48 pixels of the panel for the button. Inside
your existing Auto().Center(480, 360) scope:
using (ui.Auto().Bottom(48).PadHorizontal(20))
{
ui.FillRounded(UI.FromHex("#23C9FF"), 5);
ui.Text("Apply", "roboto_mono", 14, Color.white,
TextAlign.MiddleCenter);
if (ui.Button("apply"))
ui.ShowModal("confirm_apply");
}
Press play. The blue button appears. Clicking it does nothing visible yet; the next step builds the confirmation modal.
For the visual-then-Button idiom, refer to
Make a button.
Step 6: add a confirmation modal¶
Outside the Auto().Center(480, 360) scope (still inside DoUI,
between BeginFrame and EndFrame), declare the modal:
if (ui.BeginPanel("confirm_apply", screenRect, new PanelConfig
{
Hidden = true,
BackdropColor = new Color(0, 0, 0, 0.75f),
}))
{
using (ui.Auto().Center(320, 140))
{
ui.FillRounded(UI.FromHex("#1A1A1A"), 8);
using (ui.Auto().Top(48).PadHorizontal(20))
{
ui.Text("Apply changes?", "roboto_mono", 16, Color.white,
TextAlign.MiddleCenter);
}
using (ui.Auto().Bottom(40).PadHorizontal(20))
using (ui.PushSplitHorizontal(2, 8))
{
using (ui.Auto())
{
ui.FillRounded(UI.FromHex("#2A2A2A"), 5);
ui.Text("Cancel", "roboto_mono", 14, Color.white,
TextAlign.MiddleCenter);
if (ui.Button("cancel"))
ui.HidePanel("confirm_apply");
}
ui.Next();
using (ui.Auto())
{
ui.FillRounded(UI.FromHex("#23C9FF"), 5);
ui.Text("Apply", "roboto_mono", 14, Color.white,
TextAlign.MiddleCenter);
if (ui.Button("confirm"))
{
ApplySettings();
ui.HidePanel("confirm_apply");
}
}
}
}
ui.EndPanel();
}
Add a placeholder ApplySettings method that logs:
Press play. Click Apply on the main panel; the confirmation modal appears with a dimmed background. Cancel hides it; Apply logs and hides it.
For the modal pattern, refer to Show a modal. For the input-priority rules that make the modal block the panel below, refer to Input and focus.
What you built¶
A complete settings screen with a heading, three animated rows,
an Apply button, and a confirmation modal. Roughly 80 lines of
DoUI code.
You used:
- The
Auto()scope for layout pushes. - Single-rect helpers:
Top,Bottom,Center,Pad,PadHorizontal. - Schema layouts:
PushSeriesVertical,PushSplitHorizontal. Button,PushAnimate, andBeginPanelwithModal.PushIDfor distinct row state.
Additional resources¶
- Read the Concepts section for the model behind everything you used.
- Browse the Gallery sample (Package Manager > Tick UI > Samples > Gallery) for more worked examples.
- Add a scrollable list to your panel when you have more rows than fit.
- Add a textbox for editable fields.