# Godot Development Guidelines **Document Version:** v2 > **Note on Versioning:** > - This document version is independent — reused across Godot projects > - **Project version** source of truth: `project.godot` (`application/config/version`) mirrored in an autoload `Constants` script > - Version propagates: `project.godot` → `Constants.gd` → code > - `CHANGELOG.md` uses the project version ## Related Documents - **README.md** — Project overview, gameplay description, build/run instructions - **AGENTS.md** — Rules for AI assistants - **PROJECT.md** — Project goals and current state - **CHANGELOG.md** — Version history ### Documentation Organization All detailed documentation of features and systems belongs in the `docs/` folder, not in the project root. The root directory contains only the core documents: `README.md`, `AGENTS.md`, `DESIGN_DOCUMENT_GODOT.md`, `PROJECT.md`, `CHANGELOG.md`. --- ## 1. Code Style - **GDScript style guide** (official Godot conventions), 100-character soft line limit - **Tabs** for indentation (Godot default, enforced by `.editorconfig`) - **snake_case** functions/variables/signals, **PascalCase** classes/nodes/scenes, **SCREAMING_SNAKE_CASE** constants, **_leading_underscore** for private members - **Static typing required** on all variables, parameters, and return types (`var speed: float`, `func move(delta: float) -> void`) - **Declaration order** inside a script: 1. `class_name` / `extends` 2. `## docstring` 3. signals 4. enums, constants 5. `@export` variables 6. public variables 7. private variables (`_`) 8. `@onready` variables 9. `_init`, `_ready`, `_process`, `_physics_process` virtuals 10. public methods 11. private methods (`_`) 12. signal callbacks (`_on_*`) --- ## 2. SOLID Principles (applied to nodes & scenes) - **SRP** — One scene/script = one responsibility (a node does one job) - **OCP** — Extend behaviour by composing child nodes or new scenes, not by editing shared base scripts - **LSP** — A scene inheriting another must be usable wherever the parent is - **ISP** — Prefer small, focused signals and interfaces over god-objects - **DIP** — Depend on abstractions: reference behaviours via exported node paths / resources, not hard-coded global lookups --- ## 3. Dependency Injection Inject dependencies through `@export` variables or `_init` parameters, set in the editor or by the spawning parent. Avoid reaching across the tree with `get_node("/root/...")` for collaborators. Use autoload singletons **only** for genuinely global services (audio bus, save system, game state). --- ## 4. Composition Over Inheritance Prefer **node composition** and reusable **scenes** (e.g. a `Hitbox`, `Hurtbox`, `StateMachine` scene attached as a child) over deep script inheritance. Use `class_name` for shared base behaviour only when an "is-a" relationship is real. --- ## 5. Data Structures and Resources - Use **`Resource`** (`class_name`, `extends Resource`) for designer-editable, serializable data (enemy stats, level definitions, weapon configs) — saved as `.tres` - Use plain typed `Dictionary`/`Array` or small data classes for transient in-memory structures - Validate `Resource` fields with `@export_range`, setters, and `_get_configuration_warnings()` where appropriate --- ## 6. Logging and Console Output ### Logging Use a dedicated `Log` autoload that wraps `print`/`push_warning`/`push_error` with levels and timestamps. Never log secrets, passwords, tokens, or API keys. #### Log sinks | Sink | Level | Format | |------|-------|--------| | File `user://logs/{AppName}_{time}.log` | `DEBUG` | full (timestamp + level + message) | | Editor Output / stdout | `INFO` | full | The file sink retains **max 10 log files**. Each run creates a new file via the timestamp in the filename. The `DEBUG` sink is only active when `Constants.DEFAULT_DEBUG` is `true` (controlled by an `ENV_DEBUG` value read at startup, e.g. from a `user://settings.cfg` or an export feature tag). Additional sinks (e.g. an in-game debug overlay / console panel) may be added per project. #### Log levels | Level | When to use | |-------|-------------| | `DEBUG` | Per-frame / per-entity detail: state transitions, collision events, per-tile operations | | `INFO` | User-visible milestones: level loaded, game saved, scene changed | | `WARNING` | Recoverable issues: missing optional resource, fallback asset used | | `ERROR` | Failures the player/dev must know about: failed save, missing required scene/resource | ### Console output — `print()` `print()` is **allowed** for quick editor-time inspection during development. It is **not** a substitute for the `Log` autoload and must not remain in shipped gameplay code for event tracking. Use `push_warning`/`push_error` so messages appear in the editor Debugger. --- ## 7. Settings and Secrets - Store user/runtime config in `user://settings.cfg` via `ConfigFile` - Toggle debug behaviour with an `ENV_DEBUG`-style flag read at startup into `Constants.DEFAULT_DEBUG` - Never commit any secrets; keep them out of `project.godot` and committed `.tres`/`.cfg` files --- ## 8. Error Handling Fail fast in development: use `assert()` for invariants that must hold (these are stripped in release builds). For recoverable runtime conditions, check return values and `is_instance_valid()` before use, and surface problems via `push_error`/`Log.error` rather than silently continuing. --- ## 9. Testing - Use **GUT** (Godot Unit Test) for unit and integration tests - Tests live in `tests/`, files named `test_.gd`, methods named `test__` - Arrange-Act-Assert pattern - Keep logic testable: put game rules in plain scripts/`Resource`s that can be exercised without a running scene tree where possible --- ## 10. Tooling | Tool | Purpose | |------|---------| | **gdformat** (gdtoolkit) | Formatting | | **gdlint** (gdtoolkit) | Linting / style checks | | **GUT** | Testing | Run before every commit: ```bash gdformat . gdlint . godot --headless -s addons/gut/gut_cmdln.gd -gdir=res://tests -gexit ``` --- ## 11. Dependencies and Addons - Install editor plugins/addons under `addons/` (via the Asset Library or git submodule) - Enable plugins through Project Settings → Plugins (recorded in `project.godot`) - Keep `addons/` committed so the project opens cleanly on any machine - Document non-trivial addon requirements in `README.md` --- ## 12. Project Structure ``` project/ ├── project.godot # Engine config and project settings ├── icon.svg # Project icon ├── autoload/ # Global singletons (Constants, Log, GameState, Audio) ├── scenes/ # Game scenes (.tscn), grouped by domain │ ├── actors/ # player, enemies, NPCs │ ├── levels/ # level/world scenes │ └── ui/ # menus, HUD ├── scripts/ # Shared scripts not bound to a single scene ├── resources/ # .tres data resources (stats, configs) ├── assets/ # Art, audio, fonts (raw + imported) ├── addons/ # Editor plugins / third-party addons ├── tests/ # GUT tests └── docs/ # Detailed documentation ``` Scenes and their attached scripts live together; co-locate `Player.tscn` and `player.gd` under `scenes/actors/`. --- ## 13. Distribution and Deployment When the game is distributed as a standalone build: - Configure export presets in `export_presets.cfg` (one preset per target platform) - Build with the headless editor: ```bash godot --headless --export-release "Windows Desktop" build/Game.exe ``` - Compiled builds are stored in `build/` (or `dist/`). Decide per project whether builds are committed — for small internal distribution the repository may serve as the channel; otherwise keep `build/` in `.gitignore` - Strip debug-only nodes and disable `Constants.DEFAULT_DEBUG` in release presets > This section applies only to projects that produce shippable builds. --- ## 14. Versioning - Follow **semantic versioning**: `MAJOR.MINOR.PATCH` - Version is defined in `project.godot` (`application/config/version`) and mirrored in the `Constants` autoload - Always ask before bumping the version — never increment automatically - Update `CHANGELOG.md` before bumping the version --- ## 15. Documentation and Task Management - Keep `PROJECT.md` and `CHANGELOG.md` up to date when making changes - Document architectural changes in this file or in `docs/` ### Task notation Tasks are written as single-line comments directly in code, using the **Todo Tree** tags defined in `AGENTS.md` (`TODO`, `FIXME`, `BUG`, `HACK`, `NOTE`). `PROJECT.md` carries only cross-cutting tasks that have no single place in the code. ```gdscript # TODO: move this into a reusable StateMachine scene # FIXME: player clips through the floor at high speed # BUG: hitbox stays active one frame too long # HACK: temporary yield until the animation signal is wired up # NOTE: must run in _physics_process, not _process ``` No other task format is used — **no checkboxes, no numbered lists in documentation**. If a tag already exists at a specific location in code, do not repeat it in `PROJECT.md`. --- ## 16. Godot-Specific Conventions - **Signals over polling** — communicate events upward via signals; call down via direct method calls (`_on_*` callbacks for receiving) - **`@onready` for node references** — cache child node references instead of repeated `get_node` - **`_process` vs `_physics_process`** — gameplay/physics in `_physics_process(delta)`, visual-only/UI in `_process(delta)` - **Groups** for broadcast queries (`add_to_group`, `get_tree().call_group`) instead of manual node lists - **`queue_free()`** to remove nodes; never `free()` a node mid-signal - **Scene instancing** via `PackedScene.instantiate()`; keep scenes self-contained and parametrized through `@export` - **No hard-coded paths to assets in code** — load via `preload`/`@export` of the resource