Use when writing GDScript or C# code for Godot Engine 4.x projects, creating game scenes, configuring physics/navigation/shaders/UI/audio/animation/networking/XR, debugging Godot projects, or answering any Godot Engine development question. Covers complete GDScript and C# syntax, all 1083+ API classes, node system, 2D/3D rendering, physics, shaders, UI, animation, audio, navigation, input, networking, file I/O, exporting, performance optimization, XR, and engine architecture.
Resources
14Install
npx skillscat add sxdy4591/godot-4-7-skills Install via the SkillsCat registry.
Godot Engine 4.7 Complete Reference
Engine Overview
Godot Engine 4.7 is an open-source, cross-platform game engine supporting 2D and 3D game development. It uses a node-based architecture where every game element is a node organized in a tree hierarchy.
Core Architecture
Node Tree -- Every Godot application is a tree of nodes. The root of the tree is a SceneTree containing a Window (the main viewport). Every node has a name, can have children, and processes per-frame callbacks (_process, _physics_process). Nodes are added to the tree with add_child() and removed with remove_child() or queue_free(). The tree processes nodes top-down for _process and bottom-up for _ready.
Scene System -- A scene is a reusable branch of the node tree saved as a .tscn (text) or .scn (binary) file. Scenes can be instantiated at runtime via PackedScene.instantiate(). Scenes are Godot's primary composition unit: a character scene contains its sprite, collision shape, and scripts; a level scene contains terrain, enemies (instances of other scenes), and triggers. This "scene = prefab + inheritance" model replaces traditional entity-component systems.
Signals -- Signals are Godot's observer pattern implementation. Any node can define custom signals and emit them; other nodes connect to these signals to react. Built-in signals include Node.ready, Node.tree_entered, Timer.timeout, Area2D.body_entered, Button.pressed, and hundreds more. Signals decouple communication between nodes without requiring direct references.
# Defining and using signals
signal health_changed(new_health: int)
func take_damage(amount: int) -> void:
health -= amount
health_changed.emit(health)
# Connecting signals
func _ready() -> void:
health_changed.connect(_on_health_changed)
# Or connect to another node's signal:
$Button.pressed.connect(_on_button_pressed)Resources -- Resources are data containers that can be saved to disk and shared between nodes. Examples: Texture2D, AudioStream, Material, PackedScene, Script, Font, AnimationLibrary. Custom resources extend Resource and export properties for editor-editable data. Resources are reference-counted and deduplicated by the engine when loaded from the same path.
Three Rendering Backends:
| Renderer | API | Target | Features |
|---|---|---|---|
| Forward+ | Vulkan | Desktop / Console | Full 3D feature set: GI, volumetric fog, SDFGI, SSR, SSAO, clustered lighting |
| Mobile | Vulkan | Mobile / Mid-range | Reduced feature set optimized for tile-based GPUs, single-pass rendering |
| Compatibility | OpenGL 3.3 / ES 3.0 / WebGL 2.0 | Low-end / Web | Broadest hardware support, limited advanced effects |
All renderers share the same scene API. Switching renderer only affects visual fidelity and performance, not game logic.
Key Engine Concepts
- GDScript -- Python-like language designed for Godot. Tightly integrated, fast iteration, optional static typing.
- C#/.NET -- Full .NET 8+ support. Write game logic in C# with access to the entire .NET ecosystem.
- GDExtension -- Native C/C++/Rust extensions without recompiling the engine.
- Autoloads (Singletons) -- Nodes/scripts loaded at startup, accessible globally via name. Used for game managers, audio, data persistence.
- Groups -- Lightweight tags for nodes. Call methods on all nodes in a group simultaneously.
- Viewports -- Render targets. The main window is a viewport. Sub-viewports enable render-to-texture, split-screen, minimaps.
- Servers -- Low-level singletons (
RenderingServer,PhysicsServer2D,PhysicsServer3D,AudioServer,NavigationServer2D/3D) for direct high-performance API access bypassing the node layer.
Support File Index
Language Reference
- gdscript-reference.md -- Complete GDScript language reference: syntax, types, annotations, lambdas, await, pattern matching, typed arrays, static typing
- csharp-reference.md -- C#/.NET integration reference: signals in C#, exports, Godot collections, async, source generators, cross-language communication
- gdextension-cpp.md -- GDExtension and C++ bindings: building native modules, godot-cpp, binding methods/properties/signals
Core Concepts
- core-concepts.md -- Design philosophy, node lifecycle, scene composition, signals in depth, resources, groups, Autoloads
- best-practices.md -- Scene organization, OOP principles, project structure, code style, data-driven design, when to use nodes vs resources
- editor-guide.md -- Editor usage, project settings, editor settings, CLI flags, debugging tools, remote scene inspector
- migration-guide.md -- Version upgrade guide (Godot 3 to 4, 4.0 to 4.7): API renames, breaking changes, new features per release
Subsystem Reference
- 2d-development.md -- 2D graphics: Sprite2D, AnimatedSprite2D, particles, TileMapLayer, Camera2D, CanvasLayer, Line2D, 2D lighting
- 3d-development.md -- 3D rendering: MeshInstance3D, materials (StandardMaterial3D, ShaderMaterial), lighting (OmniLight3D, SpotLight3D, DirectionalLight3D), GI (LightmapGI, VoxelGI, SDFGI), environment, sky, fog, post-processing
- physics.md -- Physics bodies (RigidBody, CharacterBody, StaticBody, Area), collision shapes, layers/masks, raycasting, ShapeCast, Jolt physics backend, joints, soft bodies
- animation.md -- AnimationPlayer, AnimationTree (state machines, blend trees, blend spaces), Tween API, AnimationMixer, procedural animation
- shaders-reference.md -- Shader types (spatial, canvas_item, particles, fog, sky, composite), built-in variables, functions, render modes, visual shaders
- ui-system.md -- Control nodes, containers (HBox, VBox, Grid, Margin), anchors, themes, StyleBox, fonts, RichTextLabel with BBCode, drag-and-drop, focus
- audio.md -- AudioStreamPlayer (2D/3D), audio buses, effects (reverb, EQ, compressor, chorus), AudioStreamPolyphonic, interactive music
- navigation.md -- NavigationRegion2D/3D, NavigationAgent2D/3D, NavigationObstacle, avoidance, navigation meshes, A* grid/graph
- input-system.md -- InputEvent hierarchy, InputMap, actions, deadzone, input buffering, mouse/keyboard/gamepad/touch, gesture recognition
- networking.md -- MultiplayerAPI, ENet/WebSocket/WebRTC peers, RPCs, authority, spawning, synchronizers, HTTPRequest, HTTPClient
- rendering.md -- Rendering pipeline, viewports, SubViewport, resolution scaling (FSR2, MetaFSR), compositor effects, post-processing, environment
- xr-development.md -- XR/VR/AR development: OpenXR, XRCamera3D, XRController3D, hand tracking, passthrough, XRToolsKit
Tools and Deployment
- file-io.md -- FileAccess, DirAccess, user:// vs res://, config files, save/load patterns, encryption, ResourceSaver/Loader
- asset-pipeline.md -- Asset import system: images, textures, audio, 3D scenes (glTF, FBX), import presets, reimport, streaming
- export-platform.md -- Export templates, feature tags, platform configs (Windows, Linux, macOS, Android, iOS, Web, consoles), PCK files, code signing
- performance.md -- CPU/GPU profiling, optimization techniques, servers API, MultiMesh, LOD, occlusion culling, multithreading, object pooling
- internationalization.md -- TranslationServer, .po/.csv translations, locales, BiDi text, RTL layouts, pseudolocalization
- plugins.md -- EditorPlugin, @tool scripts, inspector plugins, import plugins, custom docks, EditorScript
- math-reference.md -- Vector2/3/4, Basis, Transform2D/3D, Quaternion, AABB, Plane, Projection, interpolation, random, noise, curves, geometry utilities
- engine-architecture.md -- Engine internals: main loop, servers, Object system, Variant, StringName, RID, ClassDB, memory model, build system (SCons)
API Class Reference (All 1083+ Classes)
- api-reference/node-classes-a-c.md -- Node subclasses A-C (AnimatedSprite2D/3D, AnimationPlayer, Area2D/3D, AudioStreamPlayer, Button, Camera2D/3D, CanvasLayer, CharacterBody2D/3D, CollisionShape2D/3D, ColorRect, Control, CSGBox3D...)
- api-reference/node-classes-d-h.md -- Node subclasses D-H (DirectionalLight3D, FileDialog, GPUParticles2D/3D, GraphEdit, GridContainer, HBoxContainer, HSlider, HTTPRequest...)
- api-reference/node-classes-i-n.md -- Node subclasses I-N (ItemList, Label, Light2D, Line2D, LineEdit, LinkButton, MarginContainer, MeshInstance2D/3D, MultiplayerSpawner, NavigationAgent2D/3D, NinePatchRect, Node2D/3D...)
- api-reference/node-classes-o-s.md -- Node subclasses O-S (OmniLight3D, OptionButton, Panel, ParallaxBackground, Path2D/3D, ProgressBar, RayCast2D/3D, RichTextLabel, RigidBody2D/3D, Skeleton3D, SpotLight3D, Sprite2D/3D, StaticBody2D/3D, SubViewport...)
- api-reference/node-classes-t-z.md -- Node subclasses T-Z (TabContainer, TextEdit, TextureRect, TileMapLayer, Timer, Tree, VBoxContainer, VideoStreamPlayer, VSlider, Window, WorldEnvironment, XRCamera3D, XRController3D...)
- api-reference/resource-classes-a-c.md -- Resource subclasses A-C (Animation, AnimationLibrary, ArrayMesh, AtlasTexture, AudioBusLayout, AudioEffect*, AudioStream*, BitMap, BoxMesh, BoxShape3D, ButtonGroup, CapsuleMesh, CircleShape2D, CompressedTexture2D, ConcavePolygonShape3D, ConvexPolygonShape3D, Curve, CurveTexture...)
- api-reference/resource-classes-d-i.md -- Resource subclasses D-I (DirAccess, Environment, FileAccess, Font, FontFile, FontVariation, GDScript, Gradient, GradientTexture*, HeightMapShape3D, Image, ImageTexture, ImmediateMesh, InputEvent*...)
- api-reference/resource-classes-j-p.md -- Resource subclasses J-P (JSON, LabelSettings, Material, Mesh, MeshLibrary, MultiMesh, NavigationMesh, NavigationPolygon, NoiseTexture2D, OccluderInstance3D, OccluderPolygon2D, OpenXRAction*, PackedScene, ParticleProcessMaterial, PhysicsMaterial, PlaceholderTexture2D, PlaneMesh, PointMesh, PolygonPathFinder, PortableCompressedTexture2D, PrimitiveMesh...)
- api-reference/resource-classes-q-s.md -- Resource subclasses Q-S (QuadMesh, RectangleShape2D, RenderSceneBuffersRD, Resource, RichTextEffect, Shader, ShaderMaterial, Shape2D/3D, Shortcut, Skin, Sky, SphereShape3D, SphereMesh, SpriteFrames, StandardMaterial3D, StyleBox*, SurfaceTool...)
- api-reference/resource-classes-t-z.md -- Resource subclasses T-Z (TextMesh, Texture2D, Texture3D, TextureLayered, Theme, TileMapPattern, TileSet, TileSetSource, TorusMesh, Translation, TubeTrailMesh, ViewportTexture, VisualShader*, World2D, World3D, WorldBoundaryShape2D/3D...)
- api-reference/object-classes-a-e.md -- Object subclasses A-E (AudioServer, CameraServer, ClassDB, DisplayServer, EditorInterface, EditorPlugin, EditorSettings, Engine, EngineDebugger...)
- api-reference/object-classes-f-n.md -- Object subclasses F-N (Geometry2D, Geometry3D, GodotSharp, Input, InputMap, IP, JavaScriptBridge, Marshalls, MultiplayerAPI, NavigationServer2D/3D, OS...)
- api-reference/object-classes-o-p.md -- Object subclasses O-P (Object, Performance, PhysicsDirectBodyState2D/3D, PhysicsDirectSpaceState2D/3D, PhysicsServer2D/3D, ProjectSettings...)
- api-reference/object-classes-q-s.md -- Object subclasses Q-S (RenderingDevice, RenderingServer, ResourceLoader, ResourceSaver, SceneTree, TextServerManager, ThemeDB, Time, TranslationServer...)
- api-reference/object-classes-t-z.md -- Object subclasses T-Z (TreeItem, Tween, UndoRedo, WorkerThreadPool, XMLParser, XRServer...)
- api-reference/variant-types.md -- Variant built-in types: bool, int, float, String, StringName, NodePath, Vector2/3/4, Rect2, Color, Transform2D/3D, Basis, Quaternion, AABB, Plane, Projection, RID, Callable, Signal, Dictionary, Array, Packed*Array
- api-reference/gdscript-globals.md -- @GDScript built-in functions (preload, load, assert, range, print...) and @GlobalScope (math, type checking, utility functions, enums, constants)
GDScript Quick Reference
Variables and Types
# Untyped
var x = 10
# Typed (inferred)
var y := 10 # int
var name := "Godot" # String
var vel := Vector2.ZERO # Vector2
# Typed (explicit)
var health: int = 100
var speed: float = 200.0
var items: Array[String] = []
var scores: Dictionary[String, int] = {}
# Constants
const MAX_SPEED: float = 400.0
const TILE_SIZE := 16
# Enums
enum State { IDLE, RUN, JUMP, FALL }
var current_state: State = State.IDLE
# Exports (editable in Inspector)
@export var move_speed: float = 200.0
@export var max_health: int = 100
@export_range(0, 100, 1) var volume: int = 80
@export var scene: PackedScene
@export var texture: Texture2D
@export_file("*.json") var data_path: String
@export_enum("Sword", "Bow", "Staff") var weapon: int
@export_group("Movement")
@export var acceleration: float = 10.0
@export_subgroup("Jump")
@export var jump_force: float = 300.0
@export_category("Stats")
@export var strength: int = 5Functions
# Basic function
func greet(name: String) -> String:
return "Hello, " + name
# Virtual callbacks (override these)
func _ready() -> void: # Called when node enters tree (children ready)
pass
func _process(delta: float) -> void: # Called every frame
pass
func _physics_process(delta: float) -> void: # Called every physics tick (default 60/s)
pass
func _input(event: InputEvent) -> void: # Called on any unhandled input
pass
func _unhandled_input(event: InputEvent) -> void: # Called after _input if not consumed
pass
# Static functions
static func clamp_health(val: int) -> int:
return clampi(val, 0, 100)
# Lambda
var square := func(x: float) -> float: return x * xControl Flow
# If/elif/else
if health <= 0:
die()
elif health < 30:
show_warning()
else:
hide_warning()
# Match (pattern matching)
match state:
State.IDLE:
play_idle()
State.RUN:
play_run()
State.JUMP, State.FALL:
play_air()
_:
pass
# For loops
for i in range(10): # 0..9
print(i)
for item in inventory: # iterate array
print(item.name)
for key in dict: # iterate dictionary keys
print(key, dict[key])
for child in get_children(): # iterate child nodes
child.queue_free()
# While
while not is_on_floor():
velocity.y += gravity * delta
move_and_slide()Common Annotations
@onready var sprite: Sprite2D = $Sprite2D # Initialized when _ready() runs
@onready var anim: AnimationPlayer = $AnimationPlayer
@onready var label: Label = %StatusLabel # Unique name access (scene-unique)
@export var speed: float = 100.0 # Visible in Inspector
@tool # Script runs in editor
class_name PlayerCharacter # Registers as global class
extends CharacterBody2D # InheritanceSignals
# Define signal
signal died
signal health_changed(new_value: int)
signal item_collected(item_name: String, count: int)
# Emit signal
died.emit()
health_changed.emit(health)
# Connect signal (code)
$Button.pressed.connect(_on_button_pressed)
$Area2D.body_entered.connect(_on_body_entered)
# Connect with bind
$Timer.timeout.connect(_on_timeout.bind("special"))
# Disconnect
$Button.pressed.disconnect(_on_button_pressed)
# One-shot connection
$Timer.timeout.connect(_on_timeout, CONNECT_ONE_SHOT)Await and Coroutines
# Wait for a signal
await get_tree().create_timer(1.5).timeout
# Wait for animation to finish
$AnimationPlayer.play("attack")
await $AnimationPlayer.animation_finished
# Wait for a custom signal
await target.died
# Wait for next frame
await get_tree().process_frame
# Wait for physics frame
await get_tree().physics_frameNode Access
# Child node access
$Sprite2D # Direct child
$"Sprite2D" # Same, quoted form
$Body/CollisionShape2D # Nested path
%UniqueNode # Scene-unique node (set in editor)
# Typed access
var sprite: Sprite2D = $Sprite2D as Sprite2D
# Find nodes
get_node("Path/To/Node")
get_node_or_null("MaybeExists")
find_child("Enemy*") # Wildcard search in subtree
get_parent()
get_children()
get_tree().get_nodes_in_group("enemies")
# Add/remove
add_child(node)
remove_child(node)
node.queue_free() # Safe deferred deletion
# Reparent
node.reparent(new_parent)Type Checking and Casting
if node is CharacterBody2D:
var body := node as CharacterBody2D
body.move_and_slide()
# Class name check
if node.is_class("RigidBody2D"):
pass
# Get class
print(node.get_class()) # "CharacterBody2D"Useful Built-in Functions
# Math
lerp(a, b, t) # Linear interpolation
lerpf(0.0, 100.0, 0.5) # 50.0
clampf(x, 0.0, 1.0) # Clamp float
clampi(x, 0, 100) # Clamp int
move_toward(current, target, delta) # Move value toward target
snapped(value, step) # Snap to step
remap(value, 0, 100, 0.0, 1.0) # Remap range
# Random
randf() # 0.0 to 1.0
randi() % 10 # 0 to 9
randf_range(-1.0, 1.0)
randi_range(1, 6) # Dice roll
var rng = RandomNumberGenerator.new()
rng.randomize()
# String
"Hello %s, you have %d coins" % [name, coins]
" hello ".strip_edges()
"path/to/file.txt".get_file() # "file.txt"
"path/to/file.txt".get_extension() # "txt"
# Print
print("Debug output")
print_rich("[color=red]Error![/color]")
push_warning("Something odd")
push_error("Critical failure")
printerr("Goes to stderr")20 Essential Code Patterns
1. Platformer Character Movement
extends CharacterBody2D
@export var speed: float = 200.0
@export var jump_velocity: float = -350.0
@export var gravity_scale: float = 1.0
@export var coyote_time: float = 0.1
@export var jump_buffer_time: float = 0.1
var _coyote_timer: float = 0.0
var _jump_buffer_timer: float = 0.0
var _was_on_floor: bool = false
func _physics_process(delta: float) -> void:
var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
# Gravity
if not is_on_floor():
velocity.y += gravity * gravity_scale * delta
# Coyote time: allow jumping shortly after leaving a platform
if is_on_floor():
_coyote_timer = coyote_time
elif _was_on_floor:
_coyote_timer = coyote_time
_coyote_timer -= delta
_was_on_floor = is_on_floor()
# Jump buffering: register jump input slightly before landing
if Input.is_action_just_pressed("jump"):
_jump_buffer_timer = jump_buffer_time
_jump_buffer_timer -= delta
# Jump execution
if _jump_buffer_timer > 0.0 and _coyote_timer > 0.0:
velocity.y = jump_velocity
_jump_buffer_timer = 0.0
_coyote_timer = 0.0
# Variable jump height: release early for shorter jump
if Input.is_action_just_released("jump") and velocity.y < 0:
velocity.y *= 0.5
# Horizontal movement
var direction := Input.get_axis("move_left", "move_right")
if direction != 0.0:
velocity.x = direction * speed
else:
velocity.x = move_toward(velocity.x, 0.0, speed)
move_and_slide()2. Top-Down 8-Direction Movement
extends CharacterBody2D
@export var speed: float = 150.0
@export var acceleration: float = 800.0
@export var friction: float = 1000.0
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
func _physics_process(delta: float) -> void:
var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
if input_dir != Vector2.ZERO:
# Normalize to prevent faster diagonal movement
input_dir = input_dir.normalized()
velocity = velocity.move_toward(input_dir * speed, acceleration * delta)
_update_animation(input_dir)
else:
velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
sprite.play("idle")
move_and_slide()
func _update_animation(direction: Vector2) -> void:
if abs(direction.x) > abs(direction.y):
sprite.play("walk_side")
sprite.flip_h = direction.x < 0
elif direction.y < 0:
sprite.play("walk_up")
else:
sprite.play("walk_down")3. Bullet / Projectile Spawning
extends Node2D
@export var bullet_scene: PackedScene
@export var fire_rate: float = 0.15
@export var bullet_speed: float = 600.0
var _can_fire: bool = true
func _process(_delta: float) -> void:
if Input.is_action_pressed("fire") and _can_fire:
fire()
func fire() -> void:
_can_fire = false
var bullet := bullet_scene.instantiate() as Node2D
bullet.global_position = $Muzzle.global_position
bullet.global_rotation = $Muzzle.global_rotation
# Add to scene tree at a level that won't move with the player
get_tree().current_scene.add_child(bullet)
bullet.set_meta("speed", bullet_speed)
# Cooldown
await get_tree().create_timer(fire_rate).timeout
_can_fire = true
# --- Bullet script (separate) ---
# extends Area2D
#
# var speed: float = 600.0
#
# func _ready() -> void:
# if has_meta("speed"):
# speed = get_meta("speed")
# body_entered.connect(_on_body_entered)
# # Auto-destroy after lifetime
# await get_tree().create_timer(3.0).timeout
# queue_free()
#
# func _physics_process(delta: float) -> void:
# position += transform.x * speed * delta
#
# func _on_body_entered(body: Node2D) -> void:
# if body.has_method("take_damage"):
# body.take_damage(1)
# queue_free()4. Scene Transition
# Autoload: SceneManager.gd (add to Project > Autoload)
extends Node
signal scene_changed
var _transition_in_progress: bool = false
func change_scene(scene_path: String, transition: String = "fade") -> void:
if _transition_in_progress:
return
_transition_in_progress = true
# Optional: play transition animation
if transition == "fade":
var tween := create_tween()
# Assumes a ColorRect named "FadeRect" covering the screen in a CanvasLayer
var fade_rect := _get_or_create_fade_rect()
tween.tween_property(fade_rect, "color:a", 1.0, 0.3)
await tween.finished
get_tree().change_scene_to_file(scene_path)
await get_tree().tree_changed
if transition == "fade":
var fade_rect := _get_or_create_fade_rect()
var tween := create_tween()
tween.tween_property(fade_rect, "color:a", 0.0, 0.3)
await tween.finished
_transition_in_progress = false
scene_changed.emit()
func _get_or_create_fade_rect() -> ColorRect:
if has_node("TransitionLayer"):
return $TransitionLayer/FadeRect
var layer := CanvasLayer.new()
layer.name = "TransitionLayer"
layer.layer = 100
add_child(layer)
var rect := ColorRect.new()
rect.name = "FadeRect"
rect.color = Color(0, 0, 0, 0)
rect.set_anchors_preset(Control.PRESET_FULL_RECT)
rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
layer.add_child(rect)
return rect
# Usage from any script:
# SceneManager.change_scene("res://scenes/level_2.tscn")5. Save / Load Game Data
# Autoload: SaveManager.gd
extends Node
const SAVE_PATH := "user://savegame.json"
var game_data: Dictionary = {
"player_position": Vector2.ZERO,
"player_health": 100,
"coins": 0,
"level": "res://scenes/level_1.tscn",
"inventory": [],
"flags": {},
}
func save_game() -> Error:
var file := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file == null:
push_error("Failed to open save file: %s" % error_string(FileAccess.get_open_error()))
return FileAccess.get_open_error()
# Collect save data from all "saveable" nodes
_collect_save_data()
# Convert to JSON with pretty print
var json_string := JSON.stringify(game_data, "\t")
file.store_string(json_string)
file.close()
print("Game saved.")
return OK
func load_game() -> Error:
if not FileAccess.file_exists(SAVE_PATH):
push_warning("No save file found.")
return ERR_FILE_NOT_FOUND
var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
if file == null:
return FileAccess.get_open_error()
var json := JSON.new()
var error := json.parse(file.get_as_text())
file.close()
if error != OK:
push_error("JSON parse error: %s at line %d" % [json.get_error_message(), json.get_error_line()])
return error
game_data = json.data
_apply_save_data()
print("Game loaded.")
return OK
func _collect_save_data() -> void:
for node in get_tree().get_nodes_in_group("saveable"):
if node.has_method("get_save_data"):
game_data[node.scene_file_path + ":" + str(node.get_path())] = node.get_save_data()
func _apply_save_data() -> void:
for node in get_tree().get_nodes_in_group("saveable"):
if node.has_method("load_save_data"):
var key := node.scene_file_path + ":" + str(node.get_path())
if game_data.has(key):
node.load_save_data(game_data[key])
func has_save() -> bool:
return FileAccess.file_exists(SAVE_PATH)
func delete_save() -> void:
if FileAccess.file_exists(SAVE_PATH):
DirAccess.remove_absolute(SAVE_PATH)6. Tween Animations
extends Node2D
func tween_examples() -> void:
# Simple move
var tween := create_tween()
tween.tween_property(self, "position", Vector2(500, 300), 0.5)
# Chained tweens (sequential)
tween = create_tween()
tween.tween_property($Sprite2D, "modulate", Color.RED, 0.2)
tween.tween_property($Sprite2D, "modulate", Color.WHITE, 0.2)
# Parallel tweens
tween = create_tween().set_parallel(true)
tween.tween_property(self, "position:x", 500.0, 0.5)
tween.tween_property(self, "rotation", TAU, 0.5)
# Easing and transitions
tween = create_tween()
tween.tween_property(self, "position", Vector2(400, 200), 0.8) \
.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) # Overshoot
# Looping
tween = create_tween().set_loops() # Infinite loops
tween.tween_property($Sprite2D, "modulate:a", 0.0, 0.5)
tween.tween_property($Sprite2D, "modulate:a", 1.0, 0.5)
# Callback after tween
tween = create_tween()
tween.tween_property(self, "scale", Vector2(1.5, 1.5), 0.1)
tween.tween_property(self, "scale", Vector2.ONE, 0.1)
tween.tween_callback(func(): print("Squash and stretch done!"))
# Tween method (call a method with interpolated value)
tween = create_tween()
tween.tween_method(_set_health_bar, 100.0, 0.0, 2.0)
# Wait before next step
tween = create_tween()
tween.tween_property(self, "modulate:a", 0.0, 0.3)
tween.tween_interval(1.0) # Wait 1 second
tween.tween_callback(queue_free)
func _set_health_bar(value: float) -> void:
$HealthBar.value = value7. Timer Usage
extends Node
# Method 1: Timer node (add as child in editor or code)
@onready var attack_timer: Timer = $AttackTimer
func _ready() -> void:
# Configure Timer node
attack_timer.wait_time = 2.0
attack_timer.one_shot = true
attack_timer.timeout.connect(_on_attack_timer_timeout)
func attack() -> void:
if attack_timer.is_stopped():
perform_attack()
attack_timer.start()
func _on_attack_timer_timeout() -> void:
print("Can attack again")
# Method 2: SceneTreeTimer (one-liner, no node needed)
func spawn_with_delay() -> void:
await get_tree().create_timer(0.5).timeout
spawn_enemy()
# Method 3: Creating Timer in code
func start_repeating_timer() -> void:
var timer := Timer.new()
timer.wait_time = 1.0
timer.autostart = true
timer.timeout.connect(_on_tick)
add_child(timer)
func _on_tick() -> void:
print("Tick at: ", Time.get_ticks_msec())8. Camera Shake / Screen Shake
extends Camera2D
@export var decay_rate: float = 5.0
@export var max_offset: Vector2 = Vector2(100, 75)
@export var max_rotation: float = 0.1
var _trauma: float = 0.0
var _noise := FastNoiseLite.new()
var _noise_y: int = 0
func _ready() -> void:
_noise.seed = randi()
_noise.frequency = 4.0
_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
func add_trauma(amount: float) -> void:
_trauma = minf(_trauma + amount, 1.0)
func shake(intensity: float = 0.5) -> void:
add_trauma(intensity)
func _process(delta: float) -> void:
if _trauma > 0.0:
_trauma = maxf(_trauma - decay_rate * delta, 0.0)
var shake_intensity := _trauma * _trauma # Quadratic falloff
_noise_y += 1
offset = Vector2(
max_offset.x * shake_intensity * _noise.get_noise_2d(_noise.seed, _noise_y),
max_offset.y * shake_intensity * _noise.get_noise_2d(_noise.seed + 100, _noise_y)
)
rotation = max_rotation * shake_intensity * _noise.get_noise_2d(_noise.seed + 200, _noise_y)
else:
offset = Vector2.ZERO
rotation = 0.0
# Usage from any node:
# get_viewport().get_camera_2d().shake(0.6)9. Custom Resource (Data Container)
# item_data.gd
class_name ItemData
extends Resource
@export var name: String = ""
@export var description: String = ""
@export var icon: Texture2D
@export var stack_size: int = 1
@export var value: int = 0
@export_enum("Weapon", "Armor", "Consumable", "Key") var type: int = 0
@export var stats: Dictionary = {}
func get_display_name() -> String:
return name if name != "" else "Unknown Item"
# Create in editor: right-click FileSystem > New Resource > ItemData
# Or in code:
# var sword := ItemData.new()
# sword.name = "Iron Sword"
# sword.value = 50
# ResourceSaver.save(sword, "res://items/iron_sword.tres")
#
# Load:
# var sword: ItemData = load("res://items/iron_sword.tres")
# --- Usage in inventory system ---
# @export var items: Array[ItemData] = []
# for item in items:
# print(item.get_display_name())10. Singleton / Autoload Pattern
# GameState.gd -- Add to Project > Project Settings > Autoload
extends Node
signal score_changed(new_score: int)
signal game_over
# Persistent state across scenes
var score: int = 0 :
set(value):
score = value
score_changed.emit(score)
var high_score: int = 0
var current_level: int = 1
var is_paused: bool = false
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS # Keep processing even when paused
func add_score(points: int) -> void:
score += points
if score > high_score:
high_score = score
func reset() -> void:
score = 0
current_level = 1
func pause_game() -> void:
is_paused = true
get_tree().paused = true
func resume_game() -> void:
is_paused = false
get_tree().paused = false
func toggle_pause() -> void:
if is_paused:
resume_game()
else:
pause_game()
# Usage from anywhere:
# GameState.add_score(100)
# GameState.pause_game()
# GameState.score_changed.connect(func(s): $ScoreLabel.text = str(s))11. 3D Mouse Picking / Raycasting
extends Node3D
@export var ray_length: float = 1000.0
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
var result := _raycast_from_mouse(event.position)
if result:
print("Clicked on: ", result.collider.name)
print("At position: ", result.position)
print("Surface normal: ", result.normal)
if result.collider.has_method("interact"):
result.collider.interact()
func _raycast_from_mouse(mouse_pos: Vector2) -> Dictionary:
var camera := get_viewport().get_camera_3d()
if camera == null:
return {}
var from := camera.project_ray_origin(mouse_pos)
var to := from + camera.project_ray_normal(mouse_pos) * ray_length
var query := PhysicsRayQueryParameters3D.create(from, to)
query.collision_mask = 1 # Layer 1 only
query.collide_with_areas = true
var space_state := get_world_3d().direct_space_state
return space_state.intersect_ray(query)
# 2D equivalent:
# func _raycast_from_mouse_2d(mouse_pos: Vector2) -> Dictionary:
# var space_state := get_world_2d().direct_space_state
# var query := PhysicsPointQueryParameters2D.new()
# query.position = get_global_mouse_position()
# query.collision_mask = 1
# var results := space_state.intersect_point(query)
# return results[0] if results.size() > 0 else {}12. Signal Connection Patterns
extends Node
signal player_died(player_name: String)
signal wave_completed(wave_number: int, enemies_killed: int)
func _ready() -> void:
# 1. Direct connect
$Button.pressed.connect(_on_button_pressed)
# 2. Lambda connect
$Button.pressed.connect(func(): print("Pressed!"))
# 3. One-shot (auto-disconnects after first emission)
$AnimationPlayer.animation_finished.connect(
func(anim_name): print("Played: ", anim_name),
CONNECT_ONE_SHOT
)
# 4. Deferred (called at end of frame)
$Area2D.body_entered.connect(_on_body_entered, CONNECT_DEFERRED)
# 5. Connect with extra arguments via bind
for i in range(5):
var btn := $Buttons.get_child(i) as Button
btn.pressed.connect(_on_numbered_button.bind(i))
# 6. Connect to Autoload signal
GameState.game_over.connect(_on_game_over)
# 7. Check if connected
if not $Timer.timeout.is_connected(_on_timeout):
$Timer.timeout.connect(_on_timeout)
func _on_button_pressed() -> void:
print("Button pressed")
func _on_numbered_button(index: int) -> void:
print("Button %d pressed" % index)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
body.take_damage(10)
func _on_timeout() -> void:
pass
func _on_game_over() -> void:
get_tree().change_scene_to_file("res://scenes/game_over.tscn")13. Finite State Machine
# state_machine.gd
class_name StateMachine
extends Node
@export var initial_state: State
var current_state: State
var states: Dictionary[StringName, State] = {}
func _ready() -> void:
for child in get_children():
if child is State:
states[child.name.to_lower()] = child
child.state_machine = self
if initial_state:
initial_state.enter({})
current_state = initial_state
func _process(delta: float) -> void:
if current_state:
current_state.update(delta)
func _physics_process(delta: float) -> void:
if current_state:
current_state.physics_update(delta)
func _unhandled_input(event: InputEvent) -> void:
if current_state:
current_state.handle_input(event)
func transition_to(target_state_name: StringName, data: Dictionary = {}) -> void:
if not states.has(target_state_name):
push_warning("State '%s' not found." % target_state_name)
return
current_state.exit()
current_state = states[target_state_name]
current_state.enter(data)
# state.gd
class_name State
extends Node
var state_machine: StateMachine
func enter(_data: Dictionary) -> void:
pass
func exit() -> void:
pass
func update(_delta: float) -> void:
pass
func physics_update(_delta: float) -> void:
pass
func handle_input(_event: InputEvent) -> void:
pass
# --- Example: idle_state.gd ---
# extends State
#
# func enter(_data: Dictionary) -> void:
# owner.get_node("AnimationPlayer").play("idle")
#
# func physics_update(_delta: float) -> void:
# if Input.get_axis("move_left", "move_right") != 0.0:
# state_machine.transition_to("run")
# if Input.is_action_just_pressed("jump") and owner.is_on_floor():
# state_machine.transition_to("jump", {"force": -400.0})14. Simple Dialogue System
# dialogue_system.gd -- Autoload or attach to a Control node
class_name DialogueSystem
extends CanvasLayer
signal dialogue_started
signal dialogue_finished
@onready var panel: PanelContainer = $Panel
@onready var name_label: Label = $Panel/VBox/NameLabel
@onready var text_label: RichTextLabel = $Panel/VBox/TextLabel
@onready var indicator: TextureRect = $Panel/VBox/ContinueIndicator
var _dialogue_lines: Array[Dictionary] = []
var _current_line: int = -1
var _is_active: bool = false
var _is_typing: bool = false
const TYPE_SPEED: float = 0.03 # Seconds per character
func start_dialogue(lines: Array[Dictionary]) -> void:
# lines format: [{"name": "NPC", "text": "Hello!"}, ...]
_dialogue_lines = lines
_current_line = -1
_is_active = true
panel.visible = true
indicator.visible = false
dialogue_started.emit()
advance()
func advance() -> void:
if _is_typing:
# Skip typewriter effect
text_label.visible_ratio = 1.0
_is_typing = false
indicator.visible = true
return
_current_line += 1
if _current_line >= _dialogue_lines.size():
end_dialogue()
return
var line: Dictionary = _dialogue_lines[_current_line]
name_label.text = line.get("name", "")
text_label.text = line.get("text", "")
indicator.visible = false
_type_text()
func _type_text() -> void:
_is_typing = true
text_label.visible_ratio = 0.0
var tween := create_tween()
var duration: float = text_label.text.length() * TYPE_SPEED
tween.tween_property(text_label, "visible_ratio", 1.0, duration)
tween.tween_callback(func():
_is_typing = false
indicator.visible = true
)
func end_dialogue() -> void:
_is_active = false
panel.visible = false
dialogue_finished.emit()
func _unhandled_input(event: InputEvent) -> void:
if not _is_active:
return
if event.is_action_pressed("ui_accept") or event.is_action_pressed("interact"):
advance()
get_viewport().set_input_as_handled()
# Usage:
# var lines := [
# {"name": "Elder", "text": "Welcome to our village, traveler."},
# {"name": "Elder", "text": "The forest to the east has become dangerous."},
# {"name": "Player", "text": "I will investigate."},
# ]
# DialogueSystem.start_dialogue(lines)
# await DialogueSystem.dialogue_finished15. Health / Damage System Component
# health_component.gd -- Attach to any Node with a health bar
class_name HealthComponent
extends Node
signal health_changed(current: int, maximum: int)
signal damaged(amount: int, source: Node)
signal healed(amount: int)
signal died
@export var max_health: int = 100
@export var invincibility_time: float = 0.0 # 0 = no invincibility frames
var health: int :
get:
return health
set(value):
var old := health
health = clampi(value, 0, max_health)
if health != old:
health_changed.emit(health, max_health)
if health <= 0 and old > 0:
died.emit()
var _is_invincible: bool = false
func _ready() -> void:
health = max_health
func take_damage(amount: int, source: Node = null) -> void:
if _is_invincible or health <= 0:
return
health -= amount
damaged.emit(amount, source)
if invincibility_time > 0.0:
_start_invincibility()
func heal(amount: int) -> void:
if health <= 0:
return # Can't heal if dead
health += amount
healed.emit(amount)
func _start_invincibility() -> void:
_is_invincible = true
# Flash effect on parent
var tween := create_tween().set_loops(int(invincibility_time / 0.1))
tween.tween_property(get_parent(), "modulate:a", 0.3, 0.05)
tween.tween_property(get_parent(), "modulate:a", 1.0, 0.05)
await get_tree().create_timer(invincibility_time).timeout
_is_invincible = false
get_parent().modulate.a = 1.0
func is_alive() -> bool:
return health > 0
func get_health_ratio() -> float:
return float(health) / float(max_health)16. Object Pooling
# object_pool.gd
class_name ObjectPool
extends Node
@export var scene: PackedScene
@export var pool_size: int = 20
@export var auto_expand: bool = true
var _pool: Array[Node] = []
func _ready() -> void:
for i in pool_size:
_create_instance()
func _create_instance() -> Node:
var instance := scene.instantiate()
instance.set_meta("pooled", true)
add_child(instance)
_deactivate(instance)
_pool.append(instance)
return instance
func acquire() -> Node:
for instance in _pool:
if not instance.visible:
_activate(instance)
return instance
if auto_expand:
var instance := _create_instance()
_activate(instance)
return instance
push_warning("ObjectPool: No available instances and auto_expand is off.")
return null
func release(instance: Node) -> void:
if instance.get_meta("pooled", false):
_deactivate(instance)
func _activate(instance: Node) -> void:
instance.visible = true
instance.process_mode = Node.PROCESS_MODE_INHERIT
if instance.has_method("on_pool_acquire"):
instance.on_pool_acquire()
func _deactivate(instance: Node) -> void:
instance.visible = false
instance.process_mode = Node.PROCESS_MODE_DISABLED
if instance.has_method("on_pool_release"):
instance.on_pool_release()
# Usage:
# @onready var bullet_pool: ObjectPool = $BulletPool
# func fire():
# var bullet = bullet_pool.acquire()
# if bullet:
# bullet.global_position = $Muzzle.global_position
# bullet.direction = transform.x17. 3D First-Person Controller
extends CharacterBody3D
@export_group("Movement")
@export var walk_speed: float = 5.0
@export var sprint_speed: float = 8.0
@export var jump_velocity: float = 4.5
@export var acceleration: float = 10.0
@export var air_control: float = 0.3
@export_group("Mouse Look")
@export var mouse_sensitivity: float = 0.002
@export var max_pitch: float = 89.0
@onready var head: Node3D = $Head
@onready var camera: Camera3D = $Head/Camera3D
var _gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
rotate_y(-event.relative.x * mouse_sensitivity)
head.rotate_x(-event.relative.y * mouse_sensitivity)
head.rotation.x = clampf(head.rotation.x, deg_to_rad(-max_pitch), deg_to_rad(max_pitch))
if event.is_action_pressed("ui_cancel"):
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _physics_process(delta: float) -> void:
# Gravity
if not is_on_floor():
velocity.y -= _gravity * delta
# Jump
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
# Movement
var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
var speed := sprint_speed if Input.is_action_pressed("sprint") else walk_speed
var accel := acceleration if is_on_floor() else acceleration * air_control
if direction:
velocity.x = move_toward(velocity.x, direction.x * speed, accel * delta)
velocity.z = move_toward(velocity.z, direction.z * speed, accel * delta)
else:
velocity.x = move_toward(velocity.x, 0.0, accel * delta)
velocity.z = move_toward(velocity.z, 0.0, accel * delta)
move_and_slide()18. Inventory System
# inventory.gd
class_name Inventory
extends Resource
signal item_added(item: ItemData, slot: int)
signal item_removed(item: ItemData, slot: int)
signal inventory_changed
@export var slots: Array[ItemData] = []
@export var max_slots: int = 20
func _init() -> void:
slots.resize(max_slots)
func add_item(item: ItemData) -> bool:
# First try to stack with existing item
for i in slots.size():
if slots[i] != null and slots[i].name == item.name:
if slots[i].stack_size > 1: # Stackable
item_added.emit(item, i)
inventory_changed.emit()
return true
# Find empty slot
for i in slots.size():
if slots[i] == null:
slots[i] = item
item_added.emit(item, i)
inventory_changed.emit()
return true
push_warning("Inventory full!")
return false
func remove_item(slot: int) -> ItemData:
if slot < 0 or slot >= slots.size() or slots[slot] == null:
return null
var item := slots[slot]
slots[slot] = null
item_removed.emit(item, slot)
inventory_changed.emit()
return item
func swap_items(slot_a: int, slot_b: int) -> void:
var temp := slots[slot_a]
slots[slot_a] = slots[slot_b]
slots[slot_b] = temp
inventory_changed.emit()
func get_item(slot: int) -> ItemData:
if slot < 0 or slot >= slots.size():
return null
return slots[slot]
func has_item(item_name: String) -> bool:
return slots.any(func(item): return item != null and item.name == item_name)
func get_item_count() -> int:
return slots.reduce(func(acc, item): return acc + (1 if item != null else 0), 0)19. Spawner with Weighted Random
extends Node2D
@export var spawn_interval: float = 2.0
@export var max_spawns: int = 10
@export var spawn_radius: float = 300.0
## Array of {"scene": PackedScene, "weight": float}
@export var spawn_table: Array[Dictionary] = []
var _spawn_count: int = 0
var _total_weight: float = 0.0
func _ready() -> void:
# Calculate total weight
for entry in spawn_table:
_total_weight += entry.get("weight", 1.0)
# Start spawning
var timer := Timer.new()
timer.wait_time = spawn_interval
timer.timeout.connect(_on_spawn_timer)
timer.autostart = true
add_child(timer)
func _on_spawn_timer() -> void:
if _spawn_count >= max_spawns:
return
var scene := _pick_random_scene()
if scene == null:
return
var instance := scene.instantiate() as Node2D
instance.global_position = _get_random_spawn_position()
get_parent().add_child(instance)
_spawn_count += 1
# Track when spawned entity is freed
instance.tree_exited.connect(func(): _spawn_count -= 1)
func _pick_random_scene() -> PackedScene:
var roll := randf() * _total_weight
var cumulative := 0.0
for entry in spawn_table:
cumulative += entry.get("weight", 1.0)
if roll <= cumulative:
return entry.get("scene") as PackedScene
return spawn_table.back().get("scene") as PackedScene
func _get_random_spawn_position() -> Vector2:
var angle := randf() * TAU
var distance := randf_range(50.0, spawn_radius)
return global_position + Vector2(cos(angle), sin(angle)) * distance20. Pause Menu with Settings
extends Control
@onready var master_slider: HSlider = %MasterSlider
@onready var sfx_slider: HSlider = %SFXSlider
@onready var music_slider: HSlider = %MusicSlider
@onready var fullscreen_check: CheckButton = %FullscreenCheck
@onready var vsync_check: CheckButton = %VsyncCheck
func _ready() -> void:
visible = false
process_mode = Node.PROCESS_MODE_ALWAYS
# Initialize UI from current settings
master_slider.value = db_to_linear(AudioServer.get_bus_volume_db(
AudioServer.get_bus_index("Master")))
sfx_slider.value = db_to_linear(AudioServer.get_bus_volume_db(
AudioServer.get_bus_index("SFX")))
music_slider.value = db_to_linear(AudioServer.get_bus_volume_db(
AudioServer.get_bus_index("Music")))
fullscreen_check.button_pressed = (
DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN)
vsync_check.button_pressed = (
DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED)
# Connect signals
master_slider.value_changed.connect(_on_master_changed)
sfx_slider.value_changed.connect(_on_sfx_changed)
music_slider.value_changed.connect(_on_music_changed)
fullscreen_check.toggled.connect(_on_fullscreen_toggled)
vsync_check.toggled.connect(_on_vsync_toggled)
%ResumeButton.pressed.connect(_on_resume)
%QuitButton.pressed.connect(_on_quit)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("pause"):
_toggle_pause()
get_viewport().set_input_as_handled()
func _toggle_pause() -> void:
visible = !visible
get_tree().paused = visible
func _on_resume() -> void:
_toggle_pause()
func _on_quit() -> void:
get_tree().paused = false
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func _on_master_changed(value: float) -> void:
_set_bus_volume("Master", value)
func _on_sfx_changed(value: float) -> void:
_set_bus_volume("SFX", value)
func _on_music_changed(value: float) -> void:
_set_bus_volume("Music", value)
func _set_bus_volume(bus_name: String, linear_value: float) -> void:
var bus_index := AudioServer.get_bus_index(bus_name)
AudioServer.set_bus_volume_db(bus_index, linear_to_db(linear_value))
AudioServer.set_bus_mute(bus_index, linear_value < 0.01)
func _on_fullscreen_toggled(enabled: bool) -> void:
if enabled:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
func _on_vsync_toggled(enabled: bool) -> void:
if enabled:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
else:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)Common Node Hierarchy Recipes
2D Platformer Player
CharacterBody2D (player.gd)
+-- Sprite2D (or AnimatedSprite2D)
+-- CollisionShape2D (CapsuleShape2D)
+-- Camera2D
+-- AnimationPlayer
+-- StateMachine
| +-- Idle (idle_state.gd)
| +-- Run (run_state.gd)
| +-- Jump (jump_state.gd)
+-- Hitbox (Area2D)
| +-- CollisionShape2D
+-- Hurtbox (Area2D)
| +-- CollisionShape2D
+-- CoyoteTimer (Timer, one_shot=true)
+-- JumpBufferTimer (Timer, one_shot=true)3D Character
CharacterBody3D (character.gd)
+-- CollisionShape3D (CapsuleShape3D)
+-- Head (Node3D)
| +-- Camera3D
+-- MeshInstance3D (or imported scene)
+-- AnimationTree
+-- NavigationAgent3D
+-- RayCast3D (ground detect)UI Layout
Control (root)
+-- MarginContainer
| +-- VBoxContainer
| +-- Label (title)
| +-- HBoxContainer
| | +-- TextureRect (icon)
| | +-- VBoxContainer
| | +-- Label (name)
| | +-- ProgressBar (health)
| +-- GridContainer (inventory grid)
+-- CanvasLayer (layer=10, for overlays)
+-- PauseMenu (Control)
+-- DialogueBox (Control)Key Project Settings
| Setting | Path | Default | Notes |
|---|---|---|---|
| Window size | display/window/size/viewport_width/height |
1152x648 | Base resolution |
| Stretch mode | display/window/stretch/mode |
disabled |
canvas_items for pixel art, viewport for 3D |
| Stretch aspect | display/window/stretch/aspect |
ignore |
keep or expand recommended |
| Physics FPS | physics/common/physics_ticks_per_second |
60 | |
| 2D gravity | physics/2d/default_gravity |
980.0 | pixels/sec^2 |
| 3D gravity | physics/3d/default_gravity |
9.8 | m/sec^2 |
| Rendering method | rendering/renderer/rendering_method |
forward_plus |
mobile, gl_compatibility |
| MSAA 3D | rendering/anti_aliasing/quality/msaa_3d |
disabled |
2x, 4x, 8x |
| Shadow atlas size | rendering/lights_and_shadows/directional_shadow/size |
4096 | |
| Audio driver | audio/driver/driver |
platform default |
File System Conventions
res:// # Project root (read-only in exported builds)
+-- project.godot # Project configuration
+-- scenes/ # .tscn scene files
+-- scripts/ # .gd script files
+-- assets/
| +-- sprites/ # 2D textures
| +-- models/ # 3D models (.glb, .gltf)
| +-- audio/ # Sound effects and music
| +-- fonts/ # .ttf, .otf font files
| +-- shaders/ # .gdshader files
+-- resources/ # .tres custom resources
+-- addons/ # Editor plugins
+-- export_presets.cfg # Export configurations
user:// # Writable directory (persistent)
# Windows: %APPDATA%/Godot/app_userdata/<project_name>/
# Linux: ~/.local/share/godot/app_userdata/<project_name>/
# macOS: ~/Library/Application Support/Godot/app_userdata/<project_name>/Debugging Cheat Sheet
| Task | Method |
|---|---|
| Print to output | print(), print_rich(), printerr() |
| Breakpoints | Click line number gutter in script editor |
| Remote scene tree | Debugger > Scene panel while running |
| Draw debug shapes | draw_line(), draw_circle() in _draw() + queue_redraw() |
| Physics debug | Project Settings > Debug > Visible Collision Shapes |
| Navigation debug | Project Settings > Debug > Visible Navigation |
| Performance monitor | Debugger > Monitors tab |
| Profile frame | Debugger > Profiler tab |
| GDScript warnings | Project Settings > Debug > GDScript > Enable Warnings |
| Assert | assert(condition, "message") |
| Push warnings | push_warning("msg"), push_error("msg") |
| Group calls debug | print(get_tree().get_nodes_in_group("enemies")) |
This skill covers Godot Engine 4.7. For API details on specific classes, methods, properties, signals, and enums, refer to the API class reference files listed above.