Menu
CoddyTech

GDScript Cheat Sheet

Last updated

Basics and output

A script extends a node type, and Godot calls its functions for you.

OperationSyntax
Attach the script to a node typeextends Node
Code that runs when the node startsfunc _ready() -> void: print("Hello, World!")
Print values with no separatorprint("HP: ", hp)
Print values separated by spacesprints("a", "b", 3)
Comment# This is a comment
Documentation comment## Shown in the editor's help
Multi-line stringvar text := """Line one Line two"""
Report an errorpush_error("Health is negative")
Report a warningpush_warning("Old save file")

Variables, constants and types

Types are optional. Add them for editor checks, autocompletion and faster code.

OperationSyntax
Variable (dynamic type)var score = 0
Variable with a static typevar speed: float = 200.0
Infer the static typevar lives := 3
Constantconst MAX_HP = 100
Enumenum State { IDLE, RUN, JUMP }
Use an enum valuevar state: State = State.IDLE
Vector and color valuesvar pos := Vector2(10, 20) var tint := Color(1, 0, 0)
Empty object referencevar target: Node = null
Convert to text, int or floatstr(42), int("42"), float("3.5")
Float to int (drops the decimals)int(3.9) # 3
Check a typevalue is int, node is Sprite2D
Cast to a typevar sprite := node as Sprite2D

Operators

Familiar math, plus English words for logic.

OperationSyntax
Add, subtract, multiply, dividea + b, a - b, a * b, a / b
Integer division (int / int)7 / 2 # 3
Remainder7 % 3, fmod(7.5, 2.0)
Power2 ** 10 # 1024
Compound assignmentx += 1, x -= 1, x *= 2, x /= 2
Comparison==, !=, <, >, <=, >=
Logica and b, a or b, not a
Logic (symbol form)a && b, a || b, !a
Inline ifvar label := "win" if score > 10 else "lose"
Is it in a collection"sword" in inventory
Bitwisea & 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.

OperationSyntax
If, elif, elseif hp <= 0: die() elif hp < 20: print("Low HP") else: print("OK")
One-line ifif hp <= 0: die()
Loop over a rangefor i in range(5): print(i) # 0 to 4
Range with start, end and stepfor i in range(10, 0, -2): print(i) # 10, 8, 6, 4, 2
Repeat n timesfor i in 3: print(i) # 0, 1, 2
Loop over an arrayfor item in items: print(item)
Typed loop variable (Godot 4.2+)for n: String in names: print(n.to_upper())
While loopwhile hp > 0: hp -= 10
Leave or skip a loop stepbreak, continue
Empty block placeholderpass

Pattern matching (match)

match runs the first branch whose pattern fits. There is no fallthrough.

OperationSyntax
Match valuesmatch state: State.IDLE: print("idle") State.RUN: print("run")
Several values in one branchmatch key: "w", "up": move_up()
Default branch (wildcard)match n: 0: print("zero") _: print("other")
Bind the value to a namematch cmd: "quit": get_tree().quit() var other: print("Unknown: ", other)
Array patternmatch pos: [0, 0]: print("origin") [var x, 0]: print("on the x axis at ", x)
Array with any tailmatch args: ["move", ..]: print("move command")
Dictionary patternmatch 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.

OperationSyntax
Typed parameters and returnfunc add(a: int, b: int) -> int: return a + b
No return valuefunc reset() -> void: score = 0
Default argumentfunc greet(who := "player") -> void: print("Hi, ", who)
Static functionstatic func double(x: int) -> int: return x * 2
One-line lambdavar square := func(x): return x * x
Call a Callablesquare.call(4) # 16
Function as a valuevar on_done := reset on_done.call()
Bind argumentsvar add_ten := add.bind(10) print(add_ten.call(5)) # 15
Multi-line lambdavar greet_all := func(names: Array) -> void: for n in names: print("Hi, ", n)
Wait for a timerawait get_tree().create_timer(1.0).timeout
Wait for a coroutinevar data = await fetch_data()

Strings

String methods return a new string and never change the original.

OperationSyntax
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)
Lengthtext.length()
Upper or lower casetext.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 texttext.replace("cat", "dog")
Find a positiontext.find("lo") # -1 if missing
Starts or ends withtext.begins_with("Hi"), text.ends_with("!")
Substringtext.substr(0, 5)
Contains"ell" in "Hello" # true

Arrays

Arrays grow and shrink. A typed array only accepts its element type.

OperationSyntax
Array literal (mixed types)var items = [1, "two", 3.0]
Typed arrayvar scores: Array[int] = [10, 20, 30]
Read by index (negative counts from the end)scores[0], scores[-1]
Add to the endscores.append(40), scores.push_back(40)
Insert at an indexscores.insert(0, 5)
Remove and return the lastvar last = scores.pop_back()
Remove by value or by indexscores.erase(20), scores.remove_at(0)
Contains, index ofscores.has(30), scores.find(30)
Length, empty checkscores.size(), scores.is_empty()
Sortscores.sort()
Sort with your own ruleenemies.sort_custom(func(a, b): return a.hp < b.hp)
Mapscores.map(func(x): return x * 2)
Filterscores.filter(func(x): return x > 15)
Reducescores.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.

OperationSyntax
Dictionary literalvar stats = {"hp": 100, "mp": 30}
Typed dictionary (Godot 4.4+)var prices: Dictionary[String, int] = {"sword": 50}
Read or write a keystats["hp"], stats["xp"] = 50
Read with a defaultstats.get("luck", 0)
Has a keystats.has("hp"), "hp" in stats
Remove a keystats.erase("mp")
Keys and valuesstats.keys(), stats.values()
Number of entriesstats.size()
Merge another dictionary (true overwrites)stats.merge({"luck": 5}), stats.merge(bonus, true)
Loop over keysfor key in stats: print(key, " = ", stats[key])

Classes and annotations

Every script is a class. Annotations start with @.

OperationSyntax
Give the class a global nameclass_name Player extends CharacterBody2D
Constructorfunc _init(start_hp := 100) -> void: hp = start_hp
Create an instancevar p := Player.new()
Inner classclass Item: var name := "" var price := 0
Call the parent version of this methodfunc _ready() -> void: super() print("child ready")
Call another parent methodsuper.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 gettervar 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.

OperationSyntax
Declare a signalsignal died
Signal with typed argumentssignal health_changed(old_hp: int, new_hp: int)
Emitdied.emit()
Emit with argumentshealth_changed.emit(100, 80)
Connect to a methodplayer.died.connect(_on_player_died)
Connect a lambdabutton.pressed.connect(func(): print("Clicked"))
Pass extra argumentsbutton.pressed.connect(_on_pressed.bind("start"))
Fire only oncetimer.timeout.connect(_on_timeout, CONNECT_ONE_SHOT)
Disconnectplayer.died.disconnect(_on_player_died)
Check a connectionplayer.died.is_connected(_on_player_died)
Wait for a signalawait player.died

Nodes and lifecycle

The callbacks Godot calls on your node, and the calls you make on the scene tree.

OperationSyntax
Object created (before the tree)func _init() -> void: print("created")
Node and its children are readyfunc _ready() -> void: print("ready")
Every framefunc _process(delta: float) -> void: position.x += 100.0 * delta
Every physics tickfunc _physics_process(delta: float) -> void: velocity.y += 980.0 * delta move_and_slide()
Input the UI did not usefunc _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("jump"): jump()
Get a node by pathget_node("Sprite2D"), $Sprite2D
Path with spaces, unique name$"UI/Health Bar", %HealthBar
Check an input actionInput.is_action_pressed("move_right")
Create an instance of a scenevar enemy := preload("res://enemy.tscn").instantiate() add_child(enemy)
Delete a node safelyqueue_free()
Parent and childrenget_parent(), get_children()
Change the sceneget_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?
Yes. This GDScript cheat sheet is completely free, with no sign-up required. Bookmark it and come back whenever you need the exact syntax for a signal, a match pattern or an array method.
Is this cheat sheet for Godot 4 or Godot 3?
Godot 4. The two versions differ in ways that break copied code: Godot 4 uses the @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?
Declare a signal with 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?
It looks similar: blocks are set by indentation, and it uses 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?
Yes. Open the GDScript playground to run snippets from this cheat sheet in your browser: the playground attaches your script to a node, so code inside _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.
Coddy programming languages illustration

Learn GDScript with Coddy

GET STARTED