# Godot Engine Architecture Knowledge ## Overview Godot is an open-source game engine using a scene tree architecture where everything is a node. Scenes are reusable node compositions that can be instanced, nested, and inherited. Godot supports GDScript (Python-like), C#, and C++ via GDExtension. ## Architecture Fundamentals ### Scene Tree Model Godot's architecture is built on a tree of nodes. Every game element — from UI to physics bodies to audio — is a node in the scene tree. **Key concepts:** - **Node** — Base building block. Has a name, can have children, receives callbacks (`_ready`, `_process`, `_physics_process`) - **Scene** — A saved tree of nodes (`.tscn` file). Can be instanced at runtime - **Autoload** — Singleton nodes loaded before any scene, accessible globally via name - **Resource** — Data objects (`.tres`) that can be shared across nodes and saved to disk **Node hierarchy pattern:** ``` Main (Node) ├── World (Node2D / Node3D) │ ├── Level (Node) │ │ ├── TileMap / GridMap │ │ └── SpawnPoints │ ├── Entities (Node) │ │ ├── Player (CharacterBody2D/3D) │ │ └── Enemies (Node) │ └── Camera2D / Camera3D ├── UI (CanvasLayer) │ ├── HUD │ └── Menus └── Systems (Node) ├── AudioManager └── GameStateManager ``` ### Autoloads vs Dependency Injection **Autoloads (Singletons):** Use sparingly for truly global services: - `GameManager` — Game state, pause, scene transitions - `AudioManager` — Music and SFX playback - `SaveManager` — Save/load operations - `EventBus` — Global signal hub (if using event bus pattern) **When NOT to use autoloads:** - Systems only needed in gameplay scenes (use scene composition instead) - Data that should be passed explicitly (use Resources or method parameters) - Anything that makes unit testing harder ### Signal Architecture Signals are Godot's observer pattern implementation. They decouple communication between nodes. **Best practices:** ```gdscript # Define signals with typed parameters signal health_changed(new_health: int, max_health: int) signal died # Connect in code (preferred for dynamic connections) enemy.died.connect(_on_enemy_died) # Emit with arguments health_changed.emit(current_health, max_health) ``` **Signal vs direct call guidelines:** | Use Signals When | Use Direct Calls When | |---|---| | Notifying unknown listeners | Calling a known child node | | Broadcasting state changes | Requesting specific behavior | | Decoupling siblings | Parent configuring a child | | Multiple listeners possible | Single target, synchronous | ### Resource-Based Data Custom Resources are powerful for data-driven design: ```gdscript # weapon_stats.gd class_name WeaponStats extends Resource @export var damage: float = 10.0 @export var attack_speed: float = 1.0 @export var range: float = 100.0 @export var damage_type: DamageType = DamageType.PHYSICAL var dps: float: get: return damage * attack_speed ``` **Use Resources for:** - Item definitions, enemy stats, ability data - Configuration presets - Shared data between scenes - Any data that designers should edit in the Inspector ## GDScript vs C# Decision Guide | Factor | GDScript | C# | |---|---|---| | **Learning curve** | Easy, Python-like | Moderate, requires .NET knowledge | | **Engine integration** | Native, full API access | Full API access, slight overhead | | **Performance** | Good for gameplay logic | Better for computation-heavy code | | **Typing** | Optional static typing | Strong static typing | | **Tooling** | Built-in editor | VS Code, Rider, Visual Studio | | **Ecosystem** | Godot-native addons | NuGet packages available | | **Export targets** | All platforms | Most platforms (.NET 6+ required) | | **Team familiarity** | Godot-specific skill | Transferable C# skill | **Recommendation:** Use GDScript as default. Use C# when the team has strong C# background or when heavy computation benefits from it. Mixing is supported but adds build complexity. ### GDScript Static Typing Always use static typing for better performance and error catching: ```gdscript # Preferred: fully typed var speed: float = 200.0 var direction: Vector2 = Vector2.ZERO var inventory: Array[Item] = [] var stats: Dictionary = {} func take_damage(amount: float, source: Node) -> void: health -= amount if health <= 0: die() func get_nearest_enemy() -> Enemy: # Return type declared ... ``` ## Coding Best Practices ### Node References ```gdscript # Use @onready for child references (resolved after _ready) @onready var sprite: Sprite2D = $Sprite2D @onready var collision: CollisionShape2D = $CollisionShape2D @onready var anim_player: AnimationPlayer = $AnimationPlayer # Use %UniqueNodeName for unique nodes in scene @onready var health_bar: ProgressBar = %HealthBar # For nodes in other parts of tree, use get_node or signals # NEVER use absolute paths like /root/Main/World/Player ``` ### Export Variables ```gdscript # Use @export for Inspector-configurable values @export var move_speed: float = 200.0 @export var jump_force: float = -400.0 @export var max_health: int = 100 # Export groups for organization @export_group("Movement") @export var acceleration: float = 500.0 @export var friction: float = 700.0 @export_group("Combat") @export var weapon: WeaponStats @export var armor_rating: int = 0 # Export enums @export_enum("Warrior", "Mage", "Rogue") var character_class: String # Export ranges @export_range(0, 100, 1) var volume: int = 80 ``` ### State Machine Pattern ```gdscript # state_machine.gd class_name StateMachine extends Node @export var initial_state: State var current_state: State func _ready() -> void: for child in get_children(): if child is State: child.state_machine = self current_state = initial_state current_state.enter() func _process(delta: float) -> void: current_state.update(delta) func _physics_process(delta: float) -> void: current_state.physics_update(delta) func transition_to(target_state: State) -> void: current_state.exit() current_state = target_state current_state.enter() # state.gd class_name State extends Node var state_machine: StateMachine func enter() -> void: pass func exit() -> void: pass func update(_delta: float) -> void: pass func physics_update(_delta: float) -> void: pass ``` ### Scene Organization Conventions ``` project/ ├── addons/ # Third-party plugins ├── assets/ # Raw art, audio, fonts │ ├── sprites/ │ ├── audio/ │ │ ├── music/ │ │ └── sfx/ │ ├── fonts/ │ └── shaders/ ├── scenes/ # .tscn scene files │ ├── main/ │ ├── levels/ │ ├── ui/ │ └── entities/ ├── scripts/ # .gd script files │ ├── autoloads/ │ ├── components/ │ ├── resources/ │ ├── state_machines/ │ └── utils/ ├── data/ # .tres resource files, JSON configs │ ├── items/ │ ├── enemies/ │ └── levels/ └── tests/ # GUT test files ├── unit/ ├── integration/ └── e2e/ ``` **Naming conventions:** - Files and folders: `snake_case` (e.g., `player_controller.gd`, `main_menu.tscn`) - Classes: `PascalCase` (e.g., `class_name PlayerController`) - Variables and functions: `snake_case` - Constants: `UPPER_SNAKE_CASE` - Signals: `snake_case` past tense (e.g., `health_changed`, `item_collected`) - Private members: prefix with `_` (e.g., `_internal_state`) ## Performance Optimization ### General Guidelines - **Use `_physics_process` for movement/physics**, `_process` for visuals and input - **Avoid `_process` on nodes that don't need per-frame updates** — use signals or timers instead - **Object pooling** for frequently spawned objects (bullets, particles, enemies) - **Use `call_deferred`** for operations that shouldn't happen mid-frame ### Rendering Performance - **Use visibility notifiers** (`VisibleOnScreenNotifier2D/3D`) to disable processing off-screen - **Limit draw calls** — use texture atlases, merge static geometry - **For 2D:** Use `CanvasGroup` for batch rendering, minimize `CanvasLayer` count - **For 3D:** Use LOD (MultiMeshInstance3D), occlusion culling, and the appropriate renderer: - **Forward+** — Best for most 3D games - **Mobile** — Lower overhead, fewer features - **Compatibility (OpenGL)** — Web export, older hardware ### Physics Performance - **Use `Area2D/3D`** for triggers instead of physics bodies when no collision response needed - **Simplify collision shapes** — circles/spheres over polygons where possible - **Collision layers and masks** — configure strictly, don't leave defaults - **For many static bodies** use `StaticBody` not `RigidBody` with `freeze` ### Memory and Loading - **Preload vs load:** `preload()` for small, always-needed assets; `load()` or `ResourceLoader.load_threaded_request()` for large or conditional assets - **Free nodes explicitly** when done: `queue_free()` for deferred, `free()` for immediate - **Use `ResourceLoader` threaded loading** for seamless scene transitions: ```gdscript func change_scene_async(path: String) -> void: ResourceLoader.load_threaded_request(path) # Show loading screen while ResourceLoader.load_threaded_get_status(path) != ResourceLoader.THREAD_LOAD_LOADED: await get_tree().process_frame var scene = ResourceLoader.load_threaded_get(path) get_tree().change_scene_to_packed(scene) ``` ### GDScript Performance Tips - **Use static typing everywhere** — typed code runs significantly faster - **Avoid `get_node()` in loops** — cache references with `@onready` - **Use `Array.map()`, `filter()`, `reduce()`** carefully — they create closures - **For hot paths**, consider moving to C# or GDExtension (C++) ## Popular Third-Party Plugins ### Essential Development | Plugin | Purpose | Source | |---|---|---| | **GUT** (Godot Unit Test) | Testing framework | AssetLib or git submodule | | **Phantom Camera** | Advanced camera system (2D/3D) | AssetLib | | **Limbo AI** | Behavior trees and state machines for AI | AssetLib or GitHub | | **Dialogic 2** | Dialogue and timeline system | AssetLib | | **Terrain3D** | Large-scale 3D terrain editing | GitHub releases | ### Gameplay Systems | Plugin | Purpose | Source | |---|---|---| | **GodotSteam** | Steamworks SDK integration | GitHub / GDExtension | | **Godot Jolt** | Jolt Physics replacement (faster, more stable 3D) | AssetLib | | **SmartShape2D** | Procedural 2D terrain/shapes | AssetLib | | **Maaack's Game Template** | Project template with menus, settings, save system | GitHub / AssetLib | | **Quest Manager** | Quest/objective tracking | AssetLib | ### Visual and Audio | Plugin | Purpose | Source | |---|---|---| | **Godot Shaders** | Curated shader collection | GitHub | | **Sprite3D Billboard** | 2D sprites in 3D world | AssetLib | | **SFXPlayer** | Advanced SFX management with pooling | AssetLib | ### Networking | Plugin | Purpose | Source | |---|---|---| | **Godot Steam Multiplayer Peer** | Steam networking transport | GitHub | | **Nakama** | Open-source game server (auth, matchmaking, realtime) | Docker + GDScript SDK | | **WebRTC** | Browser-compatible P2P networking | Built-in module | ### Export and Platform | Plugin | Purpose | Source | |---|---|---| | **Godot Google Play** | Google Play services integration | GitHub | | **Godot Admob** | Ad monetization for mobile | GitHub | ## AI-Assisted Development Godot has no official MCP server, but being open source it has spawned several community ones — all free, unlike Unity's official MCP, which sits behind a paid Unity AI subscription. An **MCP (Model Context Protocol)** server lets an AI assistant inspect and edit your project directly rather than guessing from chat descriptions. - **GoPeak** (`HaD0Yun/Gopeak-godot-mcp`) — MIT-licensed, actively maintained, ~95+ tools for Godot 4.x. Covers the full edit → run → inspect → fix loop: scene and GDScript editing, resource and shader work, LSP diagnostics, DAP breakpoints, live scene-tree inspection, screenshots, and input injection for testing. Run it with `npx -y gopeak`, then point your MCP client at the Godot executable and a tool profile (compact, full, or legacy). Other community options — `tugcantopaloglu/godot-mcp` (149 tools, runtime GDScript over TCP), plus `bradypp/godot-mcp` and `ee0pdt/Godot-MCP` — are catalogued with install steps in `engine-mcps.yaml`. MCP repos move fast, so confirm a project is still maintained before adopting it. ## Common Architectural Patterns ### Component Pattern (Composition over Inheritance) ```gdscript # Instead of deep inheritance, compose nodes: # Player (CharacterBody2D) # ├── HealthComponent # ├── HitboxComponent # ├── HurtboxComponent # ├── StateMachine # │ ├── IdleState # │ ├── RunState # │ └── JumpState # └── InteractionComponent # health_component.gd class_name HealthComponent extends Node signal health_changed(current: int, maximum: int) signal died @export var max_health: int = 100 var current_health: int func _ready() -> void: current_health = max_health func take_damage(amount: int) -> void: current_health = max(0, current_health - amount) health_changed.emit(current_health, max_health) if current_health == 0: died.emit() func heal(amount: int) -> void: current_health = min(max_health, current_health + amount) health_changed.emit(current_health, max_health) ``` ### Event Bus Pattern ```gdscript # autoloads/event_bus.gd extends Node # Game flow signal game_started signal game_paused signal game_over(score: int) # Gameplay signal enemy_defeated(enemy_type: String, position: Vector2) signal item_collected(item: Item) signal level_completed(level_id: String) # UI signal show_dialogue(text: String, speaker: String) signal notification_requested(message: String) ``` ### Scene Transition Manager ```gdscript # autoloads/scene_manager.gd extends Node signal scene_changed(scene_name: String) signal loading_progress(progress: float) var _current_scene: Node var _loading_screen: Control func change_scene(path: String, transition: String = "fade") -> void: _show_transition(transition) await _transition_midpoint _load_scene_async(path) ``` ## Godot 4 vs Godot 3 Key Differences If architecture docs or tutorials reference Godot 3, note these changes: | Godot 3 | Godot 4 | |---|---| | `yield()` | `await` | | `KinematicBody2D` | `CharacterBody2D` | | `move_and_slide(velocity)` returns velocity | Set `self.velocity = ...` then call `move_and_slide()` (no args, returns void) | | `export var` | `@export var` | | `onready var` | `@onready var` | | `connect("signal", obj, "method")` | `signal.connect(callable)` | | `tween = create_tween()` | Same, but `Tween` is refcounted | | GDNative (C++) | GDExtension (C++) | | `PoolStringArray` | `PackedStringArray` | ## Licensing and Cost Godot is licensed under the **MIT License** — fully free and open source with no royalties, no per-seat fees, and no revenue share. You can modify the engine source, use it commercially, and ship on any platform without paying Godot anything. **Cost implications:** - Engine: Free forever, no revenue thresholds - Console export: Requires third-party porting services (see below) — paid - Plugins: Most community plugins are MIT/free; some Asset Store items are paid - No vendor lock-in: you own your export templates and can fork the engine ## When NOT to Use Godot Godot may be the wrong choice when: - **AAA-quality 3D visuals are required** — Godot's 3D renderer is improving rapidly but does not yet match Unreal's Nanite/Lumen or Unity's HDRP for high-fidelity visuals - **Console launch is a hard requirement** — Console export requires contracting with specific porting companies (W4 Games, Lone Wolf Technology, Pineapple Works) under NDA, adds cost, and limits release timing. This is a significant business risk for console-first projects - **Large team with C# expertise and no GDScript willingness** — While Godot supports C#, the ecosystem (plugins, tutorials, community) is overwhelmingly GDScript-oriented - **Proven production pipeline for 100+ person teams** — Godot's tooling for large-scale team workflows (asset pipelines, custom editors, build systems) is less mature than Unity or Unreal - **VR/XR is the primary platform** — Godot has OpenXR support but the ecosystem (interaction toolkits, platform SDKs) is substantially less mature than Unity XR Toolkit or Unreal OpenXR ## Console Export Reality Console export for Godot is substantially more complex than desktop/mobile/web: - **Not available through the open-source engine** — Console SDKs (Nintendo, Sony, PlayStation) are under NDA and cannot be distributed with Godot - **Requires a third-party porting company** with console SDK access: - **W4 Games** — Founded by Godot core contributors, offers official console porting - **Lone Wolf Technology** — Provides Godot console export solutions - **Pineapple Works** — Another porting option - **Cost and timeline:** Porting contracts add development cost and can introduce delays. Budget this early in planning - **Developer program membership required** — You still need Nintendo Developer, PlayStation Partners, or Xbox ID@Xbox registration independently - **Testing and certification** — Console certification requirements add QA overhead regardless of engine **If console is a target:** Factor porting cost and timeline into the architecture decision. Consider whether Unity or Unreal's built-in console export support would reduce risk for your project. ## Architecture Decision Checklist When choosing Godot for a project, verify: - [ ] Target platforms supported by Godot export (web, mobile, desktop, consoles via third-party) - [ ] GDScript vs C# decision based on team skills - [ ] 2D vs 3D renderer selection (Forward+, Mobile, Compatibility) - [ ] Physics engine: default Godot Physics or Jolt plugin for 3D - [ ] Networking approach if multiplayer (built-in MultiplayerAPI, Steam, Nakama) - [ ] Audio middleware needed or engine-native sufficient - [ ] Key plugins identified and verified as Godot 4 compatible - [ ] Console export strategy evaluated (see Console Export Reality section — requires porting company, adds cost and timeline)