Canvas & UI
Anchors, scaling, and layout groups
Unity UI is the part most likely to look perfect on your monitor and broken on someone else's phone. Two settings prevent almost all of that: the Canvas Scaler, and anchors.
Canvas render modes
| Render mode | Behaves like | Use for |
|---|---|---|
Screen Space - Overlay | Painted on top of everything | HUD, menus. The default. |
Screen Space - Camera | Drawn by a camera | UI that needs post-processing or 3D elements. |
World Space | An object in the scene | Health bars over enemies, in-world screens. |
Canvas Scaler: set this first
On a fresh canvas, UI Scale Mode is Constant Pixel Size, which means your UI keeps its pixel dimensions and therefore shrinks into a corner on a big display and overflows on a small one.
- 1 Select the Canvas and find Canvas Scaler
It sits below the Canvas component.
- 2 Set UI Scale Mode to Scale With Screen Size
This alone fixes most cross-resolution problems.
- 3 Set a reference resolution
Design at one size and let Unity scale from it.
1920 × 1080for desktop,1080 × 1920for portrait mobile. - 4 Set Match to suit your game
0matches width,1matches height,0.5splits the difference. Landscape games usually want width; portrait usually wants height.
Anchors
A RectTransform positions itself relative to its anchors, not to the screen. Anchors are the single most misunderstood part of Unity UI, and getting them right is what makes a layout survive a different aspect ratio.
| You want it | Anchor to | Result |
|---|---|---|
Stuck to a corner | That corner | Stays put at any resolution. |
Centred | Centre | Stays centred. |
A full-width bar | Stretch horizontally | Grows with the screen. |
Filling its parent | Stretch both | Backgrounds and overlays. |
Layout groups
Rather than positioning every element by hand, let layout groups arrange children: Horizontal, Vertical, and Grid. Add a ContentSizeFitter when the container should grow to fit its contents — a tooltip that resizes around its text, for example.
ContentSizeFitter, forces repeated layout passes and shows up in the profiler as Canvas.SendWillRenderCanvases.The canvas rebuild problem
A canvas batches its elements into a mesh. Change any element and the entire canvas rebuilds. A single timer updating every frame therefore rebuilds your whole HUD every frame.
- Split static and dynamic UI onto separate canvases. This is the big one.
- Update text only when the value actually changes, not every frame.
- Disable the
Canvascomponent to hide a screen — cheaper thanSetActive(false), which destroys and rebuilds the batch. - Turn off Raycast Target on any graphic that is not clickable.
private int lastShown = -1;
private void Update()
{
int seconds = Mathf.CeilToInt(timeRemaining);
if (seconds != lastShown)
{
lastShown = seconds;
timerText.text = seconds.ToString(); // once per second
}
}private void Update()
{
// Assigning .text marks the canvas dirty even when the
// string is identical - the whole canvas rebuilds.
timerText.text = Mathf.CeilToInt(timeRemaining).ToString();
}Safe areas on mobile
Notches and rounded corners cover part of the screen. Screen.safeArea gives you the usable rectangle; apply it to a container so nothing important hides behind the hardware.
private void ApplySafeArea()
{
Rect safe = Screen.safeArea;
Vector2 min = safe.position;
Vector2 max = safe.position + safe.size;
min.x /= Screen.width; min.y /= Screen.height;
max.x /= Screen.width; max.y /= Screen.height;
rectTransform.anchorMin = min;
rectTransform.anchorMax = max;
}What to take away
- Set Canvas Scaler to Scale With Screen Size before building any UI.
- Anchor elements to the edge they belong to, not the centre.
- Split changing UI onto its own canvas to limit rebuilds.
- Only assign
.textwhen the value has actually changed. - Switch off Raycast Target on non-interactive graphics.