Godot has two coordinate systems for 2D. Node2D-derived nodes use world coordinates โ absolute positions in the scene, unaware of screen size. Control-derived nodes use a layout system with anchors and margins, like CSS. For a mobile card game you'll use Controls for the UI (hand, buttons, score display), and Node2Ds for the game world (dueling arena, animated card playing effects).
Getting this split right is the difference between a UI that works on every Android screen size and one that falls apart on phones with notches.
| Node2D | Control | |
|---|---|---|
| Position | Absolute Vector2 | Anchor + offset |
| Responds to window resize | No | Yes |
| Auto-layout (containers) | No | Yes |
| Input routing | Manual (Area2D) | Automatic (Control.gui_input) |
| Use for | Game world, effects | UI, menus, HUD |
<canvas> where you paint pixels at specific coordinates, Control is a <div> that flows and anchors. For a card game HUD โ hand at the bottom, mana top-left, enemy at the top โ you want Control.Every Control node has four anchor values (top, bottom, left, right), each a number from 0 to 1 representing a fraction of the parent's size. The control's edges are positioned at those fractions plus pixel offsets.
Godot gives you Layout Presets โ a dropdown in the toolbar when a Control is selected โ that sets all four anchors sensibly. You'll use these 90% of the time. The interesting ones:
Instead of positioning every child Control by hand, wrap them in a Container that arranges them automatically. These are the building blocks:
| Container | What it does |
|---|---|
HBoxContainer | Children in a horizontal row. The hand of cards. |
VBoxContainer | Children in a vertical column. A stats panel. |
GridContainer | N columns, children flow. A card collection grid. |
CenterContainer | Centers its single child. |
MarginContainer | Adds padding. Put inside to force pixel padding on any layout. |
PanelContainer | Adds a background panel sized to contents. |
AspectRatioContainer | Enforces a child aspect ratio. Useful for cards. |
Containers handle the math. You put cards in an HBoxContainer, and it spaces them evenly. Add or remove cards at runtime, the layout re-flows. This is exactly what you want.
# Spawning cards into a hand at runtime
@onready var hand_container: HBoxContainer = $UI/HandContainer
const CARD_SCENE := preload("res://scenes/card.tscn")
func draw_card(data: CardData) -> void:
var card := CARD_SCENE.instantiate()
card.data = data
hand_container.add_child(card) # layout is automatic
| Node | What |
|---|---|
Label | Text display. Read-only. |
Button | Clickable, has pressed signal. |
TextureRect | Display an image. expand_mode controls scaling. |
TextureButton | Button that's an image (normal/hover/pressed textures). |
ProgressBar | HP bars, loading indicators. |
LineEdit | Single-line text input. For word games, this is your input field. |
RichTextLabel | BBCode-formatted text. Damage numbers with color, card descriptions. |
Panel | Styleable background. Apply via StyleBox. |
You'll want your UI to have a consistent look. Don't style every Button individually โ define a Theme resource and apply it to the root Control.
Theme. Save as ui/main_theme.tres.main_theme.tres into the Theme slot.Every Button under that Control now uses your theme. Override per-instance only when needed.
# Example: StyleBoxFlat in code for a specific panel
var sb := StyleBoxFlat.new()
sb.bg_color = Color("1a1f2e")
sb.border_width_bottom = 2
sb.border_color = Color("fca311")
sb.corner_radius_top_left = 8
sb.corner_radius_top_right = 8
$Panel.add_theme_stylebox_override("panel", sb)
Modern phones have notches, rounded corners, and bottom gesture bars. Godot exposes a Safe Area via DisplayServer.get_display_safe_area(). You should not put interactive UI at the very edges of the screen.
MarginContainer with ~40px margins on all sides. It won't look cramped on your phone, and the hand of cards will sit above the gesture bar instead of getting eaten by it.
We'll revisit this in the mobile export module. For now, remember: 40px padding everywhere is fine.
Project Settings โ Display โ Window โ Stretch:
canvas_items โ scales everything based on a reference resolution. Use this.keep โ maintains aspect ratio. Start with this.With these settings, your UI scales uniformly across phone sizes. Without them, a card that's 120px wide looks tiny on a 1440p screen and huge on a 720p one.
canvas_items, Aspect = keep. Set Viewport Width = 1080, Height = 1920.main.tscn. Add a CanvasLayer child. Inside it, add a Control node. With the Control selected, click Layout Preset โ Full Rect.HBoxContainer. Layout Preset โ Bottom Wide. This is your hand container โ the rest of the exercise comes next lesson.