← Tech Guides
// 01

Quick Reference

Essential GDScript syntax, node types, and keyboard shortcuts at a glance. Keep this section bookmarked.

GDScript Syntax Cheat Sheet

var name = "Godot"
Variable declaration (inferred type)
var hp: int = 100
Typed variable declaration
const MAX = 999
Constant (compile-time)
@export var speed: float = 5.0
Exposed to Inspector panel
@onready var sprite = $Sprite2D
Assign after _ready()
func move(dir: Vector2) -> void:
Typed function signature
signal health_changed(new_hp)
Custom signal definition
emit_signal("health_changed", hp)
Emit signal with arguments
if / elif / else:
Conditional branching
for item in array:
Iterate over collection
match value:
Pattern matching (like switch)
await get_tree().create_timer(1.0).timeout
Async wait for 1 second

Most-Used Node Types

Category Node Purpose
2D Sprite2D Displays a 2D texture
2D CharacterBody2D Player/NPC with move_and_slide()
2D RigidBody2D Physics-driven body (gravity, forces)
2D TileMapLayer Tile-based level design
2D Camera2D Viewport camera with smoothing/limits
3D MeshInstance3D Renders a 3D mesh
3D CharacterBody3D 3D player/NPC movement
3D Camera3D 3D viewport camera
3D DirectionalLight3D Sun/moon global lighting
UI Control Base class for all UI nodes
UI Label Text display
UI Button Clickable button with signals
UI VBoxContainer Vertical auto-layout
Core Node Base class (script-only logic)
Core Timer Countdown with timeout signal
Core Area2D / Area3D Detect overlap / trigger zones

Editor Keyboard Shortcuts

Context Shortcut Action
Editor F5 Run project
Editor F6 Run current scene
Editor F8 Stop running scene
Editor Ctrl+S Save scene
Editor Ctrl+Shift+S Save all scenes
Viewport Q Select mode
Viewport W Move mode
Viewport E Rotate mode
Viewport S Scale mode
Script Ctrl+Space Autocomplete
Script Ctrl+D Duplicate line
Script Ctrl+Shift+K Delete line
Script Ctrl+K Toggle comment
Script F1 Open documentation for symbol
Script Ctrl+Shift+F Search across all files

Common Built-in Functions

get_node("Path") / $Path
Access child node by path
get_tree()
Access the SceneTree singleton
queue_free()
Safely destroy node at end of frame
print() / push_error()
Console output / error logging
lerp(a, b, t)
Linear interpolation between values
clamp(val, min, max)
Restrict value to range
randf_range(0.0, 1.0)
Random float in range
Input.is_action_pressed("ui_right")
Check if input action is held
preload("res://scene.tscn")
Load resource at compile time
load("res://file.tres")
Load resource at runtime
instantiate()
Create instance from PackedScene
add_child(node)
Add node to the scene tree
// 02

Installation & Setup

Downloading Godot, understanding the editor layout, project structure, and choosing between the standard and .NET editions.

Download Options

Method Command / URL Notes
Official Site godotengine.org/download Recommended. Choose Standard or .NET
Steam Search "Godot Engine" on Steam Free. Auto-updates. Standard edition only
Homebrew (macOS) brew install --cask godot Standard edition via Homebrew Cask
Flatpak (Linux) flatpak install flathub org.godotengine.Godot Sandboxed Linux package
Scoop (Windows) scoop install godot Windows package manager
Build from Source github.com/godotengine/godot Requires SCons, Python, C++ compiler

Standard vs .NET Edition

Feature Standard .NET
Scripting GDScript only GDScript + C#
Download Size ~40 MB ~80 MB (includes .NET runtime)
External IDE Optional (built-in editor is good) VS Code / Rider recommended for C#
Dependencies None (single executable) Requires .NET 8 SDK
Web Export Full support Limited (no C# in web builds)
Best For Most projects, beginners Teams with C# experience, NuGet ecosystem

Start with Standard

Unless you have a strong reason to use C#, the standard edition with GDScript is simpler, exports to all platforms (including web), and has better documentation coverage. You can always switch later.

Editor Layout Overview

Godot's editor has five main areas. Each can be resized or rearranged.

Scene Dock (top-left)
Node tree hierarchy for the current scene. Add, delete, reparent nodes here.
FileSystem Dock (bottom-left)
Project directory browser. Drag files into the scene or Inspector.
Viewport (center)
2D/3D canvas for placing and arranging nodes visually. Tabs for Script, AssetLib.
Inspector (right)
Properties panel for the selected node. Edit transforms, textures, physics, scripts.
Output / Debugger (bottom)
Console output, errors, profiler, and debugger breakpoints.

Project File Types

Extension Type Description
project.godot Config Project settings (resolution, input maps, autoloads). The "root" file.
.tscn Scene Text-based scene file (nodes, properties, connections). Version-control friendly.
.gd Script GDScript source file. Attached to nodes.
.tres Resource Text-based resource (themes, materials, custom resources).
.res Resource Binary resource (faster loading, not human-readable).
.gdshader Shader Godot's shader language file (GLSL-like).
.import Meta Auto-generated import settings for assets. Commit these to VCS.
.gdextension Plugin GDExtension configuration for native C/C++/Rust bindings.

Recommended Project Structure

res://
  project.godot
  scenes/
    player/
      player.tscn
      player.gd
    enemies/
      slime.tscn
      slime.gd
    levels/
      level_01.tscn
    ui/
      hud.tscn
      main_menu.tscn
  scripts/
    autoload/
      game_manager.gd
      audio_manager.gd
    resources/
      weapon_data.gd
  assets/
    sprites/
    audio/
      sfx/
      music/
    fonts/
    shaders/
  addons/           # third-party plugins
  export_presets.cfg # export templates config

res:// is your root

All paths in Godot start with res:// (project root) or user:// (user data directory for saves, settings). Never use absolute OS paths in your game code.

Godot 4 vs 3 Key Migration Changes

Godot 3 Godot 4 Reason
KinematicBody2D CharacterBody2D Clearer naming
move_and_slide(vel) velocity = vel; move_and_slide() Velocity is now a built-in property
export var @export var Annotations replace keywords
onready var @onready var Same annotation pattern
yield() await Matches modern async patterns
connect("signal", obj, "method") signal.connect(callable) Type-safe callable syntax
TileMap TileMapLayer Per-layer nodes replace multi-layer TileMap
Spatial Node3D Consistent 2D/3D naming
Tween (node) create_tween() (method) Tweens are now lightweight, no scene tree needed
GLES2 / GLES3 Compatibility / Forward+ / Mobile Three rendering backends for different targets

Migration is not automatic

Godot 4 includes a project converter, but it handles only some renames. Scripts with yield, old signal syntax, and custom C++ modules need manual porting. For large projects, consider starting fresh in Godot 4 rather than converting.

// 03

Scene System & Nodes

Godot's fundamental architecture: everything is a node, every game object is a scene, and scenes compose into trees.

"Everything is a Node" Philosophy

In Godot, the smallest building block is a Node. Nodes have a name, belong to a tree, and can have child nodes. There is no separate entity/component system -- you compose behavior by combining specialized node types in a parent-child hierarchy.

A CharacterBody2D is a node. Its Sprite2D child is a node. The CollisionShape2D attached to it is a node. The animation player controlling its sprites is a node. This tree of nodes, saved together, is called a scene.

Scenes are reusable blueprints

A player scene, an enemy scene, a bullet scene, a UI panel scene -- each is an independent .tscn file containing a node tree. Scenes can be instanced (spawned) into other scenes at any time. This is how you build modular, reusable game objects.

Node Tree Concepts

# The scene tree is a hierarchy of nodes:
Main (Node2D)
  ├── Player (CharacterBody2D)
  │     ├── Sprite2D
  │     ├── CollisionShape2D
  │     └── AnimationPlayer
  ├── Enemies (Node2D)
  │     ├── Slime (CharacterBody2D)
  │     └── Bat (CharacterBody2D)
  └── UI (CanvasLayer)
        └── HUD (Control)

# Accessing nodes from script:
$Sprite2D               # shorthand for get_node("Sprite2D")
$"../Enemies/Slime"     # relative path (up one, then down)
%HUD                    # unique name shorthand (node marked as unique)
get_node("/root/Main")  # absolute path from root

The $ operator is syntactic sugar for get_node(). Use relative paths to keep scenes portable. The % operator accesses nodes marked as "unique name" in the editor (right-click node > Access as Unique Name).

Scene Instancing with PackedScene

Scenes become objects in the game world through instancing. You load a .tscn file as a PackedScene resource, then call instantiate() to create a live copy.

# Preload at compile time (preferred for frequently used scenes)
const BulletScene = preload("res://scenes/bullet/bullet.tscn")

func shoot():
    var bullet = BulletScene.instantiate()
    bullet.position = global_position
    bullet.rotation = global_rotation
    # Add to the scene tree so it becomes active
    get_tree().current_scene.add_child(bullet)

# Load at runtime (for dynamic or conditional loading)
func spawn_enemy(type: String):
    var scene = load("res://scenes/enemies/" + type + ".tscn")
    var enemy = scene.instantiate()
    $Enemies.add_child(enemy)

preload vs load

preload() loads the resource during script compilation -- instant access at runtime but increases initial load time. load() loads at the moment it is called -- useful for dynamic paths but may cause frame hitches. For large assets, use ResourceLoader.load_threaded_request() for background loading.

Node Lifecycle Callbacks

Every node has virtual methods that Godot calls at specific moments. Override them to add behavior.

Callback When Called Typical Use
_init() Object constructed (before tree) Set default values, never access tree here
_enter_tree() Node added to scene tree Called every time, including re-parenting
_ready() Node + all children have entered tree One-time setup, access siblings, connect signals
_process(delta) Every visual frame (~60/s) Non-physics logic: animations, UI updates, timers
_physics_process(delta) Every physics tick (default 60/s) Movement, raycasts, physics queries
_input(event) Any unhandled input event Global input (pause, screenshot, debug)
_unhandled_input(event) Input not consumed by UI or _input Game input (movement, shooting, interaction)
_exit_tree() Node removed from scene tree Cleanup: disconnect signals, free resources
extends CharacterBody2D

@onready var sprite = $Sprite2D

func _ready():
    # Called once when node and children are in the tree
    sprite.modulate = Color.GREEN

func _physics_process(delta: float):
    # Called every physics frame (fixed timestep)
    var direction = Input.get_vector("left", "right", "up", "down")
    velocity = direction * 200.0
    move_and_slide()

func _exit_tree():
    print("Player removed from scene")

Scene Management

# Change to a new scene (replaces current scene tree)
get_tree().change_scene_to_file("res://scenes/levels/level_02.tscn")

# Reload the current scene
get_tree().reload_current_scene()

# Change scene with a packed scene reference
var next_level = preload("res://scenes/levels/level_02.tscn")
get_tree().change_scene_to_packed(next_level)

# Groups -- tag nodes for batch operations
func _ready():
    add_to_group("enemies")

# Call a method on all nodes in a group
get_tree().call_group("enemies", "take_damage", 10)

# Get all nodes in a group
var all_enemies = get_tree().get_nodes_in_group("enemies")
for enemy in all_enemies:
    enemy.queue_free()

Autoload / Singleton Pattern

Autoloads are scripts (or scenes) that persist across scene changes. They appear at the root of the scene tree and are accessible globally. Perfect for game state, audio managers, save/load systems.

# 1. Create the script: scripts/autoload/game_manager.gd
extends Node

var score: int = 0
var current_level: int = 1
var player_lives: int = 3

signal score_changed(new_score)

func add_score(amount: int):
    score += amount
    score_changed.emit(score)

func reset():
    score = 0
    current_level = 1
    player_lives = 3

# 2. Register in Project Settings:
#    Project > Project Settings > Autoload
#    Path: res://scripts/autoload/game_manager.gd
#    Name: GameManager

# 3. Access from ANY script in the project:
func _on_coin_collected():
    GameManager.add_score(100)

Don't overuse autoloads

Autoloads are global state. Use them for truly cross-scene data (score, settings, audio). For scene-specific logic, prefer passing data through scene instancing or signals. Too many autoloads create hidden coupling between systems.

// 04

GDScript Fundamentals

Godot's built-in scripting language: Python-like syntax, tight editor integration, and designed from the ground up for game development.

Variable Declarations

# Untyped (dynamic)
var health = 100
var name = "Player"
var items = ["sword", "shield"]

# Typed (static -- enables better autocompletion & error checking)
var health: int = 100
var speed: float = 5.0
var direction: Vector2 = Vector2.ZERO
var enemies: Array[Enemy] = []

# Type inference (type locked at assignment)
var damage := 25          # inferred as int
var pos := Vector2(10, 20) # inferred as Vector2

# Constants (compile-time, must be literal or preload)
const MAX_HEALTH = 100
const GRAVITY = 980.0
const PLAYER_SCENE = preload("res://scenes/player.tscn")

# @export -- visible in the Inspector panel
@export var move_speed: float = 300.0
@export var jump_force: float = -400.0
@export var max_health: int = 5

# @onready -- assigned when _ready() is called
@onready var sprite: Sprite2D = $Sprite2D
@onready var anim: AnimationPlayer = $AnimationPlayer
@onready var collision: CollisionShape2D = $CollisionShape2D

Export Annotations Reference

The @export family controls how variables appear in the Inspector.

Annotation Example Inspector Widget
@export @export var hp: int = 10 Default widget for the type
@export_range @export_range(0, 100, 5) var vol: int Slider with min/max/step
@export_enum @export_enum("Fire", "Ice", "Shock") var el: int Dropdown selector
@export_flags @export_flags("Run", "Jump", "Fly") var ab: int Checkbox flags (bitmask)
@export_file @export_file("*.json") var config: String File picker dialog
@export_dir @export_dir var save_path: String Directory picker dialog
@export_multiline @export_multiline var desc: String Multi-line text area
@export_color_no_alpha @export_color_no_alpha var c: Color Color picker (no alpha)
@export_group @export_group("Movement") Collapsible group header in Inspector
@export_subgroup @export_subgroup("Advanced") Nested group within a group
@export_category @export_category("AI Settings") Top-level category divider
# Organized export example:
@export_category("Combat")

@export_group("Offense")
@export var damage: int = 10
@export_range(0.1, 5.0, 0.1) var attack_speed: float = 1.0
@export_enum("Melee", "Ranged", "Magic") var attack_type: int = 0

@export_group("Defense")
@export var armor: int = 5
@export_range(0.0, 1.0, 0.05) var dodge_chance: float = 0.1

@export_category("Visual")
@export var sprite_texture: Texture2D
@export_color_no_alpha var tint: Color = Color.WHITE

Functions

# Basic function
func greet():
    print("Hello from Godot!")

# With parameters and return type
func calculate_damage(base: int, multiplier: float) -> int:
    return int(base * multiplier)

# Default parameter values
func spawn_enemy(type: String = "slime", level: int = 1):
    var enemy = load("res://scenes/enemies/" + type + ".tscn").instantiate()
    enemy.level = level
    add_child(enemy)

# Static function (can be called without an instance)
static func clamp_health(value: int) -> int:
    return clampi(value, 0, 100)

# Lambda / Callable (anonymous functions)
var double = func(x): return x * 2
print(double.call(5))  # prints 10

# Passing callables
var enemies_by_hp = enemies.duplicate()
enemies_by_hp.sort_custom(func(a, b): return a.hp < b.hp)

# Callable references to methods
var callback: Callable = self.on_damage_taken
callback.call(10)

Signals

Signals are Godot's observer pattern. A node emits a signal; any number of other nodes connect to it and react. This keeps nodes decoupled.

# Define a custom signal
signal health_changed(new_health: int)
signal died
signal item_picked_up(item_name: String, quantity: int)

# Emit signals
func take_damage(amount: int):
    health -= amount
    health_changed.emit(health)
    if health <= 0:
        died.emit()

# Connect in _ready() -- modern Godot 4 syntax
func _ready():
    # Connect to a method on this node
    health_changed.connect(_on_health_changed)

    # Connect to a method on another node
    $Button.pressed.connect(_on_button_pressed)

    # One-shot connection (auto-disconnects after first call)
    died.connect(_on_player_died, CONNECT_ONE_SHOT)

    # Connect with bound arguments
    $Button.pressed.connect(_on_button_pressed.bind("start"))

# Callback functions
func _on_health_changed(new_health: int):
    $HUD/HealthBar.value = new_health

func _on_button_pressed():
    get_tree().change_scene_to_file("res://scenes/game.tscn")

func _on_player_died():
    $DeathScreen.show()

# Await a signal (pause execution until signal fires)
func show_dialog(text: String):
    $DialogBox.text = text
    $DialogBox.show()
    await $DialogBox.confirmed  # waits here until the signal
    $DialogBox.hide()

# Await a timer
await get_tree().create_timer(2.0).timeout
print("2 seconds have passed")

Signal naming convention

Signal names use snake_case and describe what happened (past tense or state). Callback methods follow the pattern _on_NodeName_signal_name. The editor auto-generates this pattern when connecting signals via the GUI.

Enums & Match Statements

# Named enum
enum State { IDLE, RUNNING, JUMPING, FALLING, ATTACKING }
var current_state: State = State.IDLE

# Anonymous enum (constants at class scope)
enum { NORTH, SOUTH, EAST, WEST }

# Match statement (pattern matching)
func handle_state(state: State):
    match state:
        State.IDLE:
            play_animation("idle")
        State.RUNNING:
            play_animation("run")
        State.JUMPING, State.FALLING:
            play_animation("air")
        State.ATTACKING:
            play_animation("attack")
        _:
            push_warning("Unknown state: %s" % state)

# Match with binding
match typeof(value):
    TYPE_INT:
        print("It's an integer: ", value)
    TYPE_STRING:
        print("It's a string: ", value)
    _:
        print("Unknown type")

# Match with arrays
match command:
    ["move", var direction]:
        move(direction)
    ["attack", var target, var damage]:
        attack(target, int(damage))
    ["heal"]:
        heal()
    _:
        print("Unknown command")

Classes & Inner Classes

# Every .gd file IS a class (extends a node or Resource)
# player.gd
extends CharacterBody2D
class_name Player  # registers a global class name

var health: int = 100

func take_damage(amount: int):
    health -= amount

# Inner class (defined inside another script)
class Inventory:
    var items: Array[String] = []
    var capacity: int = 20

    func add_item(item: String) -> bool:
        if items.size() >= capacity:
            return false
        items.append(item)
        return true

    func has_item(item: String) -> bool:
        return item in items

# Usage
var inventory = Inventory.new()
inventory.add_item("health_potion")

# Custom Resource class (for data objects)
# weapon_data.gd
extends Resource
class_name WeaponData

@export var name: String = ""
@export var damage: int = 10
@export var attack_speed: float = 1.0
@export var sprite: Texture2D

# Create instances in the editor or via code:
var sword = WeaponData.new()
sword.name = "Iron Sword"
sword.damage = 15

Properties (Getters & Setters)

# Property with setter (Godot 4 syntax)
var health: int = 100:
    set(value):
        health = clampi(value, 0, max_health)
        health_changed.emit(health)
        if health <= 0:
            died.emit()
    get:
        return health

# Property with only a setter
var score: int = 0:
    set(value):
        score = value
        $HUD/ScoreLabel.text = "Score: %d" % score

# Property with custom backing variable
var _speed: float = 0.0
var speed: float:
    set(value):
        _speed = clampf(value, 0.0, max_speed)
    get:
        return _speed

Useful Built-in Functions

get_node() / $ / %
Access nodes by path, shorthand, or unique name
is_instance_of(obj, class)
Runtime type checking
str() / int() / float()
Type conversion functions
lerp() / lerpf() / lerp_angle()
Interpolation (value, float, angle)
move_toward(from, to, delta)
Move value toward target by fixed step
remap(val, istart, istop, ostart, ostop)
Map value from one range to another
randi_range(min, max)
Random integer in inclusive range
"text %s %d" % [a, b]
String formatting (printf-style)
Array.filter() / .map() / .reduce()
Functional array methods (use lambdas)
Dictionary.get(key, default)
Safe dictionary access with fallback
JSON.parse_string(json_text)
Parse JSON string to Variant
FileAccess.open("path", mode)
Read/write files (saves, configs)
# Practical examples of common patterns

# Smooth camera follow
camera.position = camera.position.lerp(target.position, 5.0 * delta)

# Random item from array
var loot = ["gold", "potion", "sword", "shield"]
var drop = loot.pick_random()

# Filtering arrays
var alive_enemies = enemies.filter(func(e): return e.health > 0)
var total_hp = enemies.reduce(func(sum, e): return sum + e.health, 0)
var names = enemies.map(func(e): return e.name)

# Save game data as JSON
func save_game():
    var data = {"score": score, "level": level, "items": items}
    var file = FileAccess.open("user://save.json", FileAccess.WRITE)
    file.store_string(JSON.stringify(data, "\t"))

func load_game():
    if not FileAccess.file_exists("user://save.json"):
        return
    var file = FileAccess.open("user://save.json", FileAccess.READ)
    var data = JSON.parse_string(file.get_as_text())
    score = data.get("score", 0)
    level = data.get("level", 1)
// 05

Physics & Collision

Physics bodies, collision layers, raycasting, and the move_and_slide() workflow. Everything you need to make objects interact in physical space.

Physics Body Types

Godot provides four physics body types, each suited to different gameplay roles. Choosing the right one is the first decision for every physical game object.

Node Use Case Movement
StaticBody2D/3D Walls, floors, immovable objects None (or animated via AnimatableBody)
RigidBody2D/3D Physics-simulated objects (crates, balls) Driven by physics engine
CharacterBody2D/3D Player characters, NPCs Manual via move_and_slide()
Area2D/3D Triggers, hitboxes, pickups Detects overlap only

CharacterBody2D

The workhorse node for player and NPC movement. You control velocity directly and call move_and_slide() to handle collisions and sliding automatically.

# Standard platformer movement
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta):
    if not is_on_floor():
        velocity.y += gravity * delta
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY
    var direction = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * SPEED
    move_and_slide()

Godot 4 API change

move_and_slide() takes NO parameters in Godot 4. Set the velocity property directly on the node before calling it. In Godot 3, velocity was passed as an argument.

Motion Modes

MOTION_MODE_GROUNDED (default) is designed for platformers with floor/wall/ceiling detection. MOTION_MODE_FLOATING disables floor logic and is ideal for top-down games.

# Top-down movement (MOTION_MODE_FLOATING)
func _physics_process(delta):
    var dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = dir * SPEED
    move_and_slide()

Collision Response

After calling move_and_slide(), iterate over slide collisions to react to what was hit.

move_and_slide()
for i in get_slide_collision_count():
    var col = get_slide_collision(i)
    print("Hit: ", col.get_collider().name)
    print("Normal: ", col.get_normal())

move_and_collide()

Unlike move_and_slide(), this stops on the first collision and returns a KinematicCollision2D object. Good for bouncing projectiles or custom physics.

var collision = move_and_collide(velocity * delta)
if collision:
    velocity = velocity.bounce(collision.get_normal())

RigidBody2D

For fully physics-driven objects. Use _integrate_forces(state) instead of _physics_process() to apply forces safely within the physics step.

extends RigidBody2D
func _integrate_forces(state):
    # Continuous force (like thrust)
    apply_central_force(Vector2(100, 0))
    # One-time impulse (like a jump)
    apply_central_impulse(Vector2(0, -300))

Area2D

Detects overlap without physical collision. Connect to its signals for trigger zones, hitboxes, and pickups.

extends Area2D
func _ready():
    body_entered.connect(_on_body_entered)
    area_entered.connect(_on_area_entered)

func _on_body_entered(body):
    if body.is_in_group("player"):
        body.take_damage(10)

Collision Layers & Masks

Every physics object has a collision layer (what I am) and a collision mask (what I detect). Two objects only interact if one's mask includes the other's layer.

Layer Purpose
1 World / Terrain
2 Player
3 Enemies
4 Projectiles
5 Pickups / Triggers
# Player: IS on layer 2, DETECTS layers 1 (world) and 5 (pickups)
$Player.set_collision_layer_value(2, true)
$Player.set_collision_mask_value(1, true)
$Player.set_collision_mask_value(5, true)

Raycasting

Two approaches: a persistent RayCast2D node that polls every physics frame, or a one-shot query using PhysicsRayQueryParameters2D.

Method 1 RayCast2D node (persistent, polls every physics frame):

@onready var ray = $RayCast2D
func _physics_process(delta):
    if ray.is_colliding():
        var hit = ray.get_collider()
        var pos = ray.get_collision_point()

Method 2 PhysicsRayQueryParameters2D (one-shot):

func _physics_process(delta):
    var space = get_world_2d().direct_space_state
    var query = PhysicsRayQueryParameters2D.create(
        global_position, global_position + Vector2(0, 200))
    query.collision_mask = 0b0001
    query.exclude = [self.get_rid()]
    var result = space.intersect_ray(query)
    if result:
        print("Hit: ", result.collider.name)

3D Mouse Picking Cast a ray from camera through click position:

func _input(event):
    if event is InputEventMouseButton and event.pressed:
        var cam = $Camera3D
        var from = cam.project_ray_origin(event.position)
        var to = from + cam.project_ray_normal(event.position) * 1000
        var space = get_world_3d().direct_space_state
        var query = PhysicsRayQueryParameters3D.create(from, to)
        var result = space.intersect_ray(query)

Collision Shapes

Every physics body requires at least one CollisionShape2D/3D child. Choose the simplest shape that fits your object for best performance.

Shape (2D) 3D Equivalent Best For
RectangleShape2D BoxShape3D Boxes, platforms, walls
CircleShape2D SphereShape3D Balls, coins, radial triggers
CapsuleShape2D CapsuleShape3D Characters, humanoids
ConvexPolygonShape2D ConvexPolygonShape3D Irregular convex objects
ConcavePolygonShape2D ConcavePolygonShape3D Static terrain, complex meshes (static only)
WorldBoundaryShape2D WorldBoundaryShape3D Infinite planes (kill zones, boundaries)
SeparationRayShape2D SeparationRayShape3D Character feet, slope snapping

Performance tip

Circle/Sphere and Rectangle/Box are the fastest shapes. Use ConcavePolygon only for static bodies (terrain). For moving objects with complex silhouettes, approximate with 2-3 simple shapes instead of one concave polygon.

// 06

2D Development

Sprites, animations, tilemaps, cameras, parallax, particles, and all the nodes that make 2D games come alive.

Sprite2D

The basic node for displaying 2D textures. Supports sprite sheets via hframes/vframes, flipping, and region-based atlases.

@onready var sprite = $Sprite2D
func _ready():
    sprite.texture = preload("res://player.png")
    sprite.hframes = 4   # columns in sprite sheet
    sprite.vframes = 2   # rows in sprite sheet
    sprite.frame = 0     # current frame index

AnimatedSprite2D

Uses a SpriteFrames resource to manage named animations. Call play() to switch between them based on game state.

extends AnimatedSprite2D
func _physics_process(delta):
    if velocity.x != 0:
        play("walk")
        flip_h = velocity.x < 0
    elif not is_on_floor():
        play("jump")
    else:
        play("idle")

You can also create SpriteFrames dynamically from code:

var frames = SpriteFrames.new()
frames.add_animation("run")
frames.set_animation_speed("run", 8)
frames.add_frame("run", preload("res://run_1.png"))

TileMapLayer (Godot 4.3+)

TileMap is deprecated

The monolithic TileMap node is deprecated in Godot 4.3+. Use one TileMapLayer node per layer instead. This gives you more control over z-ordering, physics, and per-layer properties.

# Recommended node structure:
# Node2D (WorldMap)
# ├── TileMapLayer (ground)
# ├── TileMapLayer (decorations)
# └── TileMapLayer (foreground)

Setting cells, erasing, terrain autotiling, and coordinate conversion:

@onready var ground: TileMapLayer = $WorldMap/Ground

# Set a cell: position, source_id, atlas_coords
ground.set_cell(Vector2i(5, 3), 0, Vector2i(1, 0))

# Erase a cell
ground.erase_cell(Vector2i(5, 3))

# Terrain autotiling (auto-selects correct tile variant)
var cells = [Vector2i(0,0), Vector2i(1,0), Vector2i(2,0)]
ground.set_cells_terrain_connect(cells, 0, 0)

# Coordinate conversion (world <-> map)
var map_pos = ground.local_to_map(to_local(global_position))
var world_pos = ground.map_to_local(map_pos)

Camera2D

Attach as a child of the player node for automatic following. Configure smoothing and limits to keep the camera within your level bounds.

func _ready():
    position_smoothing_enabled = true
    position_smoothing_speed = 5.0
    limit_left = 0
    limit_right = 2048
    limit_top = 0
    limit_bottom = 1024

ParallaxBackground

Create depth illusion with multiple layers that scroll at different speeds. Each ParallaxLayer has a motion_scale controlling how fast it moves relative to the camera.

# Node structure:
# ParallaxBackground
# ├── ParallaxLayer (sky)       motion_scale=(0.1, 0.1)
# ├── ParallaxLayer (mountains) motion_scale=(0.3, 0.2)
# └── ParallaxLayer (trees)     motion_scale=(0.6, 0.4)
$SkyLayer.motion_scale = Vector2(0.1, 0.0)
$SkyLayer.motion_mirroring = Vector2(512, 0)  # seamless tiling

CanvasLayer

Renders on a separate layer that does not move with the camera. Essential for HUD elements, menus, and overlays that should stay fixed on screen.

extends CanvasLayer
func _ready():
    layer = 10  # Always on top of other canvas layers

Line2D

Draws connected line segments. Useful for trails, ropes, laser beams, and debug path visualization.

func add_trail_point(pos: Vector2):
    $Line2D.add_point(pos)
    if $Line2D.get_point_count() > 30:
        $Line2D.remove_point(0)  # limit trail length

GPUParticles2D

GPU-accelerated particle systems for visual effects like explosions, fire, rain, and dust. Key properties control the behavior of the particle system.

Property Description
amount Number of particles alive at once
lifetime How long each particle lives (seconds)
one_shot Emit once then stop (explosions)
process_material ParticleProcessMaterial controlling velocity, gravity, color, scale curves
texture Sprite for each particle

CPU fallback

Use CPUParticles2D for compatibility with older hardware or the Compatibility renderer. It has the same API but runs on the CPU instead of the GPU.

// 07

3D Development

Meshes, materials, lighting, cameras, environments, and shaders. Building 3D worlds in Godot's Forward+ or Mobile renderers.

Node3D

The base spatial node. Every 3D object inherits from Node3D, which provides position, rotation, and scale in 3D space.

position = Vector3(0, 1, -5)
rotation_degrees = Vector3(0, 45, 0)
rotate_y(delta * 1.0)                          # continuous rotation
look_at(target.global_position, Vector3.UP)    # face a target

MeshInstance3D

Renders a 3D mesh. Create primitive meshes in code or import .glb/.gltf models from Blender and other tools.

# Create a mesh from code
var mesh_instance = MeshInstance3D.new()
var box = BoxMesh.new()
box.size = Vector3(1, 1, 1)
mesh_instance.mesh = box

var mat = StandardMaterial3D.new()
mat.albedo_color = Color.FIREBRICK
mesh_instance.set_surface_override_material(0, mat)
add_child(mesh_instance)

Importing .glb/.gltf models:

# Import a 3D model exported from Blender
var scene = preload("res://models/character.glb")
var instance = scene.instantiate()
add_child(instance)

StandardMaterial3D

Godot's PBR material. Controls how surfaces look under lighting with physically-based properties.

Property Range Description
albedo_color Color Base color / texture
metallic 0.0 - 1.0 0 = dielectric, 1 = metal
roughness 0.0 - 1.0 0 = mirror, 1 = matte
emission Color Self-illumination (glow)
normal_enabled bool Enable normal mapping for surface detail
transparency enum ALPHA, ALPHA_SCISSOR, ALPHA_HASH, etc.
# Full PBR material setup
var mat = StandardMaterial3D.new()
mat.albedo_color = Color(0.8, 0.2, 0.1)
mat.metallic = 0.3
mat.roughness = 0.7
mat.normal_enabled = true
mat.normal_texture = preload("res://textures/brick_normal.png")
mat.emission_enabled = true
mat.emission = Color(1, 0.5, 0)
mat.emission_energy_multiplier = 2.0
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
mat.albedo_color.a = 0.8

Lighting

Three light types cover all common scenarios. Each can cast shadows and be configured for energy, color, and attenuation.

Light Type Use Case
DirectionalLight3D Parallel rays Sun, moon (infinite distance)
OmniLight3D Point (all directions) Bulbs, torches, campfires
SpotLight3D Cone beam Flashlights, ceiling fixtures, headlights
# Directional light (sun)
var sun = DirectionalLight3D.new()
sun.light_energy = 1.2
sun.shadow_enabled = true
sun.rotation_degrees = Vector3(-45, 30, 0)
add_child(sun)

# Omni light (torch)
var torch = OmniLight3D.new()
torch.light_energy = 3.0
torch.light_color = Color(1.0, 0.7, 0.3)
torch.omni_range = 8.0
torch.shadow_enabled = true
add_child(torch)

# Spot light (flashlight)
var flash = SpotLight3D.new()
flash.light_energy = 5.0
flash.spot_range = 20.0
flash.spot_angle = 25.0
flash.shadow_enabled = true
add_child(flash)

WorldEnvironment

Controls the sky, ambient lighting, fog, volumetric effects, and post-processing (glow, SSAO, SSR, tone mapping). Add one to your main scene.

var env = Environment.new()
var sky = Sky.new()
var sky_mat = ProceduralSkyMaterial.new()
sky_mat.sky_top_color = Color(0.1, 0.2, 0.5)
sky.sky_material = sky_mat
env.sky = sky
env.background_mode = Environment.BG_SKY
env.fog_enabled = true
env.glow_enabled = true
$WorldEnvironment.environment = env

CSGShape3D

Constructive Solid Geometry nodes for rapid prototyping. Combine shapes with union, subtraction, and intersection operations.

Prototyping only

CSG shapes have no LOD, no lightmap UV generation, and are more expensive to render than static meshes. Use them for blocking out levels, then replace with proper meshes before shipping.

Camera3D

The viewport camera. Configure FOV, near/far clipping planes, and projection type. The example below implements first-person mouse look.

func _input(event):
    if event is InputEventMouseMotion:
        rotate_y(-event.relative.x * 0.002)
        $Head.rotate_x(-event.relative.y * 0.002)
        $Head.rotation.x = clamp($Head.rotation.x, -PI/2, PI/2)

Capture the mouse

Call Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) in _ready() to lock the cursor for FPS controls. Toggle back with MOUSE_MODE_VISIBLE on pause.

GridMap

The 3D equivalent of TileMap. Place 3D mesh tiles in a grid for level building. Uses a MeshLibrary resource.

@onready var grid: GridMap = $GridMap

# Place a cell: position, item_index, orientation
grid.set_cell_item(Vector3i(0, 0, 0), 0)
grid.set_cell_item(Vector3i(1, 0, 0), 0, 4)  # rotated

# Convert world position to grid coordinates
var grid_pos = grid.local_to_map(to_local(pos))

ShaderMaterial

Write custom spatial shaders for effects the standard material cannot achieve. Godot's shader language is GLSL-like with built-in engine variables.

# Water wave shader (attach as .gdshader file or inline)
shader_type spatial;
uniform float wave_speed = 1.0;
uniform float wave_height = 0.3;

void vertex() {
    VERTEX.y += sin(VERTEX.x * 2.0 + TIME * wave_speed) * wave_height;
}

void fragment() {
    ALBEDO = vec3(0.1, 0.4, 0.8);
    ROUGHNESS = 0.1;
    METALLIC = 0.5;
}

Set shader parameters from GDScript at runtime:

$WaterMesh.material_override.set_shader_parameter("wave_speed", 2.0)
// 08

UI & Controls

Control is the base node for all UI elements. Godot's anchor and margin system lets you build resolution-independent interfaces with container-based layouts, theme overrides, and focus navigation.

Control Node Hierarchy

Control is the root of the UI tree. Every UI widget inherits from it. Position is driven by anchors (0.0 - 1.0 relative to parent) and offsets (pixel distance from anchor). Anchors pin the node to a region of its parent; offsets fine-tune placement from there.

Container Types

Containers automatically arrange their children. Nest them to build complex layouts without manual positioning.

Container Layout
VBoxContainer Vertical stack
HBoxContainer Horizontal stack
GridContainer Grid (set columns)
MarginContainer Padding around content
ScrollContainer Scrollable area
PanelContainer Background panel
CenterContainer Centers child
HSplitContainer Draggable horizontal split
TabContainer Tabbed panels
FlowContainer Wrapping flow layout

Common Controls

Key widgets available out of the box:

Button, Label, LineEdit
Basic interaction, text display, single-line input
TextEdit, RichTextLabel
Multi-line text editing and BBCode-rich display
OptionButton, CheckBox, CheckButton
Dropdown select, toggle boxes, toggle buttons
SpinBox, HSlider, VSlider
Numeric input, horizontal and vertical range sliders
ProgressBar
Health bars, loading indicators, XP gauges
TextureRect, ColorRect, NinePatchRect
Image display, solid color fill, stretchable bordered sprites
# Simple HUD — attach to a CanvasLayer node
extends CanvasLayer

@onready var health_bar: ProgressBar = $VBox/HealthBar
@onready var score_label: Label = $VBox/ScoreLabel

func update_health(value: int):
    health_bar.value = value

func update_score(value: int):
    score_label.text = "Score: %d" % value

Theme System

Override theme properties per-node in code, or create a Theme resource in the editor and assign it to a root Control so all children inherit the look.

# Override theme properties per-node
var btn = Button.new()
btn.add_theme_color_override("font_color", Color.CYAN)
btn.add_theme_font_size_override("font_size", 24)
btn.add_theme_stylebox_override("normal", stylebox)

# Create StyleBoxFlat
var style = StyleBoxFlat.new()
style.bg_color = Color(0.1, 0.1, 0.2)
style.corner_radius_top_left = 8
style.corner_radius_top_right = 8
style.border_width_bottom = 2
style.border_color = Color.CYAN

Focus & Input

Controls can receive keyboard/gamepad focus. Set focus_neighbor_* properties to define navigation order. Connect the gui_input signal for custom input handling. The focus_mode property controls whether a control can receive focus at all: FOCUS_NONE, FOCUS_CLICK, or FOCUS_ALL.

Responsive Layout

Use anchor presets and size flags to build UIs that adapt to any screen resolution.

# Full-screen anchor
control.anchor_left = 0
control.anchor_top = 0
control.anchor_right = 1
control.anchor_bottom = 1

# Size flags for containers
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL

Anchor presets

In the editor, select a Control and click the anchor preset button (green crosshair icon) to quickly set common layouts like "Full Rect", "Center", "Top Wide", or "Bottom Right".

// 09

Animation & Shaders

AnimationPlayer for timeline-based animation, AnimationTree for state machines and blend spaces, Tweens for procedural motion, and the Godot shading language for GPU-driven visual effects.

AnimationPlayer

The core animation node. Create animations in the editor's timeline, then control playback from code. Animates any property on any node in the scene.

@onready var anim = $AnimationPlayer
anim.play("walk")
anim.play_backwards("walk")
anim.queue("idle")
anim.stop()
anim.is_playing()

# Signals
anim.animation_finished.connect(_on_animation_finished)
Track Type Animates
Property Any node property (position, color, scale)
Method Call Calls a method at a keyframe
Bezier Smooth curves for float values
Audio Plays audio at keyframes
Animation Triggers other animations
Expression Evaluates expressions

AnimationTree

Layer on top of AnimationPlayer for state machines, blend trees, and blend spaces. Essential for character animation with smooth transitions between states.

@onready var anim_tree: AnimationTree = $AnimationTree

func _physics_process(delta):
    var state_machine = anim_tree["parameters/playback"]

    if is_on_floor():
        if velocity.length() > 10:
            state_machine.travel("walk")
        else:
            state_machine.travel("idle")
    else:
        state_machine.travel("jump")

    # Blend spaces
    anim_tree["parameters/BlendSpace2D/blend_position"] = velocity.normalized()

Tween

Godot 4's procedural animation system. Create tweens from any node, chain operations, run in parallel, loop, and await completion. Perfect for UI transitions, camera effects, and juice.

# Create a tween
var tween = create_tween()
tween.tween_property($Sprite2D, "modulate", Color.RED, 0.3)
tween.tween_property($Sprite2D, "modulate", Color.WHITE, 0.3)

# Chaining
var tween = create_tween()
tween.tween_property(self, "position:x", 400, 1.0)
tween.tween_callback(func(): print("arrived!"))
tween.tween_interval(0.5)
tween.tween_property(self, "position:x", 0, 1.0)

# Parallel tweens
var tween = create_tween().set_parallel(true)
tween.tween_property(self, "position", Vector2(400, 300), 1.0)
tween.tween_property(self, "modulate:a", 0.0, 1.0)

# Easing & transitions
tween.tween_property(self, "position", target, 0.5) \
    .set_trans(Tween.TRANS_BOUNCE) \
    .set_ease(Tween.EASE_OUT)

# Loop
var tween = create_tween().set_loops()  # infinite

# await completion
await tween.finished
Transition Type Curve
TRANS_LINEAR Constant speed
TRANS_SINE Smooth sine wave
TRANS_QUAD Quadratic acceleration
TRANS_CUBIC Cubic acceleration
TRANS_QUART Quartic acceleration
TRANS_QUINT Quintic acceleration
TRANS_EXPO Exponential acceleration
TRANS_CIRC Circular curve
TRANS_ELASTIC Springy overshoot
TRANS_BOUNCE Bouncing ball
TRANS_BACK Slight overshoot
TRANS_SPRING Damped spring oscillation

Shaders

Godot has its own GLSL-like shading language. Shaders run on the GPU and are specified with a shader_type declaration. Write them inline or in .gdshader files.

Type Where Use
shader_type canvas_item 2D nodes, Control Sprite effects, UI shaders
shader_type spatial 3D meshes Surface, lighting
shader_type particles GPU particles Particle behavior
shader_type sky WorldEnvironment Procedural skies
shader_type fog FogVolume Volumetric fog

CRT scanline effect (canvas_item shader):

shader_type canvas_item;
uniform float scan_line_count = 200.0;
uniform float scan_line_strength = 0.1;
uniform float aberration = 2.0;

void fragment() {
    vec2 uv = UV;
    float r = texture(TEXTURE, uv + vec2(aberration / 1000.0, 0)).r;
    float g = texture(TEXTURE, uv).g;
    float b = texture(TEXTURE, uv - vec2(aberration / 1000.0, 0)).b;
    vec3 color = vec3(r, g, b);
    float scan = sin(uv.y * scan_line_count * 3.14159) * scan_line_strength;
    COLOR = vec4(color - scan, texture(TEXTURE, uv).a);
}

Built-in Shader Variables

Key variables available in Godot shaders, depending on shader type.

Variable Shader Type Description
VERTEX All Vertex position (read/write in vertex(), read in fragment())
FRAGCOORD All Fragment screen-space coordinates
UV All Texture coordinates
COLOR canvas_item Output color (read/write)
NORMAL spatial Surface normal vector
TIME All Elapsed time in seconds
SCREEN_UV canvas_item Screen-space UV for screen-reading effects
ALBEDO spatial Base surface color
ROUGHNESS spatial PBR roughness (0 = mirror, 1 = matte)
METALLIC spatial PBR metallic (0 = dielectric, 1 = metal)
EMISSION spatial Self-illumination color

Visual Shader Editor

Godot includes a node-graph Visual Shader editor as an alternative to writing shader code. Create a VisualShader resource and open it in the editor to build effects by connecting nodes.

Setting Uniforms from GDScript

Pass values from your game logic into shader uniforms at runtime.

material.set_shader_parameter("scan_line_count", 300.0)
material.set_shader_parameter("aberration", 3.0)

Common Shader Snippets

Reusable patterns for frequent visual effects.

// Grayscale
void fragment() {
    vec4 tex = texture(TEXTURE, UV);
    float gray = dot(tex.rgb, vec3(0.299, 0.587, 0.114));
    COLOR = vec4(vec3(gray), tex.a);
}

// Outline (canvas_item)
uniform float outline_width = 1.0;
uniform vec4 outline_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
void fragment() {
    vec4 tex = texture(TEXTURE, UV);
    float a = texture(TEXTURE, UV + vec2(outline_width * TEXTURE_PIXEL_SIZE.x, 0)).a;
    a += texture(TEXTURE, UV - vec2(outline_width * TEXTURE_PIXEL_SIZE.x, 0)).a;
    a += texture(TEXTURE, UV + vec2(0, outline_width * TEXTURE_PIXEL_SIZE.y)).a;
    a += texture(TEXTURE, UV - vec2(0, outline_width * TEXTURE_PIXEL_SIZE.y)).a;
    COLOR = mix(outline_color * ceil(clamp(a, 0.0, 1.0)), tex, tex.a);
}

// Dissolve
uniform float threshold : hint_range(0.0, 1.0) = 0.5;
uniform sampler2D noise_tex;
void fragment() {
    vec4 tex = texture(TEXTURE, UV);
    float noise = texture(noise_tex, UV).r;
    tex.a *= step(threshold, noise);
    COLOR = tex;
}
// 10

Audio & Resources

Audio playback nodes, the bus system with effects, supported stream formats, and the resource loading pipeline including preload, async loading, and custom Resource classes.

Audio Players

Three audio player nodes cover all use cases, from global music to positional 3D sound.

Node Positional Use Case
AudioStreamPlayer No Music, UI sounds
AudioStreamPlayer2D 2D Footsteps, impacts
AudioStreamPlayer3D 3D Environmental sounds
@onready var music = $AudioStreamPlayer
@onready var sfx = $AudioStreamPlayer2D

func _ready():
    music.stream = preload("res://audio/bg_music.ogg")
    music.bus = "Music"
    music.play()

func play_sfx(clip: AudioStream):
    sfx.stream = clip
    sfx.play()

Audio Bus System

Route audio through buses for grouped volume control and effects. The Master bus always exists; add custom buses (Music, SFX, Ambient) in the Audio tab at the bottom of the editor.

# Volume control (in dB)
AudioServer.set_bus_volume_db(
    AudioServer.get_bus_index("Music"), -10.0)

# Mute
AudioServer.set_bus_mute(
    AudioServer.get_bus_index("SFX"), true)

# Fade with tween
var tween = create_tween()
var bus_idx = AudioServer.get_bus_index("Music")
tween.tween_method(
    func(vol): AudioServer.set_bus_volume_db(bus_idx, vol),
    0.0, -80.0, 2.0)  # fade out over 2 seconds

Audio Effects

Add effects to any bus in the Audio tab: Reverb, Compressor, EQ, Limiter, Chorus, Delay, Distortion, Phaser, and more. Effects are processed in order from top to bottom on each bus.

Stream Types

Godot supports multiple audio formats, each suited to different use cases.

Stream Type Format Best For
AudioStreamOggVorbis .ogg Music (compressed, loops well)
AudioStreamMP3 .mp3 Music (widely available format)
AudioStreamWAV .wav Short SFX (no decode latency)
AudioStreamRandomizer wrapper Random pitch/volume variation
AudioStreamPolyphonic wrapper Multiple simultaneous sounds

Format guidance

Use .ogg for music (compressed, loops cleanly with loop points). Use .wav for short sound effects (zero decode latency, instant playback). Avoid .mp3 for looping music as it has gap issues at loop boundaries.

Resource Loading

Godot provides compile-time, runtime, and async loading strategies. Choose based on when you know the asset path and how large the asset is.

# preload — compile-time, cached (use for known assets)
var tex = preload("res://icon.png")

# load — runtime, cached after first load
var tex = load("res://icon.png")

# Async loading (ResourceLoader)
ResourceLoader.load_threaded_request("res://levels/big_level.tscn")

# Poll progress
func _process(delta):
    var progress = []
    var status = ResourceLoader.load_threaded_get_status(
        "res://levels/big_level.tscn", progress)
    if status == ResourceLoader.THREAD_LOAD_LOADED:
        var scene = ResourceLoader.load_threaded_get(
            "res://levels/big_level.tscn")

preload vs load

preload() embeds the resource at compile time -- use it when the path is a constant string and you want zero runtime cost. load() resolves at runtime and is needed when the path is dynamic (e.g., built from a variable).

Custom Resources

Create your own data containers by extending Resource. Export properties to edit them in the Inspector. Save as .tres (text, version-control friendly) or .res (binary, smaller and faster to load).

class_name WeaponData
extends Resource

@export var name: String
@export var damage: int
@export var attack_speed: float
@export var icon: Texture2D
@export var rarity: int = 0

.tres vs .res

.tres files are human-readable text -- ideal for version control and debugging. .res files are binary -- smaller and faster to load but opaque. Use resource.duplicate() when you need a unique copy of a shared resource at runtime (e.g., giving each enemy its own health bar data).

// 11

AI Integration & MCP

MCP servers for AI-assisted Godot development, coding assistant plugins, in-game LLM integration patterns, NPC dialogue systems with local models, and reinforcement learning workflows.

Godot MCP Servers

MCP (Model Context Protocol) servers let AI coding assistants interact with the Godot editor and project files. Several community-built options target different workflows.

Server Key Feature Best For
bradypp/godot-mcp Most complete, read-only mode, auto-detect Production projects
LeeSinLiang/godot-mcp Remote debugger via WebSocket Debugging workflows
Coding-Solo/godot-mcp Editor control (launch/run/stop) Getting started
ee0pdt/Godot-MCP Scene creation, script editing Simple prototyping
nguyenchiencong/godot-mcp-cli CLI-first, saves tokens Token-constrained setups
viktorfaubl/Godot-MCP-server-for-local-LLM C# in-game MCP server Runtime AI in games

Composite capabilities across all servers:

Category Tools
Editor Control launch_editor, run_project, stop_project
Scene Editing create_scene, add_node, save_scene, load_sprite
Script create_script, read_script, validate_gdscript
Debug get_debug_output, capture_screenshot, remote_debugger
Assets generate_2d_asset, project_visualizer
Safety read_only_mode, get_uid, update_project_uids

Claude Desktop configuration example:

{
  "mcpServers": {
    "godot": {
      "command": "node",
      "args": ["/path/to/godot-mcp/dist/index.js"]
    }
  }
}

Read-Only Mode

The bradypp/godot-mcp server has a read-only mode for safe project analysis -- great for code review workflows where you want AI to inspect but never modify your project.

AI Coding Assistants

In-editor AI plugins that bring code completion, chat, and context-aware assistance directly into the Godot editor.

Plugin Backend Feature
godot-copilot OpenAI / GitHub Copilot Caret-position completion with GDScript 4 injection
AI Assistant Hub Local LLMs Code editor read/write, saveable prompt buttons
Fuku Ollama, OpenAI, Claude, Gemini Multi-provider chat panel
Itqan AI Gemini API In-editor assistant
AI Context Generator Any LLM Export project structure as JSON for AI context
Godot AI Suite Claude, Gemini, ChatGPT Masterprompt + Agent Mode

Cursor IDE integration -- place a .cursorrules file at the project root:

# godot.cursorrules (place at project root)
- Target Godot 4.4, GDScript 2.0
- Use @export, @onready, @tool annotations
- CharacterBody2D/3D (NOT KinematicBody)
- move_and_slide() takes no parameters
- Use randf_range()/randi_range() (NOT rand_range())
- Use deg_to_rad() (NOT deg2rad())

Common AI GDScript errors -- things AI models frequently get wrong:

Wrong (Godot 3) Correct (Godot 4)
KinematicBody2D CharacterBody2D
move_and_slide(velocity) velocity = ...; move_and_slide()
deg2rad() deg_to_rad()
rand_range(a, b) randf_range(a, b)
connect("sig", self, "fn") signal.connect(callable)
yield(timer, "timeout") await timer.timeout
export var @export var
onready var @onready var
instance() instantiate()
extents (Shape) size

AI & GDScript 4.x

AI models have sparse GDScript 4.x training data. Always specify "Godot 4.4, GDScript 2.0" in prompts. The most common failure is generating Godot 3.x syntax -- especially move_and_slide(velocity), yield, and old connect() syntax.

In-Game LLM Integration

Two main patterns for calling LLMs from GDScript: cloud APIs via HTTPRequest and local models via Ollama.

Pattern 1: Cloud API via HTTPRequest

extends Node

var http: HTTPRequest

func _ready():
    http = HTTPRequest.new()
    add_child(http)
    http.request_completed.connect(_on_response)

func ask_ai(prompt: String):
    var headers = [
        "Content-Type: application/json",
        "Authorization: Bearer " + OS.get_environment("API_KEY")
    ]
    var body = JSON.stringify({
        "model": "claude-sonnet-4-20250514",
        "messages": [{"role": "user", "content": prompt}]
    })
    http.request("https://api.anthropic.com/v1/messages",
        headers, HTTPClient.METHOD_POST, body)

func _on_response(result, code, headers, body):
    if code == 200:
        var json = JSON.parse_string(body.get_string_from_utf8())
        var reply = json["content"][0]["text"]

Pattern 2: Local LLM via Ollama (no API key, fully offline):

func ask_local(prompt: String):
    var body = JSON.stringify({
        "model": "llama3.2",
        "prompt": prompt,
        "stream": false
    })
    http.request("http://localhost:11434/api/generate",
        ["Content-Type: application/json"],
        HTTPClient.METHOD_POST, body)

NPC Dialogue with Local LLMs

GDExtension plugins that run LLMs directly inside the game process for zero-latency NPC dialogue.

Plugin Architecture Key Feature
NobodyWho GDExtension + Vulkan Best ease-of-use, Metal support
GDLlama GDExtension + llama.cpp Function calling, structured output
godot-llm GDExtension + llama.cpp Vision (GDLlava), vector store
godot-llm-framework HTTPRequest wrapper Cloud APIs (Anthropic, OpenAI)

NobodyWho example -- add a NobodyWhoModel node (shared) and NobodyWhoChat per NPC:

# Add NobodyWhoModel node (shared, loads GGUF weights)
# Add NobodyWhoChat node per NPC

@onready var chat: NobodyWhoChat = $NobodyWhoChat

func _ready():
    chat.response_updated.connect(_on_token)
    chat.system_prompt = "You are a gruff blacksmith in a medieval village."

func player_speaks(text: String):
    chat.say(text)

func _on_token(token: String):
    $DialogueLabel.text += token  # streams word by word

Share Model Weights

Share NobodyWhoModel nodes across NPCs -- model weights are expensive to load. Use one model node with many chat nodes to keep memory usage manageable.

ML & Reinforcement Learning

Run trained neural networks inside Godot or train agents with reinforcement learning using the Godot RL Agents framework.

Extension Runtime Format
godot_onnx_extension ONNX Runtime .onnx
Godot-ONNX-AI-Models-Loaders ONNX Runtime .onnx
iree.gd IREE (Google) .vmfb (compiled from TFLite/PyTorch)

Godot RL Agents -- full reinforcement learning framework. Python side: pip install godot-rl (StableBaselines3, CleanRL, Ray RLLib). GDScript side: install plugin from AssetLib. Train agents, export to ONNX for in-game inference. Achieves 12k interactions/sec on 4 CPU cores.

# ONNX inference example
@onready var onnx: ONNXLoader = $ONNXLoader

func _ready():
    onnx.load_model("res://models/agent.onnx")

func get_action(observation: Array) -> Array:
    return onnx.predict(observation)

AI Best Practices

Pin Godot Version
Always specify "Godot 4.4, GDScript 2.0" in all AI prompts to avoid Godot 3 syntax generation
Feed Project Context
Use AI Context Generator plugin to export project structure as JSON for AI consumption
Local for Shipping
Use NobodyWho/GDLlama for shipped games -- zero latency, no API costs, works offline
Cloud for Prototyping
Cloud APIs (Anthropic, OpenAI) are easier to start with and provide access to larger models
Structured Output
Use structured output / GBNF grammar for LLM output that feeds game logic directly
Test AI Code
Always test AI-generated GDScript before committing -- Godot 3/4 syntax errors are common
Shader Prompts
Use "Godot shading language" in prompts, not "GLSL" -- Godot shaders differ from standard GLSL
// 12

Export & Ecosystem

Export templates and platform targets, command-line builds, resource packs for DLC/mods, debugging tools, GDExtension native bindings, AssetLib, performance tips, and community resources.

Export Templates

Download export templates via Editor > Manage Export Templates. Required before your first export to any platform.

Platform Requirements Notes
Windows Export template .exe + .pck
macOS Export template .dmg or .app, notarization for distribution
Linux Export template .x86_64
Android JDK, Android SDK, keystores .apk or .aab
iOS macOS + Xcode required .ipa, requires Apple Developer account
Web Export template .html + .js + .wasm, needs HTTPS for SharedArrayBuffer

Command-line export:

godot --headless --export-release "Windows Desktop" game.exe
godot --headless --export-release "Web" index.html
godot --headless --export-debug "Android" game.apk

Resource Packs & Storage

Use .pck resource packs for DLC and mods. Load them at runtime to extend your game without rebuilding.

# Load a resource pack at runtime
ProjectSettings.load_resource_pack("res://dlc/expansion.pck")
var new_scene = load("res://dlc_scenes/bonus_level.tscn")

The user:// path maps to persistent storage per platform:

Platform Path
Windows %APPDATA%\Godot\app_userdata\ProjectName\
macOS ~/Library/Application Support/Godot/app_userdata/ProjectName/
Linux ~/.local/share/godot/app_userdata/ProjectName/
Android Internal storage (app-specific)
iOS Documents directory

Debugging Tools

Built-in debugging primitives, the remote scene inspector, profiler, and custom performance monitors.

print_debug()
Prints with file:line info for easy tracing
push_error() / push_warning()
Colored console output for errors and warnings
assert(condition, "msg")
Debug-only assertion, stripped in release builds
breakpoint
Keyword that triggers debugger pause at that line
Remote Debugger
Inspect running game's scene tree live from the editor
Profiler
Frame time, physics, GPU stats, and custom monitor metrics

Custom performance monitors:

func _ready():
    Performance.add_custom_monitor("game/enemies",
        func(): return get_tree().get_nodes_in_group("enemies").size())

Debug draw (only in debug builds):

func _process(delta):
    if OS.is_debug_build():
        queue_redraw()

func _draw():
    draw_circle(position, 32, Color.RED)
    draw_line(pos_a, pos_b, Color.CYAN, 2.0)

Common errors and fixes:

Error Cause Fix
"Invalid call. Nonexistent function" Wrong node type / Godot 3 API Check class docs
"Node not found" Wrong path or not ready Use @onready or call_deferred
"Null instance" Node freed or never set is_instance_valid() check
"Cannot export" No export template Download via editor

AssetLib & GDExtension

AssetLib -- browse in editor (AssetLib tab) or online. Installs plugins to the addons/ folder. Enable in Project Settings > Plugins.

GDExtension -- native code extensions (C, C++, Rust, Go) that integrate with Godot without recompiling the engine:

# GDExtension defined in .gdextension file
[configuration]
entry_symbol = "my_extension_init"

[libraries]
linux.x86_64 = "res://bin/libmy_extension.so"
windows.x86_64 = "res://bin/my_extension.dll"
macos = "res://bin/libmy_extension.dylib"
GDQuest
Professional tutorials and courses for Godot development
KidsCanCode
Beginner-friendly Godot tutorials and recipes
Godot Shaders
Community shader repository with ready-to-use effects
Godot Forum
Official community Q&A for troubleshooting and discussion
Godot Discord
Real-time help from the Godot community
Awesome Godot
Curated list of plugins, tools, and resources

Performance Tips

Physics Process
Use _physics_process only for physics code, _process for everything else
Object Pooling
Pool objects with hide()/show() instead of queue_free()/instantiate()
Typed Arrays
Use typed arrays (Array[Enemy]) for faster iteration and type safety
GPU Particles
Enable use_gpu_particles for particle effects to offload work to the GPU
Profile First
Profile before optimizing -- use the built-in Profiler to find actual bottlenecks
Visibility Signals
Use visibility_changed signal to pause off-screen logic and save CPU cycles
move_toward()
Prefer move_toward() over manual lerp for consistent frame-rate behavior