Bringing scenes to life with GDScript: basic syntax, variables, functions and classes, signals and input handling, lifecycle callbacks like _ready and _process, how to attach scripts and get node references, and effective debugging and print/logging.

Welcome to episode 5 of the Learn Godot series. The previous four episodes built scenes, nodes, and architecture — now it's time to bring everything to life. GDScript is Godot's built-in scripting language whose syntax resembles Python and is designed specifically to interact seamlessly with the scene system.
Interestingly, GDScript isn't a general-purpose language "forced" onto Godot — it was born from the engine's own needs, with integrated signal, type, and node reference handling. This episode's roadmap: basic syntax, variables, functions and classes, signals and input handling, lifecycle callbacks, attaching scripts and node references, then debugging and logging.
GDScript uses indentation for code blocks, exactly like Python. Variables are declared with var, constants with const, and functions with func. Types can be written explicitly after a colon, and the @export keyword lets a variable be set directly from the Inspector — one of the features that makes Godot so productive:
extends CharacterBody2D
@export var kecepatan: float = 200.0
var arah: Vector2 = Vector2.ZERO
func _physics_process(_delta: float) -> void:
velocity = arah * kecepatan
move_and_slide()Notice extends CharacterBody2D on the first line — the script "attaches" to a specific node type and inherits all of its functions. New classes can also be defined with class_name, for example class_name Peluru extends Area2D, so they can be used as types in other scripts.
To connect a script to a node, click the node in the Scene panel, then in the Inspector click Attach Script (the paper icon). Godot automatically creates a .gd file with extends matching the node type. Once attached, you need to access other nodes in the scene — that's done via node paths:
$Sprite2D — access a direct child node by name.$Panel/Kotak — access a node deeper in the path.get_node("Node/Peluru") — the explicit version of $.get_parent() — access the parent node.The recommended pattern for frequently used nodes: grab the reference once in _ready and store it in a variable, instead of calling $ over and over.
Every node has a lifecycle that scripts can fill in. The three most used callbacks:
_ready() — called once when the node and all its children are ready to enter the scene tree. Ideal for initialization._process(delta) — called every frame. delta is the time between frames in seconds. Good for UI logic and animation._physics_process(delta) — called at a fixed interval (usually 60 times per second). Good for movement and physics.The rule of thumb: movement and physics go in _physics_process, everything else in _process. Multiply movement by delta so speed stays consistent across different frame rates:
func _process(delta: float) -> void:
position += Vector2(100.0, 0.0) * deltaEpisode 2 introduced signals; now we use them. Connect a node's signal to a script method via the Node tab in the Inspector — select the signal, click Connect, and Godot creates the handler method. They can also be connected via code:
signal nyawa_berkurang(nyawa: int)
func _ready() -> void:
$Area2D.body_entered.connect(_on_area_body_entered)
func _on_area_body_entered(body: Node2D) -> void:
nyawa -= 1
nyawa_berkurang.emit(nyawa)For input, Godot uses an action system defined in Project Settings (for example, the ui_left and ui_right actions). In script, check with Input.is_action_pressed("ui_left"). Episode 11 will cover the input map thoroughly; for now, just master the basic pattern:
func _physics_process(_delta: float) -> void:
arah = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = arah * kecepatan
move_and_slide()Input.get_vector directly produces a directional Vector2 from four actions — the same pattern used in almost every 2D game.
Godot provides a debugger connected to the editor: breakpoints, variable inspection, and stack traces can all be done from the bottom panel. In addition, manual logging is still important:
print("Nilai skor: ", skor)
push_warning("Ini peringatan")
push_error("Ini error")
print_rich("[color=green]Teks berwarna[/color]")print shows a message in the Output; push_warning and push_error are also flagged in the Debugger panel. To view variable values visually, use print_debug, which only activates when running the project from the editor — avoiding log spam in production builds.
Warning
Don't be afraid of errors — but read the messages. The most common beginner mistakes in Godot: calling $Node when the node doesn't exist in the scene yet, or calling a method before the node is ready. Get in the habit of checking node paths and the call order in _ready.
With GDScript, the scenes you assembled in episode 4 can now move, react, and communicate. You've mastered basic syntax and @export, attached scripts and grabbed node references, filled in lifecycle callbacks correctly, connected signals and read input, and used print and logging for debugging.
The key takeaways:
var for variables, func for functions.@export lets variables be set directly from the Inspector._ready for initialization, _physics_process for movement, _process for everything else.$Path, get_node, and get_parent are the ways to access nodes from script..connect, and input is read through the action map.In the next episode, episode 6, all these foundations will be used to build 2D game fundamentals: sprites, tilesets and tilemaps, parallax backgrounds, physics bodies with collision, animation with AnimationPlayer and Tween, plus Camera2D and UI overlays. You'll make your first truly playable 2D game!