# 5. Diegetic UI Approaches
Part of [[UI Toolkit and Diegetic UI]]. Requires Unity 6000.5+. Previous: [[4.Data Binding and Custom Controls]]. Next: [[6.Diegetic Terminal and Wrist Display]].
## 5.1 Vocabulary
UI is classified by two questions: *does it exist in the game world?* and *do the characters know about it?*
| Type | In world space | Known to the character | Example |
|---|---|---|---|
| Non-diegetic | No | No | Classic screen-space health bar, menu |
| **Diegetic** | Yes | Yes | Ammo counter on the weapon, terminal on a wall, wrist computer, car dashboard |
| Spatial | Yes | No | Floating enemy health bar, objective marker pinned in the world |
| Meta | No | Yes | Blood on the screen, cracked visor |
This tutorial's technical approaches cover both **diegetic** (terminal, wrist display) and **spatial** (anything in world space) UI. Technique does not decide the type; *fiction* does.
Why choose it: immersion, no screen clutter, UI that is also a gameplay object (player has to walk to the terminal, look at the wrist). The costs: legibility depends on camera angle/distance, interaction needs a 3D pointer, and everything is tied to scene lighting and aspect.
## 5.2 The three implementation routes
### A. UI Toolkit -> `RenderTexture` -> mesh (screen on a prop)
Render a UI Toolkit panel into a `RenderTexture` (`PanelSettings.targetTexture`) and use that texture on any material. Pointer input is **mapped manually** with `PanelSettings.SetScreenToPanelSpaceFunction`, usually by raycasting the screen and using the hit's UV (`RaycastHit.textureCoord`; requires a `MeshCollider`).
- Works since UI Toolkit runtime exists; no World Space feature needed.
- You control the shader/material: emission + bloom, CRT scanlines, curved glass, dirt, flicker, screen-off state, lit by the scene if you want.
- Texture resolution is a fixed budget you control, independent of distance.
- Cost: you write the input mapping; one RenderTexture per distinct screen (shared panel => shared texture).
### B. UI Toolkit World Space panels (flat panel hanging in 3D)
Set `PanelSettings` **Render Mode = World Space** and place a `PanelRenderer` in the scene. It becomes a renderable object with a transform, sized by *Pixels Per Unit* and world-space size settings, rendered unlit, sorted by camera distance. Pointer input comes through **Panel Input Configuration** and colliders.
- Fast to set up; input built in (event cameras, interaction layers, colliders).
- Good for wrist displays, in-world labels, signs, holographic panels.
- Limitations to design around (as documented for 6.2 and 6.3; confirm in 6000.5 notes): unlit by default; no depth-test control (it can draw through geometry unless you work around it, for example with a URP Render Objects feature or custom pass); no 2D sorting layers (sorts by camera distance); not for lighting-heavy "screen is a light" looks. Shader Graph custom UI shaders (6.3+, URP only) let you restyle but are not a scene-lighting integration.
### C. uGUI world-space Canvas
`Canvas > Render Mode = World Space`, assign **Event Camera**, scale the `RectTransform` (typically 0.001 per pixel). Input goes through the standard `GraphicRaycaster` + `EventSystem`.
- Mature, fully lit/depth-tested options, TextMeshPro, huge asset-store ecosystem.
- Use it when you need what UI Toolkit world space lacks (depth testing, lit materials per Graphic, VR XR Interaction Toolkit's UI support).
- Heavier GameObject hierarchy; layout is slower to author than UXML/USS.
## 5.3 Decision table
| Need | Pick |
|---|---|
| Interactive screen on a prop that must look *physical* (glow, CRT, curved glass) | **A** render texture |
| Flat panel anchored to a hand/object, mostly readouts | **B** world space panel |
| Depth-tested UI (hides behind walls), lit UI, or XR Interaction Toolkit UI | **C** uGUI, or B with workaround |
| Many identical floating health bars | B or C; profile (see [[7.UI Toolkit Pitfalls and Performance]]); consider non-UI sprites/meshes at scale |
| Same UXML in several places (screen HUD + in-world copy) | A or B: reuse the UXML, give each its own `PanelSettings` |
| Must work on a version before 6.2 | A or C |
## 5.4 Legibility rules (the part people skip)
1. **Design in physical size.** A screen 0.4 m wide viewed from 0.7 m: text needs to subtend roughly the same visual angle as a phone screen. Work backwards: pick a minimum body text of ~16-20 px **on a 1024 px-wide texture** and test at your intended viewing distance.
2. **High contrast, low density.** Diegetic panels are seen at angles, with bloom and lighting. Prefer big type, thick strokes, few elements.
3. **Use SDF/vector text** for world-space panels so text stays crisp when scaled. Apply mipmaps and anisotropic filtering to render textures (angled screens alias badly).
4. **Never show critical info only diegetically** unless that is the game's point; offer an accessibility option (HUD mirror or larger text).
5. **Give interaction a clear mode.** Look-at + press key -> "use mode" with free cursor and locked movement, rather than trying to aim FPS look and click tiny UI at once.
6. **Feedback**: hover highlights, key hints, audio. A 3D screen without hover feedback feels dead.
## 5.5 Interaction models
| Model | How | Fits |
|---|---|---|
| Use mode | Press E at the prop; cursor unlocks; look/move disabled; mouse drives the panel; Esc exits | Terminals, kiosks, PC/console + mouse |
| Crosshair click | Crosshair is the pointer; map the *screen center* ray to the panel; Interact key = click | FPS without cursor, gamepad |
| Laser pointer | Ray from controller/hand | VR, world-space panels |
| Direct touch | Finger collider overlaps the panel | VR |
Section 6 uses **use mode** for the terminal. For gamepad/crosshair, return the screen-center position from your pointer source instead of the mouse position.
## 5.6 Keyboard focus, gameplay input and the cursor
A diegetic text field has exactly the problems of an in-game chat box:
- While a text field is focused, typed WASD must not move the player. Gate gameplay input with a single bool (`UIHasFocus`) or disable the player's action map while in use mode.
- The mouse is probably locked to the screen center in FPS. Unlock and show it for use mode (`Cursor.lockState`, `Cursor.visible`), re-lock on exit.
- Esc is both "leave use mode" and probably "pause": decide which has priority and consume the key in exactly one place.
## 5.7 Official references
- World Space UI overview: https://docs.unity3d.com/6000.3/Documentation/Manual/ui-systems/world-space-ui.html
- `PanelSettings.SetScreenToPanelSpaceFunction`: https://docs.unity3d.com/ScriptReference/UIElements.PanelSettings.SetScreenToPanelSpaceFunction.html (includes the UITextureProjection sample code)
- Panel Settings properties reference: https://docs.unity.com/en-us/engine/7000.0/manual/uitoolkits/uielements/uie-support-for-runtime-ui/uie-render-runtime-ui/uie-runtime-panel-settings
- `PanelSettings.targetTexture`: https://docs.unity3d.com/6000.3/Documentation/ScriptReference/UIElements.PanelSettings-targetTexture.html
- Unity UI World Space Canvas: https://docs.unity3d.com/Packages/
[email protected]/manual/class-Canvas.html
Next: [[6.Diegetic Terminal and Wrist Display]].