All Scrolls

GDScript Cheat Sheet: Everything You Need in One Place

Coding Quests/February 24, 2026/Reference

GDScript Cheat Sheet: Everything You Need in One Place

GDScript is Godot's built-in scripting language. It's designed specifically for game development - fast to write, easy to read, and tightly integrated with the engine. If you're coming from Python, it'll feel familiar. If you're coming from C# or JavaScript, you'll appreciate how little boilerplate it needs.

This is a reference, not a tutorial. Bookmark it and come back when you need to remember how something works.

Variables and Types

GDScript
# Basic types
var health: int = 100
var speed: float = 5.5
var name: String = "Player"
var alive: bool = true
var direction: Vector2 = Vector2(1, 0)
var position: Vector3 = Vector3.ZERO
# Type inference
var score := 0 # int
var damage := 10.5 # float
var label := "Hello" # String
# Constants
const MAX_SPEED: float = 10.0
const GRAVITY: float = 9.8
# Enums
enum State { IDLE, RUN, JUMP, ATTACK }
var current_state: State = State.IDLE
# Exports (editable in Inspector)
@export var max_health: int = 100
@export var walk_speed: float = 3.0
@export var player_name: String = "Hero"
@export var item_scene: PackedScene
@export_range(0, 100, 1) var volume: int = 80
@export_enum("Sword", "Axe", "Bow") var weapon: String
@export_group("Movement")
@export var run_speed: float = 6.0
@export var jump_force: float = 8.0

Functions

GDScript
# Basic function
func take_damage(amount: int) -> void:
health -= amount
# Return value
func get_speed() -> float:
return speed * speed_multiplier
# Default parameters
func heal(amount: int = 10) -> void:
health = mini(health + amount, max_health)
# Optional return type
func is_alive() -> bool:
return health > 0
# Static function (callable without instance)
static func calculate_damage(attack: int, defense: int) -> int:
return maxi(attack - defense, 1)
# Lambda / anonymous function
var double := func(x: int) -> int: return x * 2

Control Flow

GDScript
# If / elif / else
if health <= 0:
die()
elif health < 20:
show_warning()
else:
regenerate()
# Match (like switch)
match current_state:
State.IDLE:
play_idle()
State.RUN:
play_run()
State.JUMP, State.ATTACK:
# Multiple values
play_action()
_:
# Default
pass
# Ternary
var label := "Dead" if health <= 0 else "Alive"
# For loops
for i in range(10): # 0 to 9
print(i)
for item in inventory: # Iterate array
print(item.name)
for key in stats: # Iterate dictionary keys
print(key, stats[key])
# While loop
while not is_on_floor():
velocity.y -= gravity * delta

Collections

GDScript
# Arrays
var items: Array[String] = ["Sword", "Shield", "Potion"]
items.append("Bow")
items.remove_at(0)
items.has("Shield") # true
items.size() # 3
items.find("Potion") # index or -1
items.sort()
items.shuffle()
items.filter(func(i): return i != "Bow")
items.map(func(i): return i.to_upper())
# Typed arrays
var enemies: Array[Enemy] = []
var numbers: Array[int] = [1, 2, 3]
# Dictionary
var stats: Dictionary = {
"health": 100,
"strength": 15,
"defense": 10,
}
stats["health"] # 100
stats.get("mana", 0) # 0 (default)
stats.has("strength") # true
stats.keys() # Array of keys
stats.values() # Array of values
stats.erase("defense")
stats.merge({"speed": 5})

Strings

GDScript
var name := "Adventurer"
# String operations
name.length() # 10
name.to_upper() # "ADVENTURER"
name.to_lower() # "adventurer"
name.begins_with("Adv") # true
name.contains("vent") # true
name.replace("er", "or") # "Adventuror"
name.split("e") # ["Adv", "ntur", "r"]
# String formatting
var msg := "HP: %d / %d" % [health, max_health]
var pos := "Position: %.2f, %.2f" % [x, y]
# String interpolation (Godot 4.x - no built-in f-strings, use % or +)
var greeting := "Hello, " + name + "!"

Node Lifecycle

GDScript
extends CharacterBody3D
# Called when node enters the scene tree
func _ready() -> void:
pass
# Called every frame (variable delta)
func _process(delta: float) -> void:
pass
# Called at fixed rate (default 60/sec) - use for physics
func _physics_process(delta: float) -> void:
pass
# Called for unhandled input events
func _unhandled_input(event: InputEvent) -> void:
pass
# Called for all input events
func _input(event: InputEvent) -> void:
pass
# Called when node exits the scene tree
func _exit_tree() -> void:
pass

Input

GDScript
# Polling (check state right now)
if Input.is_action_pressed("move_forward"): # Held down
move()
if Input.is_action_just_pressed("jump"): # Just pressed this frame
jump()
if Input.is_action_just_released("attack"): # Just released this frame
stop_charging()
# Movement vector (combines 4 directional inputs into Vector2)
var input := Input.get_vector("left", "right", "forward", "back")
# Mouse
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
# Event-based (in _unhandled_input or _input)
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
rotate_y(-event.relative.x * sensitivity)
if event is InputEventKey:
if event.pressed and event.keycode == KEY_ESCAPE:
get_tree().quit()

Signals

GDScript
# Define a signal
signal health_changed(new_health: int)
signal died
# Emit a signal
func take_damage(amount: int) -> void:
health -= amount
health_changed.emit(health)
if health <= 0:
died.emit()
# Connect a signal (in code)
func _ready() -> void:
$Enemy.died.connect(_on_enemy_died)
func _on_enemy_died() -> void:
score += 100
# Connect with lambda
$Button.pressed.connect(func(): print("clicked"))
# One-shot connection (auto-disconnects after firing once)
$Timer.timeout.connect(_on_timeout, CONNECT_ONE_SHOT)
# Disconnect
$Enemy.died.disconnect(_on_enemy_died)
# Check if connected
$Enemy.died.is_connected(_on_enemy_died)

Node References

GDScript
# Get child node (same scene)
@onready var sprite: Sprite2D = $Sprite2D
@onready var health_bar: ProgressBar = $UI/HealthBar
@onready var anim: AnimationPlayer = $AnimationPlayer
# Export (set in Inspector - survives restructuring)
@export var target: Node3D
@export var weapon_scene: PackedScene
# Get node by path
var player := get_node("/root/Main/Player")
# Get parent
var parent := get_parent()
# Get children
for child in get_children():
if child is Enemy:
child.take_damage(10)
# Groups
add_to_group("enemies")
if is_in_group("enemies"):
pass
var all_enemies := get_tree().get_nodes_in_group("enemies")
# Find nodes
var camera := get_viewport().get_camera_3d()

Scenes and Instantiation

GDScript
# Load a scene
var enemy_scene: PackedScene = preload("res://scenes/enemy.tscn")
# Instantiate
var enemy := enemy_scene.instantiate()
add_child(enemy)
enemy.global_position = Vector3(10, 0, 5)
# Remove from scene
enemy.queue_free()
# Change scene
get_tree().change_scene_to_file("res://scenes/game_over.tscn")
get_tree().change_scene_to_packed(preload("res://scenes/menu.tscn"))
# Reload current scene
get_tree().reload_current_scene()

Physics and Movement (CharacterBody3D)

GDScript
extends CharacterBody3D
@export var speed: float = 5.0
@export var jump_force: float = 8.0
@export var gravity: float = 20.0
func _physics_process(delta: float) -> void:
# Gravity
if not is_on_floor():
velocity.y -= gravity * delta
# Jump
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_force
# Movement
var input := Input.get_vector("left", "right", "forward", "back")
velocity.x = input.x * speed
velocity.z = input.y * speed
move_and_slide()
# Useful checks after move_and_slide()
is_on_floor()
is_on_wall()
is_on_ceiling()
get_last_slide_collision()

Timers

GDScript
# Create timer in code
var timer := Timer.new()
timer.wait_time = 2.0
timer.one_shot = true
timer.timeout.connect(_on_timer_done)
add_child(timer)
timer.start()
# Quick one-shot timer
await get_tree().create_timer(1.5).timeout
print("1.5 seconds later")
# Scene tree timer (no node needed)
get_tree().create_timer(0.5).timeout.connect(func(): print("done"))

Tweens

GDScript
# Create tween
var tween := create_tween()
# Move to position over 0.5 seconds
tween.tween_property(self, "position", Vector3(10, 0, 0), 0.5)
# Chain animations
tween.tween_property(self, "modulate:a", 0.0, 0.3) # Fade out
tween.tween_callback(queue_free) # Then remove
# Parallel tweens
tween.set_parallel(true)
tween.tween_property(self, "position:y", 5.0, 0.3)
tween.tween_property(self, "rotation:y", PI, 0.3)
# Easing
tween.tween_property(self, "position", target, 0.5)\
.set_ease(Tween.EASE_OUT)\
.set_trans(Tween.TRANS_CUBIC)
# Loop
tween.set_loops(3) # Repeat 3 times
tween.set_loops(0) # Loop forever

Resources

GDScript
# Define a custom Resource
class_name ItemData
extends Resource
@export var id: String
@export var name: String
@export var icon: Texture2D
@export var value: int
# Load a resource
var sword: ItemData = load("res://data/items/sword.tres")
var sword2: ItemData = preload("res://data/items/sword.tres") # Compile-time
# Create at runtime
var item := ItemData.new()
item.name = "Potion"
item.value = 50
# Save a resource
ResourceSaver.save(item, "user://custom_item.tres")

File I/O

GDScript
# Write JSON
func save_data(data: Dictionary) -> void:
var file := FileAccess.open("user://save.json", FileAccess.WRITE)
file.store_string(JSON.stringify(data, "\t"))
# Read JSON
func load_data() -> Dictionary:
if not FileAccess.file_exists("user://save.json"):
return {}
var file := FileAccess.open("user://save.json", FileAccess.READ)
var json := JSON.new()
if json.parse(file.get_as_text()) == OK:
return json.data
return {}
# ConfigFile (INI-style)
var config := ConfigFile.new()
config.set_value("audio", "volume", 0.8)
config.save("user://settings.cfg")
config.load("user://settings.cfg")
var vol: float = config.get_value("audio", "volume", 1.0)
# Check paths
FileAccess.file_exists("user://save.json")
DirAccess.dir_exists_absolute("user://saves/")

Math Helpers

GDScript
# Common math
abs(-5) # 5
sign(-3.2) # -1.0
clamp(value, 0, 100) # Constrain to range
clampf(0.5, 0.0, 1.0) # Float version
clampi(50, 0, 100) # Int version
mini(a, b) # Smaller int
maxi(a, b) # Larger int
minf(a, b) # Smaller float
maxf(a, b) # Larger float
# Interpolation
lerp(0.0, 100.0, 0.5) # 50.0
lerpf(a, b, t) # Float lerp
lerp_angle(a, b, t) # Angle lerp (handles wrapping)
# Random
randf() # 0.0 to 1.0
randi() # Random int
randf_range(1.0, 10.0) # Float in range
randi_range(1, 6) # Int in range (inclusive)
# Vectors
var v := Vector3(1, 2, 3)
v.normalized() # Unit vector
v.length() # Magnitude
v.distance_to(other) # Distance between two vectors
v.direction_to(other) # Normalized direction
v.lerp(other, 0.5) # Halfway between
v.dot(other) # Dot product
v.cross(other) # Cross product (3D)
# Angles
deg_to_rad(90.0) # 1.5708
rad_to_deg(PI) # 180.0
atan2(y, x) # Angle from components

Autoloads (Singletons)

GDScript
# Set up: Project → Project Settings → Autoload → Add script
# GameManager.gd (autoloaded as "GameManager")
extends Node
var score: int = 0
var current_level: int = 1
signal score_changed(new_score: int)
func add_score(amount: int) -> void:
score += amount
score_changed.emit(score)
# Use from anywhere:
GameManager.add_score(100)
GameManager.score_changed.connect(_on_score_changed)

Coroutines (Await)

GDScript
# Wait for a signal
await $AnimationPlayer.animation_finished
# Wait for a timer
await get_tree().create_timer(2.0).timeout
# Wait for a tween
var tween := create_tween()
tween.tween_property(self, "position:y", 10.0, 1.0)
await tween.finished
# Async function
func fade_out() -> void:
var tween := create_tween()
tween.tween_property(self, "modulate:a", 0.0, 0.5)
await tween.finished
queue_free()

Class Inheritance

GDScript
# Base class
# weapon.gd
class_name Weapon
extends Node3D
@export var damage: int = 10
@export var attack_speed: float = 1.0
func attack() -> void:
print("Base attack")
# Derived class
# sword.gd
class_name Sword
extends Weapon
func _ready() -> void:
damage = 25
func attack() -> void:
super.attack() # Call parent method
print("Sword slash!")
# Type checking
if weapon is Sword:
weapon.special_ability()

Common Patterns

GDScript
# Null-safe access
var health_bar := get_node_or_null("UI/HealthBar")
if health_bar:
health_bar.value = health
# Deferred calls (safe for physics/tree changes)
call_deferred("add_child", new_node)
set_deferred("monitoring", true)
queue_free() # Already deferred
# Process mode (pause behavior)
process_mode = Node.PROCESS_MODE_ALWAYS # Runs during pause
process_mode = Node.PROCESS_MODE_PAUSABLE # Stops during pause (default)
process_mode = Node.PROCESS_MODE_DISABLED # Never runs
# Pause the game
get_tree().paused = true
get_tree().paused = false

This covers the most-used parts of GDScript. For the full API reference, the official Godot docs are comprehensive.

If you want to learn these patterns by building real game systems - not just reading syntax - check out the quest board. Every course teaches GDScript through hands-on projects: inventory systems, combat, AI, save/load, and more.

gdscriptreferencebeginner