GDScript Cheat Sheet
Last updated
Basics and output
A script extends a node type, and Godot calls its functions for you.
| Operation | Syntax |
|---|---|
| Attach the script to a node type | extends Node |
| Code that runs when the node starts | func _ready() -> void:
print("Hello, World!") |
| Print values with no separator | print("HP: ", hp) |
| Print values separated by spaces | prints("a", "b", 3) |
| Comment | # This is a comment |
| Documentation comment | ## Shown in the editor's help |
| Multi-line string | var text := """Line one
Line two""" |
| Report an error | push_error("Health is negative") |
| Report a warning | push_warning("Old save file") |
Variables, constants and types
Types are optional. Add them for editor checks, autocompletion and faster code.
| Operation | Syntax |
|---|---|
| Variable (dynamic type) | var score = 0 |
| Variable with a static type | var speed: float = 200.0 |
| Infer the static type | var lives := 3 |
| Constant | const MAX_HP = 100 |
| Enum | enum State { IDLE, RUN, JUMP } |
| Use an enum value | var state: State = State.IDLE |
| Vector and color values | var pos := Vector2(10, 20)
var tint := Color(1, 0, 0) |
| Empty object reference | var target: Node = null |
| Convert to text, int or float | str(42), int("42"), float("3.5") |
| Float to int (drops the decimals) | int(3.9) # 3 |
| Check a type | value is int, node is Sprite2D |
| Cast to a type | var sprite := node as Sprite2D |
Operators
Familiar math, plus English words for logic.
| Operation | Syntax |
|---|---|
| Add, subtract, multiply, divide | a + b, a - b, a * b, a / b |
| Integer division (int / int) | 7 / 2 # 3 |
| Remainder | 7 % 3, fmod(7.5, 2.0) |
| Power | 2 ** 10 # 1024 |
| Compound assignment | x += 1, x -= 1, x *= 2, x /= 2 |
| Comparison | ==, !=, <, >, <=, >= |
| Logic | a and b, a or b, not a |
| Logic (symbol form) | a && b, a || b, !a |
| Inline if | var label := "win" if score > 10 else "lose" |
| Is it in a collection | "sword" in inventory |
| Bitwise | a & b, a | b, a ^ b, ~a, a << 1, a >> 1 |
Control flow
Blocks are set by indentation, like Python. Godot indents with tabs by default.
| Operation | Syntax |
|---|---|
| If, elif, else | if hp <= 0:
die()
elif hp < 20:
print("Low HP")
else:
print("OK") |
| One-line if | if hp <= 0: die() |
| Loop over a range | for i in range(5):
print(i) # 0 to 4 |
| Range with start, end and step | for i in range(10, 0, -2):
print(i) # 10, 8, 6, 4, 2 |
| Repeat n times | for i in 3:
print(i) # 0, 1, 2 |
| Loop over an array | for item in items:
print(item) |
| Typed loop variable (Godot 4.2+) | for n: String in names:
print(n.to_upper()) |
| While loop | while hp > 0:
hp -= 10 |
| Leave or skip a loop step | break, continue |
| Empty block placeholder | pass |
Pattern matching (match)
match runs the first branch whose pattern fits. There is no fallthrough.
| Operation | Syntax |
|---|---|
| Match values | match state:
State.IDLE:
print("idle")
State.RUN:
print("run") |
| Several values in one branch | match key:
"w", "up":
move_up() |
| Default branch (wildcard) | match n:
0:
print("zero")
_:
print("other") |
| Bind the value to a name | match cmd:
"quit":
get_tree().quit()
var other:
print("Unknown: ", other) |
| Array pattern | match pos:
[0, 0]:
print("origin")
[var x, 0]:
print("on the x axis at ", x) |
| Array with any tail | match args:
["move", ..]:
print("move command") |
| Dictionary pattern | match item:
{"type": "coin", "value": var v}:
gold += v |
| Guard with when (Godot 4.4+) | match score:
var s when s >= 90:
print("A")
var s when s >= 75:
print("B")
_:
print("C") |
Functions and lambdas
Functions are values too: store them, pass them, and bind arguments to them.
| Operation | Syntax |
|---|---|
| Typed parameters and return | func add(a: int, b: int) -> int:
return a + b |
| No return value | func reset() -> void:
score = 0 |
| Default argument | func greet(who := "player") -> void:
print("Hi, ", who) |
| Static function | static func double(x: int) -> int:
return x * 2 |
| One-line lambda | var square := func(x): return x * x |
| Call a Callable | square.call(4) # 16 |
| Function as a value | var on_done := reset
on_done.call() |
| Bind arguments | var add_ten := add.bind(10)
print(add_ten.call(5)) # 15 |
| Multi-line lambda | var greet_all := func(names: Array) -> void:
for n in names:
print("Hi, ", n) |
| Wait for a timer | await get_tree().create_timer(1.0).timeout |
| Wait for a coroutine | var data = await fetch_data() |
Strings
String methods return a new string and never change the original.
| Operation | Syntax |
|---|---|
| Format with % | "%s has %d HP" % [name, hp] |
| Two decimal places | "%.2f" % 3.14159 # "3.14" |
| Format with named placeholders | "{a} vs {b}".format({"a": 1, "b": 2}) |
| Join with + | "Score: " + str(score) |
| Length | text.length() |
| Upper or lower case | text.to_upper(), text.to_lower() |
| Split into parts | "a,b,c".split(",") # ["a", "b", "c"] |
| Join parts | ", ".join(["a", "b", "c"]) |
| Trim whitespace | " hi ".strip_edges() # "hi" |
| Replace text | text.replace("cat", "dog") |
| Find a position | text.find("lo") # -1 if missing |
| Starts or ends with | text.begins_with("Hi"), text.ends_with("!") |
| Substring | text.substr(0, 5) |
| Contains | "ell" in "Hello" # true |
Arrays
Arrays grow and shrink. A typed array only accepts its element type.
| Operation | Syntax |
|---|---|
| Array literal (mixed types) | var items = [1, "two", 3.0] |
| Typed array | var scores: Array[int] = [10, 20, 30] |
| Read by index (negative counts from the end) | scores[0], scores[-1] |
| Add to the end | scores.append(40), scores.push_back(40) |
| Insert at an index | scores.insert(0, 5) |
| Remove and return the last | var last = scores.pop_back() |
| Remove by value or by index | scores.erase(20), scores.remove_at(0) |
| Contains, index of | scores.has(30), scores.find(30) |
| Length, empty check | scores.size(), scores.is_empty() |
| Sort | scores.sort() |
| Sort with your own rule | enemies.sort_custom(func(a, b): return a.hp < b.hp) |
| Map | scores.map(func(x): return x * 2) |
| Filter | scores.filter(func(x): return x > 15) |
| Reduce | scores.reduce(func(acc, x): return acc + x, 0) |
| Slice (end excluded) | scores.slice(1, 3) |
Dictionaries
Key and value pairs. Keys can be any type; strings are the most common.
| Operation | Syntax |
|---|---|
| Dictionary literal | var stats = {"hp": 100, "mp": 30} |
| Typed dictionary (Godot 4.4+) | var prices: Dictionary[String, int] = {"sword": 50} |
| Read or write a key | stats["hp"], stats["xp"] = 50 |
| Read with a default | stats.get("luck", 0) |
| Has a key | stats.has("hp"), "hp" in stats |
| Remove a key | stats.erase("mp") |
| Keys and values | stats.keys(), stats.values() |
| Number of entries | stats.size() |
| Merge another dictionary (true overwrites) | stats.merge({"luck": 5}), stats.merge(bonus, true) |
| Loop over keys | for key in stats:
print(key, " = ", stats[key]) |
Classes and annotations
Every script is a class. Annotations start with @.
| Operation | Syntax |
|---|---|
| Give the class a global name | class_name Player
extends CharacterBody2D |
| Constructor | func _init(start_hp := 100) -> void:
hp = start_hp |
| Create an instance | var p := Player.new() |
| Inner class | class Item:
var name := ""
var price := 0 |
| Call the parent version of this method | func _ready() -> void:
super()
print("child ready") |
| Call another parent method | super.take_damage(10) |
| Static variable (Godot 4.1+) | static var count := 0 |
| Show in the Inspector | @export var speed := 300.0 |
| Show with a slider range | @export_range(0, 100) var volume := 50 |
| Get a child node when ready | @onready var sprite: Sprite2D = $Sprite2D |
| Setter and getter | var hp := 100:
set(value):
hp = clampi(value, 0, 100)
get:
return hp |
| Run the script in the editor | @tool |
Signals
Signals are first-class: declare them, then emit, connect and await them directly.
| Operation | Syntax |
|---|---|
| Declare a signal | signal died |
| Signal with typed arguments | signal health_changed(old_hp: int, new_hp: int) |
| Emit | died.emit() |
| Emit with arguments | health_changed.emit(100, 80) |
| Connect to a method | player.died.connect(_on_player_died) |
| Connect a lambda | button.pressed.connect(func(): print("Clicked")) |
| Pass extra arguments | button.pressed.connect(_on_pressed.bind("start")) |
| Fire only once | timer.timeout.connect(_on_timeout, CONNECT_ONE_SHOT) |
| Disconnect | player.died.disconnect(_on_player_died) |
| Check a connection | player.died.is_connected(_on_player_died) |
| Wait for a signal | await player.died |
Nodes and lifecycle
The callbacks Godot calls on your node, and the calls you make on the scene tree.
| Operation | Syntax |
|---|---|
| Object created (before the tree) | func _init() -> void:
print("created") |
| Node and its children are ready | func _ready() -> void:
print("ready") |
| Every frame | func _process(delta: float) -> void:
position.x += 100.0 * delta |
| Every physics tick | func _physics_process(delta: float) -> void:
velocity.y += 980.0 * delta
move_and_slide() |
| Input the UI did not use | func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("jump"):
jump() |
| Get a node by path | get_node("Sprite2D"), $Sprite2D |
| Path with spaces, unique name | $"UI/Health Bar", %HealthBar |
| Check an input action | Input.is_action_pressed("move_right") |
| Create an instance of a scene | var enemy := preload("res://enemy.tscn").instantiate()
add_child(enemy) |
| Delete a node safely | queue_free() |
| Parent and children | get_parent(), get_children() |
| Change the scene | get_tree().change_scene_to_file("res://main.tscn") |
The GDScript syntax you reach for while building a Godot game, on one page. This GDScript cheat sheet is a quick reference for Godot 4: variables and static types, operators, control flow and match, functions and lambdas, strings, arrays and dictionaries, classes, signals, and the node callbacks Godot runs every frame.
Everything here is Godot 4 syntax, so you will see @export, @onready, died.emit() and await rather than the Godot 3 forms. Copy what you need into your script, or try the language snippets live in the GDScript playground: put them inside _ready() and run them in your browser, with no Godot install.
GDScript cheat sheet FAQ
Is this GDScript cheat sheet free?
Is this cheat sheet for Godot 4 or Godot 3?
@export and @onready annotations instead of the export and onready keywords, died.emit() and died.connect(callable) instead of emit_signal("died") and string-based connect, await instead of yield, and super() instead of .method(). Rows marked 4.1+, 4.2+ or 4.4+ need at least that Godot version.What is the difference between = and := in GDScript?
var x = 5 creates a dynamic variable that can later hold any type. var x := 5 infers the type from the value (here int) and locks it, so assigning a string later is an error. var x: int = 5 states the type explicitly. Static types give you editor errors, better autocompletion and faster code.How do signals work in GDScript?
signal died, send it with died.emit(), and react to it from another node with player.died.connect(_on_player_died). You can connect a lambda instead of a method, bind extra arguments with .bind(), or pause a function until the signal fires with await player.died.Is GDScript like Python?
for ... in, if/elif/else and English words like and, or and not. But GDScript is its own language, built for Godot. It has optional static types, signals, annotations such as @export, and direct access to nodes with $.Can I practice GDScript online?
_ready() runs once. When you want structure, Coddy's free interactive GDScript course takes you from variables and functions to classes and signals step by step.