# 2. UI Toolkit Project Setup Part of [[UI Toolkit and Diegetic UI]]. Requires Unity 6000.5+. Previous: [[1.UI Toolkit Concepts]]. Next: [[3.HUD and Inventory Example]]. ## 2.1 Packages and project settings 1. Create a project from the **Universal 3D** template (URP). URP matters later: 6.3+ custom UI shaders via Shader Graph are URP-only, and the render-texture diegetic technique works in any pipeline. 2. **Input**: new 3D templates use the **Input System** package and set *Active Input Handling* to "Input System Package (New)". The scripts in this tutorial use `UnityEngine.InputSystem` (`Keyboard.current`, `Mouse.current`). If you are on the legacy Input Manager, swap those calls for `Input.GetKeyDown` and `Input.mousePosition`. 3. No extra package is needed for UI Toolkit runtime: it is part of the engine (`UnityEngine.UIElementsModule`). The UI Builder and UXML/USS importers ship with the editor. ## 2.2 Folder layout Keep UXML, USS and scripts that belong together in one folder per feature. UXML `<Style src="...">` paths are relative to the UXML file, so moving a folder as a unit does not break references. ``` Assets/ UI/ Settings/ GamePanelSettings.asset <- screen-space panel WristPanelSettings.asset <- world-space panel (own asset: Pixels Per Unit is per asset) TerminalPanelSettings.asset <- render-texture panel Shared/ Theme.uss Hud/ Hud.uxml Hud.uss HudController.cs Inventory/ Inventory.uxml InventoryRow.uxml Inventory.uss InventoryScreenController.cs Terminal/ Terminal.uxml Terminal.uss TerminalController.cs ... Game/ PlayerStats.cs ItemDefinition.cs Inventory.cs ``` ## 2.3 Create a Panel Settings asset 1. `Assets > Create > UI Toolkit > Panel Settings Asset`. Name it `GamePanelSettings`. 2. Unity creates a default runtime theme alongside it and assigns it to **Theme Style Sheet**. If buttons render with no styling at all, the theme is missing; reassign one. 3. Configure for a HUD: | Property | Value | Why | |---|---|---| | Render Mode | Screen Space Overlay | Normal screen HUD | | Scale Mode | Scale With Screen Size | Author once at a reference size | | Reference Resolution | 1920 x 1080 | Your design size | | Screen Match Mode | Match Width Or Height, Match = 0.5 | Compromise for ultrawide / portrait | | Sort Order | 0 | Raise for overlays; lower = drawn earlier | | Target Texture | None | Only set for the render-texture technique | Why not Constant Pixel Size? It is fine for editor-like tools. For games, you want the same layout at 1080p and 4K, so scale with screen. > [!note] One Panel Settings = one panel > Every component pointing at the same `PanelSettings` asset renders into the **same panel** (one visual tree space, one focus context). Different render modes/targets need different assets. This is why the wrist display and terminal later get their own assets. ## 2.4 Add UI to the scene with `PanelRenderer` (6000.5+) 1. `GameObject > UI Toolkit > Panel Renderer` (or add `Component > UI Toolkit > Panel Renderer`). 2. Assign **Panel Settings** and **Source Asset** (your UXML). 3. `PanelRenderer` derives from `Renderer`, so it has `sortingOrder` (an `int`), bounds, and culling like any renderer. Siblings render by ascending Sort Order; children render on top of parents. Give siblings unique Sort Orders when layering matters. 4. In **world space**, the component additionally exposes *World-Space Dimensions* (size mode, size, pivot, pivot reference size). See [[6.Diegetic Terminal and Wrist Display]]. ### Getting the root: the reload callback pattern `PanelRenderer` has no public `rootVisualElement`. You register a callback instead. It runs immediately if the UI is already loaded, then again whenever it reloads (asset edit/live reload, changed `PanelSettings` / `VisualTreeAsset`). ```csharp using UnityEngine; using UnityEngine.UIElements; public class ReloadPatternExample : MonoBehaviour { PanelRenderer panelRenderer; int loadedVersion = -1; void OnEnable() { panelRenderer = GetComponent<PanelRenderer>(); panelRenderer.RegisterUIReloadCallback(OnUIReload); // versioned overload } void OnDisable() => panelRenderer.UnregisterUIReloadCallback(OnUIReload); void OnUIReload(PanelRenderer renderer, VisualElement root, int version) { if (version == loadedVersion) return; // same tree as last time: skip re-wiring loadedVersion = version; var play = root.Q<Button>("play"); play.clicked += () => Debug.Log("Play"); } } ``` Why the version check: the docs state that disabling and re-enabling a `PanelRenderer` keeps the visual tree in memory (unlike `UIDocument`, which destroys and rebuilds it), so the callback fires again with the **same** version. If you re-wired anyway you would add a second `clicked` handler to the same button. When the tree is genuinely rebuilt, the version changes and your old references are stale, so you re-query everything. > [!info] Using `UIDocument` instead (any Unity 6) > `UIDocument` still works and is not removed; new features target `PanelRenderer` first. The swap is mechanical: > - Component: `UIDocument` -> `PanelRenderer` (reassign Panel Settings + Source Asset). > - Code: `GetComponent<UIDocument>().rootVisualElement` in `OnEnable` -> reload callback. > - Behaviour: `UIDocument` rebuilds its tree on every enable; `PanelRenderer` keeps it. > - `sortingOrder`: `float` on `UIDocument`, `int` on `PanelRenderer`. > Both implement `IPanelComponent`, can share one `PanelSettings`, and can coexist during migration. ## 2.5 Input and the EventSystem Screen-space panels receive input one of two ways: - **With an active `EventSystem`** (uGUI present, Input System UI Input Module): the EventSystem is the input source and UI Toolkit panels receive redirected events. - **No `EventSystem`**: UI Toolkit's built-in input handles it. For **world-space** panels, add a **Panel Input Configuration** component (`GameObject > UI Toolkit > Panel Input Configuration`); see [[6.Diegetic Terminal and Wrist Display]]. Its *Panel Input Redirection* default (**Auto-switch**) uses the EventSystem when one is active and UI Toolkit's own input otherwise. Mixed uGUI + UI Toolkit scenes should keep one EventSystem. ## 2.6 Shared theme file Create `Assets/UI/Shared/Theme.uss`. Variables defined on `.theme` inherit to every descendant of an element with that class. Add `class="theme"` to the top element of every UXML. ```css .theme { --color-bg: rgba(12, 16, 24, 0.82); --color-bg-solid: rgb(18, 24, 34); --color-border: rgba(120, 190, 255, 0.35); --color-text: rgb(230, 236, 245); --color-muted: rgb(140, 154, 175); --color-accent: rgb(64, 200, 255); --color-health: rgb(224, 82, 82); --color-gold: rgb(240, 196, 70); --radius: 8px; --gap: 8px; color: var(--color-text); font-size: 16px; } .panel { background-color: var(--color-bg); border-width: 1px; border-color: var(--color-border); border-radius: var(--radius); padding: 12px; } .caption { font-size: 11px; color: var(--color-muted); -unity-font-style: bold; letter-spacing: 2px; } ``` ## 2.7 Fonts and text - Runtime UI Toolkit text uses the panel's text settings. You can reference a Font Asset or TMP-style font through `-unity-font-definition: url("project://database/Assets/Fonts/MyFont SDF.asset");`. - Dynamic font assets work for most UI; SDF font assets give crisp scaling for world-space UI. Unity's forum status posts describe a migration to the Advanced Text Generator (ATG); check the 6000.5 Text documentation for current defaults before committing a font pipeline. ## 2.8 Smoke test 1. Create `Assets/UI/Hud/Hud.uxml` with one `Label` and a USS file; assign to a `PanelRenderer` with `GamePanelSettings`. 2. Press Play. If nothing appears: Debugger (`Window > UI Toolkit > Debugger`), set the Game view as target, and check (a) the panel exists, (b) your root has a size (`flex-grow:1` or absolute fill), (c) the theme is assigned. Next: [[3.HUD and Inventory Example]].