> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/ui/fundamentals.md).

# UI Fundamentals

***

Adding UI to your game requires writing code, but once you learn a few patterns you can build clean, scalable interfaces quickly. This guide walks you through a step-by-step tutorial that ends with a complete dialog example.

{% hint style="info" %}
All UI for a player must be drawn inside that player's `ao_late_update` call stack and wrapped with `is_local_or_server()`. This prevents UI from appearing for the wrong client.
{% endhint %}

At any time, you can jump to [UI Reference](/ui/ui-reference.md) for more patterns and deeper details.

### Step 1: Create a UI Entry Point

All interactive UI should be drawn from the local player's `ao_late_update`:

```csl
Player :: class : Player_Base {
    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() {
            draw_ui(this);
        }
    }
}
```

### Step 2: Start From a Screen Rect

Screen UI should always be derived from `UI.get_safe_screen_rect()` or `UI.get_screen_rect()`. Remember: (0, 0) is the bottom-left and Y grows upward.

```csl
draw_ui :: proc(player: Player) {
    screen := UI.get_safe_screen_rect();

    // A simple centered panel
    panel := screen.center_rect().grow(120, 200, 120, 200);
    UI.quad(panel, core_globals.white_sprite, {0, 0, 0, 0.7});
}
```

### Step 3: Use Cut For Layout

When laying out multiple elements, cut space out of a rect instead of positioning everything from the same origin.

```csl
draw_panel_layout :: proc() {
    panel := UI.get_safe_screen_rect().center_rect().grow(140, 220, 140, 220);

    header := panel.cut_top(80);
    footer := panel.cut_bottom(70);
    body := panel;

    UI.quad(header, core_globals.white_sprite, {0.1, 0.1, 0.1, 0.8});
    UI.quad(body, core_globals.white_sprite, {0.05, 0.05, 0.05, 0.8});
    UI.quad(footer, core_globals.white_sprite, {0.1, 0.1, 0.1, 0.8});
}
```

### Step 4: Add Text

Use text settings to control size, color, and alignment.

```csl
draw_header_text :: proc(rect: Rect) {
    ts := UI.default_text_settings();
    ts.size = 52;
    ts.color = {1, 1, 1, 1};
    ts.halign = .CENTER;
    ts.valign = .CENTER;

    UI.text(rect, ts, "New Quest!");
}
```

### Step 5: Add Buttons

Buttons combine layout and interaction. Use a button sprite from the engine supplied assets (see [UI Reference](/ui/ui-reference.md) for more).

```csl
draw_button :: proc(rect: Rect) {
    bs := UI.default_button_settings();
    ts := UI.default_text_settings();

    bs.sprite = get_asset(Texture_Asset, "$AO/new/modal/buttons_2/button_2.png");
    bs.press_scaling = 0.35;

    if UI.button(rect, bs, ts, "Accept").clicked {
        log_info("Accepted!");
    }
}
```

### Step 6: Build a Dialog

Now combine all of the steps into a reusable dialog layout.

```csl
draw_dialog :: proc(title: string, body: string) {
    screen := UI.get_safe_screen_rect();
    dialog := screen.center_rect().grow(200, 300, 200, 300);

    UI.quad(dialog, core_globals.white_sprite, {0, 0, 0, 0.85});

    content := dialog.inset(30);
    header := content.cut_top(80);
    buttons := content.cut_bottom(90);
    body_rect := content;

    ts := UI.default_text_settings();
    ts.halign = .CENTER;
    ts.valign = .CENTER;

    ts.size = 52;
    UI.text(header, ts, title);

    ts.size = 36;
    UI.text(body_rect, ts, body);

    bs := UI.default_button_settings();
    bs.sprite = get_asset(Texture_Asset, "$AO/new/modal/buttons_2/button_2.png");
    bs.press_scaling = 0.35;

    left := buttons.cut_left(210);
    buttons.cut_left(20);
    right := buttons.cut_left(210);

    if UI.button(left, bs, ts, "Cancel").clicked {
        log_info("Cancelled");
    }
    if UI.button(right, bs, ts, "Confirm").clicked {
        log_info("Confirmed");
    }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.allout.game/ui/fundamentals.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
