Skip to content
Bring your skills

Unity UI to Godot: Canvas, RectTransform, and uGUI in Control Nodes

Rebuild a Unity uGUI interface in Godot 4.7: what replaces Canvas and CanvasScaler, how anchors work, and which Container replaces each Layout Group.

On this page

When porting Unity UI, start with layout. Godot Containers position and size their child Controls, while anchors and offsets handle free-positioned Controls. This guide maps the common Unity UI components and rebuilds one screen using those rules.

The One Difference That Causes All the Others

Both Unity Layout Groups and Godot Containers can control the layout of their children. In Godot, a Container calculates child Control positions and sizes. Change the container settings and the child’s minimum size or size flags when you want a different result.

So a Godot Control is in exactly one of two modes. Either it is free, and you position it with anchors and offsets, or it is inside a Container, and the Container positions it while you only get to influence the result through size flags. Dragging a Container's child in the editor and watching it snap back is not a bug; it is the Container doing its job. If the idea of a parent owning its children is still new, the nodes and scenes guide covers the same ownership model everywhere else in the engine.

Canvas Becomes CanvasLayer, or Nothing At All

Unity requires a Canvas as the root of any UI. Godot does not. A Control node works anywhere in the tree, and for a menu scene that fills the screen you often need no wrapper at all.

CanvasLayer is what you actually want for a HUD. It draws its children on a separate layer that ignores the world camera, which is the behaviour Unity gives you with a Screen Space - Overlay Canvas. Without it, a HUD parented under a moving camera will drift with the game world.

Unity
Canvas (Screen Space - Overlay)
  CanvasScaler
  GraphicRaycaster
  HUDPanel (RectTransform)
    HealthBar (Slider)
    ScoreText (TextMeshProUGUI)
Godot 4.7
CanvasLayer
  MarginContainer
    VBoxContainer
      HealthBar (ProgressBar)
      ScoreLabel (Label)

There is no GraphicRaycaster to add. Input picking is built into Control via mouse_filter, which defaults to letting a Control consume the events it sits under.

RectTransform Becomes Anchors Plus Offsets

Godot splits what RectTransform does into two properties, and they are easier to reason about once you stop looking for a single rect.

  • Anchors are ratios from 0.0 to 1.0 describing a point in the parent. 0.0 is the left or top edge, 1.0 is the right or bottom, 0.5 is centre. They answer "relative to what?"
  • Offsets are pixel distances from those anchor points. They answer "how far from it?"
  • Equal anchors on an axis (both 0, or both 1) pin the control at a fixed size. Different anchors (0 and 1) make it stretch with the parent.

For 24px horizontal margins, set the left anchor to 0 and right anchor to 1, then use a left offset of 24 and a right offset of -24. The right offset is negative because the edge moves inward from the parent’s right side. Test the panel at several window widths.

Layout Groups Become Containers

Godot Containers cover common row, column, grid, padding, and scrolling layouts. These are useful starting points for translating Unity UI:

  • VerticalLayoutGroupVBoxContainer, HorizontalLayoutGroupHBoxContainer
  • GridLayoutGroupGridContainer (set the column count, rows follow)
  • Padding on a Layout Group → a MarginContainer wrapped around the thing you want padded
  • ScrollRectScrollContainer, which takes one child and adds bars when that child overflows
  • A background Image behind a panel → PanelContainer, which draws a StyleBox and sizes itself to its child
  • Centring a thing → CenterContainer, which keeps children centred at their minimum size
  • No Unity equivalent: AspectRatioContainer, HFlowContainer (wraps to the next line when it runs out of room), and TabContainer

Containers nest, and nesting is the intended style. A pause menu is usually CenterContainer → PanelContainer → MarginContainer → VBoxContainer → buttons. Each container handles part of the layout. Minimum sizes, margins, and content can still overflow, so test small windows and long labels.

Size Flags Replace Layout Element

Unity's Layout Element component lets a child override what a Layout Group wants to do with it. Godot's equivalent lives on every Control as size_flags_horizontal and size_flags_vertical:

  • Fill: occupy the space the container assigned. On by default.
  • Expand: claim any spare space in the container. This is the flag people forget, and it is why one button refuses to grow.
  • Shrink Begin / Center / End: where the control sits inside the space it was given, when it is not filling it.
  • Stretch Ratio: when several siblings all Expand, this decides how the spare space splits between them. Two children at ratio 1 and 3 divide it one quarter to three quarters.
gdscript
# A row where the label takes what it needs and the bar eats the rest.
# Set in the inspector normally; shown in code to make the mapping obvious.

func _ready() -> void:
    $Row/NameLabel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
    $Row/HealthBar.size_flags_horizontal = Control.SIZE_EXPAND_FILL

CanvasScaler Becomes Two Project Settings

Unity solves multi-resolution per Canvas, with a CanvasScaler set to Scale With Screen Size and a reference resolution. Godot solves it once for the whole project, in Project Settings → Display → Window.

Set the base window width and height to your design size, shown by the blue rectangle in the 2D editor. Then choose a Stretch Mode:

  • Disabled (the default): at Scale 1.0, one unit is one pixel. Useful when you want layout to respond directly to window dimensions.
  • Canvas Items: scales 2D content from the base size while drawing at the target resolution. 3D rendering is unaffected. A useful starting point for scalable game UI.
  • Viewport: renders at the base size before scaling the result. Useful for a deliberate low-resolution appearance; text rendered there shares that limit.

Stretch Aspect then decides what happens when the window's shape does not match your base size: keep letterboxes, expand shows more of the scene, and ignore distorts. For a game with a HUD, expand plus anchored corners gives you the behaviour Unity users expect from anchored UI.

UI Prefabs Become Scenes You Instance

Save a reusable UI element, such as an inventory slot or settings row, as a scene with a Control root and its own .tscn file. Instance it under the appropriate Container so the parent handles layout.

gdscript
const SLOT := preload("res://ui/inventory_slot.tscn")

func rebuild_inventory(items: Array) -> void:
    for child in %SlotGrid.get_children():
        child.queue_free()
    for item in items:
        var slot := SLOT.instantiate()
        slot.setup(item)
        %SlotGrid.add_child(slot)   # GridContainer lays it out; you set no position

The GridContainer sets the slots’ positions and sizes. Adjust its columns and the children’s sizing options when the layout needs to change.

Styling: One Theme Instead of Per-Component

A Theme resource defines fonts, colors, and StyleBoxes for Godot controls. Assign it near the top of a UI tree so descendants inherit the shared styles. This is a useful place to collect the visual settings from your Unity interface.

Collect the styles your Unity screens share in a Theme. Keep individual overrides for controls that need a different appearance. If several widgets need the same override, consider making that a shared style too.

Why does my menu look stretched or oversized?

Your mockup looks fine, then the buttons become huge in the running game. Before shrinking every control, find out whether the whole interface scales incorrectly or one widget refuses to fit. A reader bringing a custom theme into Godot reported both stretched controls and oversized slider handles. Those symptoms need different checks.

  • If text, icons, and spacing all grow together, record the base viewport size, actual game window size, and Stretch settings. In a copy of the project, try canvas_items with keep and Scale 1.0. This gives you an aspect-preserving comparison with bars when the window shape differs.
  • If circles turn into ovals, inspect Stretch Aspect. ignore permits unequal horizontal and vertical scaling. Compare with keep before changing any source artwork.
  • If one button refuses to shrink, inspect its minimum size, text, icon, and StyleBox margins. Its content can require more space than the rectangle you dragged in the editor.
  • If a child snaps back after you move it, check its parent. A Container handles its direct Control children. Change that layout and the child’s sizing flags instead of repeatedly setting its position.
  • If only a slider handle is oversized, check the grabber, grabber_highlight, and grabber_disabled textures in its Theme. Changing the HSlider rectangle alone does not resize those source textures. Try a smaller test texture to isolate the problem.

Remove the custom Theme from a copy of the affected screen. If the default controls fit, add your font, panel styles, and widget textures back separately. Keep the same window size during that comparison so a scaling change cannot disguise the result.

Build a pause-menu layout you can resize

Create a separate UI scene with this tree. Set PauseMenu to Full Rect, then Center to Full Rect. Keep the other nodes under their shown parents. This is a layout exercise; the buttons do not pause or resume gameplay yet.

PauseMenu.tscnGodot scene guide · read-only
Scene

Select a node to inspect it.

InspectorExample settings
PauseMenuControl
PauseMenu

The base node for interface elements. Anchors and offsets position it relative to its parent; a parent Container can manage its layout instead.

Layout
Full Rect
In this example

Full Rect. Covers the viewport when used as the scene root.

Select a node to see which part of the layout it controls.

Explore the setup here. Build and run it in Godot.

Run this scene with a base size of 640 × 360, canvas_items, keep, and Scale 1.0. Compare a 640 × 360 game window with 1280 × 720: the menu should stay centered and scale proportionally. Then try 800 × 600. With keep, expect bars outside the 16:9 content area. Switching to expand should give the interface additional layout space; repeat the check after that change.

Now change Settings to “Audio and accessibility settings.” If the panel becomes wider, inspect the text’s minimum-size requirement. Decide whether your real menu should grow, wrap its content, or use a shorter label. Containers do not decide that product choice for you.

Keep a note of the smallest window you intend to support and try every screen there. For scene ownership practice, open the Scene Builder; for the full migration context, return to the Unity migration exercises.

A Migration Order That Works

  • Set base resolution and Stretch Mode first. Doing UI work before this means measuring against a size that will change.
  • Build the Theme from your existing styles, before porting a single screen.
  • Port one small screen completely. A pause menu exercises containers, buttons, and focus navigation before you tackle a larger interface.
  • Rebuild layouts with Containers rather than translating RectTransform numbers. Reach for free-floating anchors only where a Container genuinely cannot express the layout.
  • Port the HUD next, under a CanvasLayer, and test it at three window shapes before moving on.
  • Port inventory grids, skill trees, and other screens with dynamic children after checking the basic container layout and Theme.

If you have not mapped the rest of the project yet, do that before the UI: the Unity to Godot path covers the node and lifecycle model, and the equivalents dictionary has the lookups for everything outside the UI layer.

The Short Version

Finish the pause menu by resizing the window, increasing a label’s length, and navigating with a keyboard or controller. Fix any overflow or focus problems before reusing the scene’s containers and Theme in the next screen.

Verification notes

Sources and revision context

Updated September 8, 2026. Added a distorted-menu diagnosis and a pause-menu layout exercise. Scaling, minimum sizes, containers, and slider theme textures reviewed against the stable documentation on September 8, 2026. Visual layout and input focus have not been tested in Godot.