LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 719 min read

Functions & Lifecycle Callbacks

Learn GDScript function syntax, return types, default parameters, and the built-in lifecycle callbacks Godot calls automatically.

Introduction

GDScript functions work much like functions in any language — but a special category of them, "lifecycle callbacks," are called automatically by the Godot engine itself at specific moments, without you ever calling them directly. Understanding those is central to writing any Godot script.

What You Will Learn
  • How to declare functions, with return types and default parameter values.
  • What _ready() is and when the engine calls it.
  • The difference between _process() and _physics_process().
  • How @onready variables tie into the node lifecycle.

Declaring Functions

func greet(name: String) -> String:
return "Hello, " + name + "!"
print(greet("Isha"))
Output

Click Run to see what this code prints.

Return Types and Default Parameters

A -> Type annotation on a function declares its return type (optional, but recommended for clarity). Parameters can have default values, making them optional at the call site.

func take_damage(amount: int, is_critical: bool = false) -> int:
var total := amount
if is_critical:
total *= 2
return total
print(take_damage(10)) # 10
print(take_damage(10, true)) # 20
Output

Click Run to see what this code prints.

The _ready() Callback

Use case: _ready() is called automatically by Godot exactly once, right after a node (and all of its children) has fully entered the scene tree — the standard place to run setup code that depends on child nodes already existing.

extends Node
func _ready() -> void:
print("This node and its children are ready.")
Output

Click Run to see what this code prints.

_process() vs _physics_process()

Use case: both callbacks run automatically on every frame, but for different purposes. _process(delta) runs once per rendered frame, ideal for visual updates. _physics_process(delta) runs at a fixed rate tied to the physics engine, the correct place for movement and collision-related logic.

extends Node2D
func _process(delta: float) -> void:
rotation += delta # rotate smoothly, tied to render framerate
func _physics_process(delta: float) -> void:
position.x += 100 * delta # movement, tied to the physics tick
delta Is Time, Not a Fixed Step

delta is the elapsed time (in seconds) since the last call — multiplying a speed by delta, as shown above, keeps movement consistent regardless of frame rate, instead of moving faster on faster machines.

@onready Variables

Use case: @onready var defers a variable's initialization until right before _ready() runs, guaranteeing that any child-node lookups (like $Sprite2D from lesson 5) succeed, since the whole subtree is guaranteed to exist by that point.

extends CharacterBody2D
@onready var sprite: Sprite2D = $Sprite2D # safe: resolved right before _ready()
func _ready() -> void:
print("Sprite found:", sprite != null)
Output

Click Run to see what this code prints.

Common Mistakes

Avoid These Mistakes
  • Putting movement logic in _process() instead of _physics_process(), causing frame-rate-dependent or jittery motion.
  • Looking up a child node with $NodeName directly at the top of a script (outside @onready) — the node tree may not be built yet at that point.
  • Forgetting to multiply by delta in per-frame logic, causing behavior to run at a different speed on different machines.

Best Practices

  • Use _physics_process() for movement, collisions, and anything physics-related; use _process() for purely visual per-frame updates.
  • Always multiply per-frame changes by delta so behavior stays consistent across different frame rates.
  • Group @onready node references at the top of a script for a clear, scannable list of what a script depends on.

Frequently Asked Questions

No — implement only the ones a given script actually needs. Many scripts use just one, or neither.

Every node in the tree that has a script implementing _ready() gets its own call, in a defined order (children before their parent).

It signals "the engine calls this automatically" — a strong, consistent naming convention across all of Godot's built-in callback functions.

Key Takeaways

  • Functions use func, with optional -> ReturnType annotations and default parameter values.
  • _ready() runs once, automatically, after a node and its children have fully entered the scene tree.
  • _process() runs per rendered frame; _physics_process() runs on a fixed physics tick — use the right one for the job.
  • @onready defers a variable's setup until right before _ready(), making child-node lookups safe.

Summary

Functions and lifecycle callbacks are how your code actually runs inside Godot's engine loop. Next, you'll cover signals — GDScript's built-in event system for letting nodes talk to each other.

Next Lesson →

Signals: GDScript's Event System