GDScript is Godot’s Python-like scripting language. Learn to attach scripts to nodes, use the lifecycle callbacks (_ready, _process, _physics_process), and access other nodes in the tree.
GDScript is Godot’s built-in language — dynamically typed, indentation-based, and deliberately Python-like so it is quick to learn and tightly integrated with the engine. A script extends a node type, adds variables and functions, and can expose variables to the editor with @export. Its close engine integration (node access, signals, the type system) is why most Godot games are written in it.
extends CharacterBody2D # this script IS-A CharacterBody2D
@export var speed := 200.0 # @export -> editable in the Inspector
var health := 100
func take_damage(amount: int) -> void:
health -= amount
if health <= 0:
die()
func die() -> void:
print("Player died")
queue_free() # remove this node from the tree
# GDScript: Python-like syntax, dynamically typed (optional type hints),
# tightly integrated with nodes. 'extends' sets which node type the script is.Godot calls special functions on your node at the right moments. _ready runs once when the node enters the tree (setup). _process(delta) runs every rendered frame (visuals, input polling). _physics_process(delta) runs at a fixed rate for movement and physics — the fixed timestep from the Game Physics course. Putting logic in the right callback is fundamental to correct, smooth games.
extends Node
func _ready() -> void:
# runs ONCE when the node enters the tree -> initialization
print("ready")
func _process(delta: float) -> void:
# runs EVERY rendered frame (variable rate) -> visuals, non-physics logic
# 'delta' = seconds since last frame; multiply movement by it
pass
func _physics_process(delta: float) -> void:
# runs at a FIXED rate (default 60/s) -> movement, physics, collisions
# use this for anything physics-related (see the Game Physics course)
passScripts interact with the node tree to read and control other nodes. The $ shorthand grabs a child by name ($Sprite2D), get_node handles paths, and @onready caches a reference once the node is ready. This is how a Player script tells its AnimationPlayer to play, or reads its CollisionShape — the everyday glue between a script and its scene.
extends CharacterBody2D
# @onready waits until the node is in the tree, then caches the reference
@onready var sprite := $Sprite2D # $ = get a child by name
@onready var anim := $AnimationPlayer
@onready var hud := get_node("../UI/HUD") # a path to elsewhere in the tree
func _ready() -> void:
anim.play("idle")
func face(direction: float) -> void:
sprite.flip_h = direction < 0 # control the child sprite
anim.play("run" if direction != 0 else "idle")
# $Name is shorthand for get_node("Name"). @onready caches it once ready.Both _process and _physics_process receive delta — the time since the last call — and you multiply movement and timers by it so the game runs at the same real speed regardless of frame rate, exactly as the Game Math course stressed. Forgetting delta makes a game run faster on a fast machine, the classic beginner bug. Physics logic belongs in _physics_process; visual-only logic in _process.
Always use delta required — Multiply movement, timers, and rotation by delta so behavior is frame-rate independent. Omitting it makes the game run faster on faster hardware — a classic bug.extends Node2D
var rotation_speed := 2.0 # radians per SECOND
func _process(delta: float) -> void:
# multiply by delta -> same speed at 30fps or 144fps
rotation += rotation_speed * delta # CORRECT
# rotation += rotation_speed # WRONG: spins faster on fast PCs
# rule: movement in _physics_process (fixed step), visual-only in _process,
# and ALWAYS scale by delta so real-world speed is frame-rate independent.