2D Fundamentals

Godot Fundamentals

Chapter 4 · 2D Fundamentals

Every chapter so far has worked with an empty Node2D. This chapter makes something actually appear on screen: displaying an image with Sprite2D, understanding exactly how Godot measures position in 2D space, and moving something across the screen using nothing more than what's already been covered.

Godot's 2D Coordinate System

Godot's 2D origin, (0, 0), sits at the top-left of the viewport. X increases to the right, as you'd expect — but Y increases downward, not upward.

Y grows downward — the single most common early surprise — this is the opposite of a standard math graph (and of a NumPy/matplotlib plot), where Y usually increases upward. It matches how most 2D game engines and image formats work, since screen pixels are naturally numbered from the top-left corner down. A positive y value moves something down the screen, not up.
position.x

Increasing it moves an object right; decreasing it moves left.

position.y

Increasing it moves an object down; decreasing it moves up.

Vector2

An (x, y) pair. position on every Node2D is itself a Vector2.

Position, Rotation & Scale

Every Node2D (and everything that extends it, including Sprite2D) automatically has three built-in properties: position, rotation, and scale. These were mentioned briefly in Chapter 1 — here's what each one actually does.

extends Node2D func _ready() -> void: position = Vector2(100, 50) # 100px right, 50px down from origin rotation = PI / 4 # rotation is in RADIANS, not degrees scale = Vector2(2.0, 2.0) # twice the original size
rotation is in radians — if degrees are more natural to think in, use rotation_degrees instead; it's the same underlying value, just exposed in a different unit.

Sprite2D — Displaying an Image

Sprite2D is the node responsible for showing a 2D image (a "texture," in Godot's own terminology). Add one as a child, assign it an image via its texture property, and it renders at its parent's position.

extends Node2D func _ready() -> void: $Sprite2D.texture = preload("res://player.png")

More commonly, a texture is assigned directly in the editor's Inspector panel rather than from a script — drag an image file onto the Sprite2D's own texture slot, and it's ready to go with no code at all.

PropertyDoes
textureThe image to display
centeredIf true (the default), the image is centered on the node's own position; if false, its top-left corner sits at that position instead
flip_h / flip_vMirror the image horizontally or vertically — useful for a character facing left vs. right

Basic On-Screen Movement

Chapter 1 already showed the pattern for frame-rate-independent movement using _physics_process and delta. Applied here to an actual visible sprite, the same idea makes something glide across the screen.

extends Sprite2D const SPEED = 150.0 func _physics_process(delta: float) -> void: # Moves steadily to the right, 150 pixels per second. position.x += SPEED * delta

Note this script extends Sprite2D directly, rather than a separate Node2D parent — since Sprite2D already inherits position from Node2D, a script can be attached straight to the sprite itself.

This isn't "real" movement yet — directly changing position works for a simple demo, but it has no awareness of walls, floors, or other objects; it will happily move straight through anything. Chapter 5 adds real input control, and Chapter 6 introduces CharacterBody2D and collision — the node types built specifically for movement that actually respects the rest of the game world.
Coming from Python Vector2(x, y) behaves a lot like a lightweight NumPy array of length 2 — you can add, subtract, and scale two Vector2 values directly with +, -, and *, the same way NumPy overloads those operators for arrays, rather than needing separate .x/.y arithmetic every time. The Y-axis direction is the real trap to remember: a matplotlib plot or a plain Cartesian mental model has Y increasing upward; Godot's 2D space does not.

Coding Challenges

Challenge 1
Add a Sprite2D to a scene with any image assigned to its texture. Write a script that sets its position to Vector2(200, 300) and its scale to Vector2(1.5, 1.5) in _ready(), and print the resulting position and scale.
→ Solution
Challenge 2
Write a script attached to a Sprite2D that continuously moves it downward at 80 pixels per second using _physics_process. Once its position.y passes 400, print "Reached the bottom" exactly once (not on every frame after).
→ Solution
Challenge 3
Write a script that continuously increases a Sprite2D's rotation in _physics_process, making it spin steadily in place. Use rotation_degrees instead of rotation and confirm the sprite still spins identically.
→ Solution

Quick Reference — 2D Fundamentals

  • Origin (0, 0) is top-left; X increases right; Y increases down
  • position, rotation (radians; rotation_degrees for degrees), scale — all on Node2D and everything that extends it
  • Sprite2D.texture — the image to display; centered, flip_h/flip_v
  • Direct movement: position.x += SPEED * delta inside _physics_process
  • Directly setting position ignores walls/collisions — real movement comes in Chapters 5-6