Intro Into Gdscript
π€ Introduction to GDScript (Godot 4.x)
I really enjoy GDScriptβit sits in a sweet spot as a practical mix of Python, Lua, and Go:
- Like Python: Clean, indentation-based syntax, readable structure, and fast iteration speed.
- Like Lua: Lightweight, deeply embedded into the engine core, and designed specifically for game scripting without external runtime bloat.
- Like Go: Pragmatic, clean structural design, first-class functions/signals, and built-in asynchronous primitives (
await) without callback hell.
While GDScript started as a purely dynamically typed language, modern GDScript 2.0 (Godot 4.x) introduces strong static typing, annotations, first-class signals, and substantial performance improvements.
β‘ The Low-Level Dev's Mental Model
Coming from a C/C++ or Odin background, think of GDScript as your high-level orchestration layer:
- Use GDScript for: Game flow, UI logic, state machines, quests, and hooking components together.
- Drop down to GDExtension (C++, Odin, Rust) for: Heavy number crunching, custom physics solvers, procedural mesh generation, or custom socket protocols.
π 1. Variables & The Static Type System
In Godot 4, static typing is strongly recommended. Typed GDScript executes up to 2β3x faster because the VM can skip dynamic method lookups and type validation at runtime.
extends CharacterBody3D
class_name PlayerController
# 1. Dynamic typing (flexible, but slower)
var unassigned_var
var dynamic_score = 100
# 2. Explicit static typing (Recommended)
var max_health: int = 100
var move_speed: float = 7.5
var player_name: String = "Ryan"
var is_active: bool = true
# 3. Type inference (:= infers type from right-hand assignment)
var velocity_multiplier := 1.25 # Inferred as float
var target_direction := Vector3.ZERO # Inferred as Vector3
# 4. Constants
const GRAVITY: float = 9.81
const MAX_INVENTORY_SLOTS: int = 24
π·οΈ 2. Essential Modern Annotations
Annotations in Godot 4 replace the older export keywords and provide clear metadata to the editor and compiler.
# Exposes variable to the Godot Inspector
@export var walk_speed: float = 5.0
@export_range(0.0, 100.0, 0.5) var armor_rating: float = 25.0
# Grouping variables cleanly in the Inspector
@export_group("Combat Stats")
@export var attack_power: int = 15
@export var critical_chance: float = 0.15
# Safe node reference: assigns once the node and its children enter the scene tree
@onready var camera: Camera3D = $CameraPivot/SpringArm3D/Camera3D
@onready var animation_tree: AnimationTree = %AnimationTree # Scene Unique Node syntax
# Runs script directly inside the Godot Editor viewport
# @tool
# Networking RPC configuration
# @rpc("any_peer", "call_local", "reliable")
%) Instead of brittle long node paths like $UI/Margin/HBox/VBox/Label, mark your node as Access as Unique Name in the scene dock. In code, access it directly with %Label. If you rearrange the UI hierarchy later, the code reference doesn't break!
π‘ 3. Signals (First-Class Observer Pattern)
Signals are Godotβs built-in observer pattern. They decouple your systems: nodes emit signals when things happen, and listener nodes subscribe to them.
# Defining typed signals
signal health_changed(current_hp: int, max_hp: int)
signal died
var current_hp: int = 100
var max_hp: int = 100
func take_damage(amount: int) -> void:
current_hp = clampi(current_hp - amount, 0, max_hp)
# Emit the signal to all listeners
health_changed.emit(current_hp, max_hp)
if current_hp <= 0:
died.emit()
func _ready() -> void:
# Connecting via Callables (Godot 4 style)
health_changed.connect(_on_health_changed)
func _on_health_changed(hp: int, max: int) -> void:
print("Health updated: %d / %d" % [hp, max])
π 4. The Node Lifecycle (_ready, _process, _physics_process)
Understanding execution order is critical for preventing null pointer exceptions and simulation jitter.
extends Node3D
# Called when node enters tree, before children are ready
func _enter_tree() -> void:
pass
# Called once when this node AND all children have entered the tree
func _ready() -> void:
# Safe to access children nodes here
pass
# Called every render frame (variable delta). Use for visual updates / UI
func _process(delta: float) -> void:
# Rotate visual mesh
rotate_y(1.0 * delta)
# Called every fixed physics tick (default 60Hz). Use for movement and physics
func _physics_process(delta: float) -> void:
# Physics math, CharacterBody movement, Raycasts
pass
# Called when node leaves tree (cleanup)
func _exit_tree() -> void:
pass
Never handle Character movement or physics collisions in _process(). Frame rate fluctuations will cause inconsistent speed and wall-clipping. Always use _physics_process(delta).
β³ 5. Asynchronous Logic (await)
Godot 4 scrapped yield in favor of await, mimicking modern async/await patterns from Go and JS:
func attack_routine() -> void:
print("Attack windup...")
# Wait for a 0.5 second one-shot timer
await get_tree().create_timer(0.5).timeout
print("Strike executed!")
# Wait for an animation signal to finish
await animation_player.animation_finished
print("Ready for next command.")
π¦ 6. Custom Resources: Data-Oriented Design
In Godot, Resource is the equivalent of Unity's ScriptableObject or Unreal's UDataAsset. Itβs great for item databases, enemy stats, and audio profiles.
# ItemData.gd
class_name ItemData
extends Resource
@export var item_id: String = "item_001"
@export var display_name: String = "Iron Blade"
@export var damage: int = 25
@export var icon: Texture2D
You can save this as an .tres asset in the filesystem and drag-and-drop it into any node's @export var item: ItemData slot!
π Comparison Summary: GDScript vs C++ vs C#
| Feature | GDScript (Godot 4) | C# | C++ / GDExtension |
|---|---|---|---|
| Compilation | Bytecode / VM (Fast restart) | JIT / AOT (.NET 8+) | Native machine code (DLL) |
| Engine Integration | Native 1st class (built-in editor) | External IDE via LSP | C ABI via godot-cpp |
| Execution Speed | Fast for game logic (typed) | High (~2-5x faster) | Maximum (Bare metal) |
| Memory Management | Reference counted (RefCounted) | Garbage Collected (.NET GC) | Manual / Smart Pointers |
| Best Used For | UI, Gameplay flow, Prototyping | Core logic, Large codebases | Shaders, DSP, Physics, Sockets |
π Related Notes
- Godot Basics & Architecture β Engine core structure, SceneTree, and low-level servers.
- Environment β WorldEnvironment, ACES Tonemapping, SSAO, and Unreal vs Godot visuals.
- Intro Into Navigation In Godot β NavigationServer3D, Regions, and agent pathfinding.
- Navigation Regions β Setting up 2D and 3D walkable meshes.
- Object Avoidance β Dynamic obstacle avoidance (RVO2) for navigation agents.
- GDExtension Notes β Integrating low-level Odin/C++ code into Godot.
- Game Development Hub β Main index for engine architectures.