Reference

Words to know

The words the lessons assume you already have. Start at the top: a game is nodes, nodes are arranged into scenes, scripts tell a node what to do, and signals are how nodes talk to each other.

Every word comes with the real thing next to it: the line of GDScript it shows up in, and for 22 of them the actual scene tree from a lesson, with the node lit up so you can see where it sits.

Scenes and nodes

The pieces every Godot game is built out of. Learn these first, because every lesson assumes them.

node 01 · Top-Down Movement

One piece of your game: a player, a wall, a coin, a label. Every node is one kind of thing, and that kind decides what it can do. Nodes are the smallest thing Godot works with.

player.tscn Lesson 01
  • TopDownPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

Three nodes. Each row is one, and its type (the grey word) is what decides what it can do.

scene 01 · Top-Down Movement

A group of nodes saved together in one .tscn file. A scene can be a whole level, or one small thing like a coin that you use over and over.

player.tscn Lesson 01
  • TopDownPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

Those same three nodes saved together in one file. Drop player.tscn into any level and you get all three at once.

scene tree 01 · Top-Down Movement

The list of nodes in the Scene dock, drawn as a family tree. Nodes indented under another node are its children, and children move with their parent.

top_down_movement.tscn Lesson 01
  • TopDownMovement Node2D
  • TopDownPlayer CharacterBody2D player.tscn
  • Borders StaticBody2D
  • Top CollisionShape2D
  • Bottom CollisionShape2D
  • Left CollisionShape2D
  • Right CollisionShape2D

Indent is parent and child. Move Borders and all four of its shapes move with it, because they are underneath it.

root node 01 · Top-Down Movement

The node at the very top of a scene. Its type is what the whole scene counts as, so a scene whose root is a CharacterBody2D can be dropped into a level and it moves.

player.tscn Lesson 01
  • TopDownPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

The top row, the one nothing is indented under. The whole scene counts as a CharacterBody2D because that is what its root is.

Node2D 01 · Top-Down Movement

The plain 2D node. It has a position and a rotation and draws nothing on its own, so it is what you reach for when you just need something to hold other nodes.

top_down_movement.tscn Lesson 01
  • TopDownMovement Node2D
  • TopDownPlayer CharacterBody2D player.tscn
  • Borders StaticBody2D
  • Top CollisionShape2D
  • Bottom CollisionShape2D

It draws nothing at all. It is a position for other nodes to hang off, which is what almost every level root is. (Borders really has four shapes; two are shown.)

extends Node2D

# draws nothing, but it has a position
position = Vector2(100, 50)
CharacterBody2D 01 · Top-Down Movement

A body you drive yourself with code. It is the node for anything that walks, runs, or jumps: the player, and usually the enemies. You set velocity, call move_and_slide(), and Godot stops it at walls.

player.tscn Lesson 01
  • TopDownPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

The thing you drive. It is the only node in this scene that moves, and the only one with a script.

extends CharacterBody2D

func _physics_process(delta):
    velocity = Vector2(200, 0)
    move_and_slide()
StaticBody2D 03 · Walls and Collisions

A solid thing that never moves: walls, floors, platforms. It blocks bodies that run into it and needs no code at all.

walls_and_collisions.tscn Lesson 03
  • WallsAndCollisions Node2D
  • MazePlayer CharacterBody2D player.tscn
  • Goal Area2D goal.tscn
  • Wall1 StaticBody2D
  • Polygon2D
  • CollisionPolygon2D
  • Wall2 StaticBody2D
  • Polygon2D
  • CollisionPolygon2D

A wall never moves and has no script. It only has to be there, with a collision shape under it, and the player stops. (The real maze has four walls; two are shown.)

Area2D 09 · Collect Coins and Items

A shape that notices a touch but does not block it. Coins, spikes, checkpoints, bullets. It tells you something arrived by sending the body_entered signal.

coin.tscn Lesson 09
  • Coin Area2D coin.gd
  • ColorRect
  • CollisionShape2D

Reach for an Area2D when you want to NOTICE a touch without blocking it: coins, checkpoints, hazards, goal zones. A StaticBody2D would stop the player; this lets them walk right through and sends body_entered as they do.

extends Area2D

func _on_body_entered(body):
    queue_free()    # noticed the touch, did not block it
CollisionShape2D 01 · Top-Down Movement

The invisible shape that says which part of a node counts as solid. A body or an area with no CollisionShape2D child touches nothing, which is the most common reason a coin does nothing when you walk over it.

coin.tscn Lesson 09
  • Coin Area2D coin.gd
  • ColorRect
  • CollisionShape2D

It goes UNDER the node that needs to be touchable, never on its own. Leave it off and the coin is invisible to collisions: the player walks through and nothing happens.

Sprite2D 21 · How Sprites Work

The node that draws a picture. Drag an image into its Texture slot and it shows up. The picture is only how the thing looks, never how it collides.

player.tscn Lesson 01
  • TopDownPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

A child of the player, so it moves with it. The Sprite2D is what you SEE; the CollisionShape2D next to it is what you HIT. They are two different nodes on purpose.

$Sprite2D.texture = load("res://art/coin.png")
$Sprite2D.flip_h = true
Timer 11 · Timer Countdown

A node that counts down and sends timeout when it reaches zero. Use it for a shot cooldown, a countdown clock, or a spawner that fires every two seconds.

enemy_spawner.tscn Lesson 08
  • EnemySpawner Node2D enemy_spawner.gd
  • SpawnerPlayer CharacterBody2D % player.tscn
  • SpawnTimer Timer %

A Timer sits in the scene like any other node. It counts down and sends timeout, over and over if you let it.

$Timer.wait_time = 2.0
$Timer.start()      # sends timeout in 2 seconds
Camera2D 19 · Camera Follow

Decides which part of the world is on screen. Make it a child of the player and the view follows the player for free.

player.tscn Lesson 19
  • CameraPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D
  • Camera Camera2D %

A CHILD of the player, and that is the whole trick: children move with their parent, so the view follows without a single line of code.

CanvasLayer 09 · Collect Coins and Items

A layer that stays put on screen even when the camera moves. Your score, your hearts, and your menus go in here so they do not slide away with the world.

hud.tscn Lesson 09
  • HUD CanvasLayer hud.gd
  • MarginContainer
  • ScoreLabel Label %
  • HomeButton Button %

Everything underneath it is drawn on the SCREEN instead of in the world, so the score stays in its corner while the camera moves.

Label 09 · Collect Coins and Items

Draws text on screen. Set its text from code to show a score, a countdown, or the words GAME OVER.

hud.tscn Lesson 09
  • HUD CanvasLayer hud.gd
  • MarginContainer
  • ScoreLabel Label %
  • HomeButton Button %

Green rows are UI nodes. The % is Access as Unique Name, which is what lets the script say %ScoreLabel instead of spelling out the path through MarginContainer.

%ScoreLabel.text = "Score: " + str(score)
Button 15 · Main Menu

A thing the player can click. It sends the pressed signal, which is how a Play button starts the game.

main_menu.tscn Lesson 15
  • MainMenu Control main_menu.gd
  • MenuPanel VBoxContainer %
  • TitleLabel Label
  • PlayButton Button %
  • OptionsButton Button %
  • QuitButton Button %

A Button sends pressed when it is clicked, and you connect that to one of your functions. Everything green here is a UI node. (The real menu has two more panels below this one.)

%PlayButton.pressed.connect(_on_play_pressed)

func _on_play_pressed():
    get_tree().change_scene_to_file("res://level1.tscn")
AudioStreamPlayer 16 · Sound Effects

The node that plays a sound. Put the sound file in its Stream slot, then call .play() when the coin is picked up.

sound_effects.tscn Lesson 16
  • SoundEffects Node2D sound_effects.gd
  • SfxPlayer CharacterBody2D player.tscn
  • PickupSound AudioStreamPlayer %
  • JumpSound AudioStreamPlayer %
  • HUD CanvasLayer % hud.tscn

One node per sound, sitting in the scene with everything else. After that, $PickupSound.play() is the entire thing. (The real scene also has a PickupArea the player walks into.)

$PickupSound.play()
AnimationPlayer 18 · Simple Animation

Changes a value over time and can loop it: a coin that spins, a door that slides open, a sprite that flashes red when it is hit.

player.tscn Lesson 18
  • AnimPlayer CharacterBody2D player.gd
  • Sprite Sprite2D %
  • CollisionShape2D
  • AnimationPlayer %

A sibling of the thing it animates, not a parent of it. It holds the named animations; the script only ever asks for one by name.

$AnimationPlayer.play("spin")
$AnimationPlayer.play("hit_flash")
Marker2D 05 · Shooting Bullets

An invisible node used only to mark a spot. Ours sits at the tip of the gun, so bullets start there instead of in the middle of the player.

player.tscn Lesson 05
  • ShooterPlayer CharacterBody2D player.gd
  • Body Sprite2D
  • Gun ColorRect
  • Muzzle Marker2D %
  • ShootTimer Timer %
  • CollisionShape2D

An empty point that moves with the gun. Spawn the bullet at $Muzzle.global_position and it leaves the barrel every time, whichever way the player is facing.

var bullet = bullet_scene.instantiate()
bullet.global_position = $Muzzle.global_position
PackedScene 08 · Enemy Spawner

A saved scene file sitting ready to be copied, like a cookie cutter. It is not in the game yet.

spawn_enemy.tscn Lesson 08
  • SpawnEnemy CharacterBody2D spawn_enemy.gd
  • ColorRect
  • CollisionShape2D

This scene is not in the level anywhere. It is the cookie cutter the spawner loads and copies while the game runs.

@export var enemy_scene: PackedScene

# the cookie cutter, not a cookie yet
instance 08 · Enemy Spawner

One real copy of a scene, alive in the game. instantiate() makes the copy and add_child() puts it in, which is how one bullet scene becomes fifty bullets.

coin_pickup.tscn Lesson 09
  • CoinPickup Node2D coin_pickup.gd
  • Player CharacterBody2D player.tscn
  • Coin1 Area2D coin.tscn
  • Coin2 Area2D coin.tscn
  • Coin3 Area2D coin.tscn
  • HUD CanvasLayer hud.tscn

Three copies of coin.tscn, plus one of player.tscn and one of hud.tscn. The teal badge is the file each copy came from: edit the coin scene once and all three change.

var enemy = enemy_scene.instantiate()   # one real copy
add_child(enemy)                       # now it is in the game

Scripts

What the code attached to a node is made of, and the handful of lines that show up in almost every lesson.

script 01 · Top-Down Movement

A .gd file attached to a node that says what that node does. The node is the thing; the script is its behaviour.

player.tscn Lesson 01
  • TopDownPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D

The badge on the right is the script file attached to that node. One script per node at most, and most nodes have none.

# player.gd, attached to the Player node
extends CharacterBody2D

var speed = 200
extends 01 · Top-Down Movement

The first line of every script. extends CharacterBody2D means this script is attached to a CharacterBody2D and already knows everything that node can do.

extends CharacterBody2D   # always the first line
func 01 · Top-Down Movement

Starts a function: a named block of lines that runs when something calls it. Everything indented under it belongs to it.

func take_damage(amount):
    health -= amount      # indented, so it belongs to the func
    if health <= 0:
        queue_free()
var 01 · Top-Down Movement

Makes a variable, a named box holding a value the script can read and change later, like var speed = 200.

var speed = 200
var health = 3
speed = 400               # a variable can change later
@export 01 · Top-Down Movement

Puts a variable in Godot’s Inspector so you can change it without editing code. Speed, jump height, and health all belong here, because no number is right on the first try.

@export var speed = 200
@export var jump_height = 400
# both now show up in the Inspector
@onready 09 · Collect Coins and Items

Grabs a node AFTER the scene is built instead of before. Without it, the script goes looking for a node that does not exist yet and you get a null error.

@onready var label = %ScoreLabel
# waits until the scene exists, so label is never null
%NodeName 09 · Collect Coins and Items

The shortcut for a node marked "Access as Unique Name". %ScoreLabel finds that node anywhere in the scene, so moving it in the tree does not break your code. $Sprite2D is the other way, spelling out the exact path.

hud.tscn Lesson 09
  • HUD CanvasLayer hud.gd
  • MarginContainer
  • ScoreLabel Label %
  • HomeButton Button %

The % is switched on per node in the Scene dock. It is what makes %ScoreLabel work from anywhere in this scene, even though the label is buried under a container.

%ScoreLabel.text = "0"    # found anywhere in the scene
$Sprite2D.flip_h = true   # the exact path, from this node
_ready() 03 · Walls and Collisions

A function Godot runs ONCE, the moment the node joins the game. One-time setup lives here: connect your signals, grab your nodes, set the starting score.

func _ready():
    score = 0             # runs once, when the node joins the game
    $Timer.start()
_physics_process(delta) 01 · Top-Down Movement

Runs 60 times a second, in step with the physics engine. Anything about moving a body or bumping into things goes here.

func _physics_process(delta):
    velocity.x = 200      # 60 times a second
    move_and_slide()
_process(delta) 04 · Mouse Aiming

Runs every drawn frame, which is as fast as the computer can manage. Use it for looks only: camera smoothing, spinning a coin, updating a label.

func _process(delta):
    rotation += 2 * delta   # looks only, every drawn frame
delta 02 · Platformer Movement

How many seconds went by since the last frame, usually about 0.016. Multiplying by it turns "a bit every frame" into "the same amount every second", so your game plays the same on a fast computer and a slow one.

position.x += speed * delta
# same distance per SECOND on a fast or slow computer
Vector2 01 · Top-Down Movement

Two numbers that work like an arrow: x and y together. Positions, directions, and speeds are all Vector2. On screen, y gets bigger going DOWN.

var dir = Vector2(1, 0)    # right
var up = Vector2(0, -1)    # up, because y grows downward
position += dir * speed
velocity 01 · Top-Down Movement

A CharacterBody2D’s built-in Vector2 for how fast it is moving and which way. Setting it does nothing on its own until you call move_and_slide().

velocity = Vector2(200, 0)   # sets it, moves nothing yet
move_and_slide()             # now it moves
move_and_slide() 01 · Top-Down Movement

Takes the velocity you set and actually moves the body, sliding it along walls instead of sticking. One line, and collisions are handled for you.

velocity = Vector2(200, 0)
move_and_slide()   # moves, and slides along walls
Input 01 · Top-Down Movement

How you ask what the player is doing right now. Input.is_action_pressed("move_right") is true while that key is held. The action names come from Project Settings, so changing the key never touches your code.

if Input.is_action_pressed("move_right"):
    velocity.x = speed
# "move_right" is named in Project Settings, not here
queue_free() 03 · Walls and Collisions

Deletes a node, safely, at the end of the current frame. This is how a coin disappears when you touch it and how a bullet cleans itself up.

func _on_body_entered(body):
    queue_free()   # the coin removes itself
class_name 02 · Platformer Movement

Gives a script a name other scripts can check for. Write class_name Player in player.gd and any script can ask if body is Player, which is how a coin knows the player touched it and not an enemy.

# in player.gd
class_name Player

# in coin.gd
if body is Player:
    queue_free()

Signals

How one node tells the rest of the game that something happened, without the two files knowing about each other.

signal 09 · Collect Coins and Items

A message a node sends out when something happens. A signal is not a function: it does not do anything by itself, it just announces. Whoever cares is listening.

signal coin_collected(value)
# declared, but nothing has happened yet
emit 09 · Collect Coins and Items

To send the signal. coin_collected.emit(value) shouts "a coin was collected" to the whole game. If nobody is listening, nothing happens, and that is fine.

coin_collected.emit(1)   # shouts it to whoever is listening
connect 09 · Collect Coins and Items

To start listening. body_entered.connect(_on_body_entered) says "when that signal arrives, run this function of mine". Hand over the function NAME with no (), or you run it right now by accident.

body_entered.connect(_on_body_entered)
# the function NAME, with no () after it
handler 09 · Collect Coins and Items

The function that runs when the signal arrives. By habit its name starts with _on_, as in _on_body_entered, so you can tell at a glance that you never call it yourself.

func _on_body_entered(body):
    queue_free()
# the _on_ prefix means you never call it yourself
body_entered 09 · Collect Coins and Items

A signal every Area2D already has. It fires when a body moves into the area and hands you the body that did it, so the coin can check whether it was the player.

func _on_body_entered(body):
    if body is Player:
        queue_free()   # a coin the player touched
timeout 11 · Timer Countdown

The signal a Timer sends when it hits zero. Connect it to spawn the next enemy, end the round, or let the player shoot again.

$Timer.timeout.connect(_on_timer_timeout)

func _on_timer_timeout():
    spawn_enemy()
pressed 15 · Main Menu

The signal a Button sends when it is clicked. Same pattern as every other signal, which is the point: once you know one, you know them all.

%PlayButton.pressed.connect(_on_play_pressed)

func _on_play_pressed():
    start_game()
signal bus 09 · Collect Coins and Items

One shared script holding the signals the whole game cares about, ours is game_events.gd. The coin emits into it and the HUD listens to it, so neither file has to know the other exists.

# in game_events.gd
signal coin_collected(value)

# the coin shouts, the HUD listens, neither knows the other
GameEvents.coin_collected.emit(1)
group 03 · Walls and Collisions

A label you stick on a node with add_to_group("enemies"). Other scripts can then ask is_in_group("enemies") without knowing which file that node came from.

add_to_group("enemies")

if body.is_in_group("enemies"):
    take_damage(1)