godot-master
Godot Master: Lead Architect Knowledge Hub
Every section earns its tokens by focusing on Knowledge Delta β the gap between what Claude already knows and what a senior Godot engineer knows from shipping real products.
π§ Part 1: Expert Thinking Frameworks
"Who Owns What?" β The Architecture Sanity Check
Before writing any system, answer these three questions for EVERY piece of state:
- Who owns the data? (The
StatsComponentowns health, NOT theCombatSystem) - Who is allowed to change it? (Only the owner via a public method like
apply_damage()) - Who needs to know it changed? (Anyone listening to the
health_changedsignal)
If you can't answer all three for every state variable, your architecture has a coupling problem. This is not OOP encapsulation β this is Godot-specific because the signal system IS the enforcement mechanism, not access modifiers.
The Godot "Layer Cake"
Organize every feature into four layers. Signals travel UP, never down:
ββββββββββββββββββββββββββββββββ
β PRESENTATION (UI / VFX) β β Listens to signals, never owns data
ββββββββββββββββββββββββββββββββ€
β LOGIC (State Machines) β β Orchestrates transitions, queries data
ββββββββββββββββββββββββββββββββ€
β DATA (Resources / .tres) β β Single source of truth, serializable
ββββββββββββββββββββββββββββββββ€
β INFRASTRUCTURE (Autoloads) β β Signal Bus, SaveManager, AudioBus
ββββββββββββββββββββββββββββββββ
Critical rule: Presentation MUST NOT modify Data directly. Infrastructure speaks exclusively through signals. If a Label node is calling player.health -= 1, the architecture is broken.
The Signal Bus Tiered Architecture
- Global Bus (Autoload): ONLY for lifecycle events (
match_started,player_died,settings_changed). Debugging sprawl is the cost β limit events to < 15. - Scoped Feature Bus: Each feature folder has its own bus (e.g.,
CombatBusonly for combat nodes). This is the compromise that scales. - Direct Signals: Parent-child communication WITHIN a single scene. Never across scene boundaries.
π§ Part 2: Architectural Decision Frameworks
The Master Decision Matrix
| Scenario | Strategy | MANDATORY Skill Chain | Trade-off |
|---|---|---|---|
| Rapid Prototype | Event-Driven Mono | READ: Foundations β Autoloads. Do NOT load genre or platform refs. | Fast start, spaghetti risk |
| Complex RPG | Component-Driven | READ: Composition β States β RPG Stats. Do NOT load multiplayer or platform refs. | Heavy setup, infinite scaling |
| Massive Open World | Resource-Streaming | READ: Open World β Save/Load. Also load Performance. | Complex I/O, float precision jitter past 10K units |
| Server-Auth Multi | Deterministic | READ: Server Arch β Multiplayer. Do NOT load single-player genre refs. | High latency, anti-cheat secure |
| Mobile/Web Port | Adaptive-Responsive | READ: UI Containers β Adapt DeskβMobile β Platform Mobile. | UI complexity, broad reach |
| Application / Tool | App-Composition | READ: App Composition β Theming. Do NOT load game-specific refs. | Different paradigm than games |
| Romance / Dating Sim | Affection Economy | READ: Romance β Dialogue β UI Rich Text. | High UI/Narrative density |
| Secrets / Easter Eggs | Intentional Obfuscation | READ: Secrets β Persistence. | Community engagement, debug risk |
| Collection Quest | Scavenger Logic | READ: Collections β Marker3D Placement. | Player retention, exploration drive |
| Seasonal Event | Runtime Injection | READ: Easter Theming β Material Swapping. | Fast branding, no asset pollution |
| Souls-like Mortality | Risk-Reward Revival | READ: Revival/Corpse Run β Physics 3D. | High tension, player frustration risk |
| Wave-based Action | Combat Pacing Loop | READ: Waves β Combat. | Escalating tension, encounter design |
| Survival Economy | Harvesting Loop | READ: Harvesting β Inventory. | Resource scarcity, loop persistence |
| Racing / Speedrun | Validation Loop | READ: Time Trials β Input Buffer. | High precision, ghost record drive |
The "When NOT to Use a Node" Decision
One of the most impactful expert-only decisions. The Godot docs explicitly say "avoid using nodes for everything":
| Type | When to Use | Cost | Expert Use Case |
|---|---|---|---|
Object |
Custom data structures, manual memory management | Lightest. Must call .free() manually. |
Custom spatial hash maps, ECS-like data stores |
RefCounted |
Transient data packets, logic objects that auto-delete | Auto-deleted when no refs remain. | DamageRequest, PathQuery, AbilityEffect β logic packets that don't need the scene tree |
Resource |
Serializable data with Inspector support | Slightly heavier than RefCounted. Handles .tres I/O. |
ItemData, EnemyStats, DialogueLine β any data a designer should edit in Inspector |
Node |
Needs _process/_physics_process, needs to live in the scene tree |
Heaviest β SceneTree overhead per node. | Only for entities that need per-frame updates or spatial transforms |
The expert pattern: Use RefCounted subclasses for all logic packets and data containers. Reserve Node for things that must exist in the spatial tree. This halves scene tree overhead for complex systems.
π§ Part 3: Core Workflows
Workflow 1: Professional Scaffolding
From empty project to production-ready container.
MANDATORY β READ ENTIRE FILE: Foundations
- Organize by Feature (
/features/player/,/features/combat/), not by class type. Aplayer/folder contains the scene, script, resources, and tests for the player. - READ: Signal Architecture β Create
GlobalSignalBusautoload with < 15 events. - READ: GDScript Mastery β Enable
untyped_declarationwarning in Project Settings β GDScript β Debugging. - Apply Project Templates for base
.gitignore, export presets, and input map. - Use MCP Scene Builder if available to generate scene hierarchies programmatically. Do NOT load combat, multiplayer, genre, or platform references during scaffolding.
Workflow 2: Entity Orchestration
Building modular, testable characters.
MANDATORY Chain β READ ALL: Composition β State Machine β CharacterBody2D or Physics 3D β Animation Tree Do NOT load UI, Audio, or Save/Load references for entity work.
- The State Machine queries an
InputComponent, never handles input directly. This allows AI/Player swap with zero refactoring. - The State Machine ONLY handles transitions. Logic belongs in Components.
MoveStatetellsMoveComponentto act, not the other way around. - Every entity MUST pass the F6 test: pressing "Run Current Scene" (F6) must work without crashing. If it crashes, your entity has scene-external dependencies.
Workflow 3: Data-Driven Systems
Connecting Combat, Inventory, Stats through Resources.
MANDATORY Chain β READ ALL: Resource Patterns β RPG Stats β Combat β Inventory
- Create ONE
ItemData.gdextendingResource. Instantiate it as 100.tresfiles instead of 100 scripts. - The HUD NEVER references the Player directly. It listens for
player_health_changedon the Signal Bus. - Enable "Local to Scene" on ALL
@export Resourcevariables, or callresource.duplicate()in_ready(). Failure to do this is Bug #1 in Part 8.
Workflow 4: Persistence Pipeline
MANDATORY: Autoload Architecture β Save/Load β Scene Management
- Use dictionary-mapped serialization. Old save files MUST not corrupt when new fields are added β use
.get("key", default_value). - For procedural worlds: save the Seed plus a Delta-List of modifications, not the entire map. A 100MB world becomes a 50KB save.
Workflow 5: Performance Optimization
MANDATORY: Debugging/Profiling β Performance Optimization
Diagnosis-first approach (NEVER optimize blindly):
- High Script Time β Profile with built-in Profiler. Check if
_processis being called on hundreds of nodes. Move to single-manager pattern or Server APIs (see Part 6). - High Draw Calls β Use
MultiMeshInstancefor repetitive geometry. Batch materials with ORM textures. - Physics Stutter β Simplify collisions to primitive shapes. Load 2D Physics or 3D Physics. Check if
_processis used instead of_physics_processfor movement. - VRAM Overuse β Switch textures to VRAM Compression (BPTC/S3TC for desktop, ETC2 for mobile). Never ship raw PNG.
- Intermittent Frame Spikes β Usually GC pass, synchronous
load(), or NavigationServer recalculation. UseResourceLoader.load_threaded_request().
Workflow 6: Cross-Platform Adaptation
MANDATORY: Input Handling β Adapt DesktopβMobile β Platform Mobile Also read: Platform Desktop, Platform Web, Platform Console, Platform VR as needed.
- Use an
InputManagerautoload that translates all input types into normalized actions. NEVER readInput.is_key_pressed()directly β it blocks controller and touch support. - Mobile touch targets: minimum 44px physical size. Use
MarginContainerwith Safe Area logic for notch/cutout devices. - Web exports: Godot's
AudioServerrequires user interaction before first play (browser policy). Handle this with a "Click to Start" screen.
Workflow 7: Procedural Generation
MANDATORY: Procedural Gen β Tilemap Mastery or 3D World Building β Navigation
- ALWAYS use
FastNoiseLiteresource with a fixedseedfor deterministic generation. - Never bake NavMesh on the main thread. Use
NavigationServer3D.parse_source_geometry_data()+NavigationServer3D.bake_from_source_geometry_data_async(). - For infinite worlds: chunk loading MUST happen on a background thread using
WorkerThreadPool. Build the scene chunk off-tree, thenadd_child.call_deferred()on the main thread.
Workflow 8: Multiplayer Architecture
MANDATORY β READ ALL: Multiplayer Networking β Server Architecture β Adapt SingleβMulti Do NOT load single-player genre blueprints.
- Client sends Input, Server calculates Outcome. The Client NEVER determines damage, position deltas, or inventory changes.
- Use Client-Side Prediction with server reconciliation: predict locally, correct from server snapshot. Hides up to ~150ms of latency.
MultiplayerSpawnerhandles replication in Godot 4. Configure it per scene, not globally.
Workflow 9: Premium UI/UX
MANDATORY: UI Theming β UI Containers β Tweening β Rich Text
- NEVER override colors in Inspector. Create a
.themeresource as the single source of truth for global skinning. - Every interactive element should have micro-animation:
Tweenscale pulse on buttons,RichTextEffecton damage numbers,AnimationPlayeron panel transitions. - Use
Control.FOCUS_MODE_ALLand test full keyboard/gamepad navigation. Inaccessible UI blocks console certification.
Workflow 10: Graphics & Atmosphere
MANDATORY: 3D Lighting β 3D Materials β Shader Basics β Particles
- Use
GPUParticles3Dfor environment effects (rain, fog, fire). UseCPUParticlesONLY when script must read/write individual particle positions. - Always set
visibility_aabbmanually on GPU particles. The auto-calculated AABB is often wrong, causing particles to disappear when the emitter is off-screen. - For stylized looks: use
CanvasItemshaders withscreen_texturefor post-processing in 2D. In 3D, use aWorldEnvironmentwith customEnvironmentresource. ReflectionProbevsVoxelGIvsSDFGI: Probes are cheap/static, VoxelGI is medium/baked, SDFGI is expensive/dynamic. Choose based on your platform budget (see Part 5).
π« Part 4: The Expert NEVER List
Each rule includes the non-obvious reason β the thing only shipping experience teaches.
- NEVER use
get_tree().root.get_node("...")β Absolute paths break when ANY ancestor is renamed or reparented. Use%UniqueNames,@export NodePath, or signal-based discovery. - NEVER use
load()inside a loop or_processβ Synchronous disk read blocks the ENTIRE main thread. Usepreload()at script top for small assets,ResourceLoader.load_threaded_request()for large ones. - NEVER
queue_free()while external references exist β Parent nodes or arrays holding refs will get "Deleted Object" errors. Clean up refs in_exit_tree()and set them tonullbefore freeing. - NEVER put gameplay logic in
_draw()β_draw()is called on the rendering thread. Mutating game state causes race conditions with_physics_process. - NEVER use
Area2Dfor 1000+ overlapping objects β Each overlap check has O(nΒ²) broadphase cost. UseShapeCast2D,PhysicsDirectSpaceState2D.intersect_shape(), or Server APIs for bullet-hell patterns. - NEVER mutate external state from a component β If
HealthComponentcalls$HUD.update_bar(), deleting the HUD crashes the game. Components emit signals; listeners decide how to respond. - NEVER use
awaitin_physics_processβawaityields execution, meaning the physics step skips frames. Move async operations to a separate method triggered by a signal. - NEVER use
Stringkeys in hot-path dictionary lookups β String hashing is O(n). UseStringName(&"key") for O(1) pointer comparisons, or integer enums. - NEVER store
Callablereferences to freed objects β Crashes silently or throws errors. Disconnect signals in_exit_tree()or useCONNECT_ONE_SHOT. - NEVER use
_processfor 1000+ entities β Each_processcall has per-node SceneTree overhead. Use a singleManager._processthat iterates an array of data structs (Data-Oriented pattern), or use Server APIs directly. - NEVER use
Tweenon a node that may be freed β If a node isqueue_free()'d while a Tween runs, it errors. Kill tweens in_exit_tree()or bind to SceneTree:get_tree().create_tween(). - NEVER request data FROM
RenderingServerorPhysicsServerin_processβ These servers run asynchronously. Calling getter functions forces a synchronous stall that kills performance. The APIs are intentionally designed to be write-only in hot paths. - NEVER use
call_deferred()as a band-aid for initialization order bugs β It masks architectural problems (dependency on tree order). Fix the actual dependency with explicit initialization signals or@onready. - NEVER create circular signal connections β Node A connects to B, B connects to A. This creates infinite loops on the first emit. Use a mediator pattern (Signal Bus) to break cycles.
- NEVER let inheritance exceed 3 levels β Beyond 3, debugging
super()chains is a nightmare. Use composition (Nodechildren) to add behaviors instead.
π Part 5: Performance Budgets (Concrete Numbers)
| Metric | Mobile Target | Desktop Target | Expert Note |
|---|---|---|---|
| Draw Calls | < 100 (2D), < 200 (3D) | < 500 | MultiMeshInstance for foliage/debris |
| Triangle Count | < 100K visible | < 1M visible | LOD system mandatory above 500K |
| Texture VRAM | < 512MB | < 2GB | VRAM Compression: ETC2 (mobile), BPTC (desktop) |
| Script Time | < 4ms per frame | < 8ms per frame | Move hot loops to Server APIs |
| Physics Bodies | < 200 active | < 1000 active | Use PhysicsServer direct API for mass sim |
| Particles | < 2000 total | < 10000 total | GPU particles, set visibility_aabb manually |
| Audio Buses | < 8 simultaneous | < 32 simultaneous | Use Audio Systems bus routing |
| Save File Size | < 1MB | < 50MB | Seed + Delta pattern for procedural worlds |
| Scene Load Time | < 500ms | < 2s | ResourceLoader.load_threaded_request() |
βοΈ Part 6: Server APIs β The Expert Performance Escape Hatch
This is knowledge most Godot developers never learn. When the scene tree becomes a bottleneck, bypass it entirely using Godot's low-level Server APIs.
When to Drop to Server APIs
- 10K+ rendered instances (sprites, meshes): Use
RenderingServerwith RIDs instead ofSprite2D/MeshInstance3Dnodes. - Bullet-hell / particle systems with script interaction: Use
PhysicsServer2Dbody creation instead ofArea2Dnodes. - Mass physics simulation: Use
PhysicsServer3Ddirectly for ragdoll fields, debris, or fluid-like simulations.
The RID Pattern (Expert)
Server APIs communicate through RID (Resource ID) β opaque handles to server-side objects. Critical rules:
# Create server-side canvas item (NO node overhead)
var ci_rid := RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(ci_rid, get_canvas_item())
# CRITICAL: Keep resource references alive. RIDs don't count as references.
# If the Texture resource is GC'd, the RID becomes invalid silently.
var texture: Texture2D = preload("res://sprite.png")
RenderingServer.canvas_item_add_texture_rect(ci_rid, Rect2(-texture.get_size() / 2, texture.get_size()), texture)
Threading with Servers
- The scene tree is NOT thread-safe. But Server APIs (RenderingServer, PhysicsServer) ARE thread-safe when enabled in Project Settings.
- You CAN build scene chunks (instantiate + add_child) on a worker thread, but MUST use
add_child.call_deferred()to attach them to the live tree. - GDScript Dictionaries/Arrays: reads and writes across threads are safe, but resizing (append, erase, resize) requires a
Mutex. - NEVER load the same
Resourcefrom multiple threads simultaneously β use one loading thread.
π§© Part 7: Expert Code Patterns
The Component Registry
class_name Entity extends CharacterBody2D
var _components: Dictionary = {}
func _ready() -> void:
for child in get_children():
if child.has_method("get_component_name"):
_components[child.get_component_name()] = child
func get_component(component_name: StringName) -> Node:
return _components.get(component_name)
Dead Instance Safe Signal Handler
func _on_damage_dealt(target: Node, amount: int) -> void:
if not is_instance_valid(target): return
if target.is_queued_for_deletion(): return
target.get_component(&"health").apply_damage(amount)
The Async Resource Loader
func _load_level_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
while ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
await get_tree().process_frame
var scene: PackedScene = ResourceLoader.load_threaded_get(path)
add_child(scene.instantiate())
State Machine Transition Guard
func can_transition_to(new_state: StringName) -> bool:
match name:
&"Dead": return false # Terminal state
&"Stunned": return new_state == &"Idle" # Can only recover to Idle
_: return true
Thread-Safe Chunk Loader (Server API Pattern)
func _load_chunk_threaded(chunk_pos: Vector2i) -> void:
# Build scene chunk OFF the active tree (thread-safe)
var chunk := _generate_chunk(chunk_pos)
# Attach to live tree from main thread ONLY
_world_root.add_child.call_deferred(chunk)
π₯ Part 8: Godot 4.x Gotchas (Veteran-Only)
@exportResources are shared by default: Multiple scene instances ALL share the sameResource. Useresource.duplicate()in_ready()or enable "Local to Scene" checkbox. This is the #1 most reported Godot 4 bug by newcomers.- Signal syntax silently fails:
connect("signal_name", target, "method")(Godot 3 syntax) compiles but does nothing in Godot 4. Must usesignal_name.connect(callable). Tweenis no longer a Node: Created viacreate_tween(), bound to the creating node's lifetime. If that node is freed, the Tween dies. Useget_tree().create_tween()for persistent tweens.PhysicsBodylayers vs masks:collision_layer= "what I am".collision_mask= "what I scan for". Setting both to the same value causes self-collision or missed detections.StringNamevsStringin hot paths:StringName(&"key") uses pointer comparison (O(1)).Stringuses character comparison (O(n)). Always useStringNamefor dictionary keys in_process.@onreadytiming: Runs AFTER_init()but DURING_ready(). If you need constructor-time setup, use_init(). If you need tree access, use@onreadyor_ready(). Mixing them causes nulls.- Server query stalls: Calling
RenderingServerorPhysicsServergetter functions in_processforces a synchronous pipeline flush. These servers run async β requesting data from them stalls the entire pipeline until the server catches up. move_and_slide()API change: Returnsbool(whether collision occurred). Velocity is now a property, not a parameter.velocity = dir * speedbefore callingmove_and_slide().
π Part 9: Module Directory (86 Blueprints)
[!IMPORTANT] Load ONLY the modules needed for your current workflow. Use the Decision Matrix in Part 2 to determine which chain to follow.
Architecture & Foundation
Foundations | Composition | App Composition | Signals | Autoloads | States | Resources | Templates | MCP Setup | Skill Discovery | Skill Judge
GDScript & Testing
GDScript Mastery | Testing Patterns | Debugging/Profiling | Performance Optimization
2D Systems
2D Animation | 2D Physics | Tilemaps | Animation Player | Animation Tree | CharacterBody2D | Particles | Tweening | Shader Basics | Camera Systems
3D Systems
3D Lighting | 3D Materials | 3D World Building | Physics 3D | Navigation/Pathfinding | Procedural Generation
Gameplay Mechanics
Abilities | Combat | Dialogue | Economy | Inventory | Questing | RPG Stats | Turn System | Audio | Scene Transitions | Save/Load | Secrets | Collections | Waves | Harvesting | Time Trials | Revival
UI & UX
UI Containers | Rich Text | Theming | Input Handling | Seasonal Theming
Connectivity & Platforms
Multiplayer | Server Logic | Export Builds | Desktop | Mobile | Web | Console | VR
Adaptation Guides
2Dβ3D | 3Dβ2D | DesktopβMobile | MobileβDesktop | SingleβMulti
Genre Blueprints
Action RPG | Shooter | RTS | MOBA | Rogue-like | Survival | Open World | Metroidvania | Platformer | Fighting | Stealth | Sandbox | Horror | Puzzle | Racing | Rhythm | Sports | Battle Royale | Card Game | Visual Novel | Romance | Simulation | Tower Defense | Idle Clicker | Party | Educational
MCP Tooling
π Part 10: Expert Diagnostic Patterns
The "Invisible Node" Bug
Symptom: Node exists in tree but isn't rendering.
Expert diagnosis chain: visible property β z_index β parent CanvasLayer wrong layer β modulate.a == 0 β behind camera's near clip (3D) β SubViewport.render_target_update_mode not set β CanvasItem not in any CanvasLayer (renders behind everything).
The "Input Eaten" Bug
Symptom: Clicks or key presses ignored intermittently.
Expert diagnosis: Another Control node with mouse_filter = STOP overlapping the target. Or, modal PopupMenu consuming unhandled input. Or, _unhandled_input() in another script calling get_viewport().set_input_as_handled().
The "Physics Jitter" Bug
Symptom: Character vibrates at surface contacts.
Expert diagnosis: Safe Margin too large. Or, _process used for movement instead of _physics_process (interpolation mismatch). Or, collision shapes overlap at spawn (push each other apart permanently).
The "Memory Leak"
Symptom: RAM grows steadily during play.
Expert diagnosis: queue_free() called but reference held in Array/Dictionary. Or, signals connected with CONNECT_REFERENCE_COUNTED without cleanup. Use Profiler "Objects" tab to find orphaned instances. Search for Node instances without a parent.
The "Frame Spike"
Symptom: Smooth FPS but periodic drops.
Expert diagnosis: GDScript GC pass. Or, synchronous load() for a large resource. Or, NavigationServer rebaking. Or, Server API query stall (requesting data from RenderingServer in _process). Profile with built-in Profiler β look for function-level spikes.