Open-Source Wikis

/

Godot

/

How to contribute

/

Patterns and conventions

godotengine/godot

Patterns and conventions

This page documents the conventions Godot follows in C++ code. The official Code style guidelines are the source of truth; this page summarizes what you need to know to read and write idiomatic Godot code.

C++ standard and style

  • C++20 is the language target. Older code may be C++17-flavoured; new code uses C++20 features where helpful (concepts, designated initializers, std::span-equivalent helpers).
  • Tabs for indentation, width 4. The clang-format config (.clang-format) enforces this.
  • UpperCamelCase for types, lower_snake_case for functions and variables, UPPER_SNAKE_CASE for constants and enum members, p_ prefix for parameters, r_ prefix for return-by-reference output parameters.
  • Member functions in C++ headers are listed in this conventional order: public methods → public statics → protected → private members. Initialize members at declaration where possible (the in-class member initializer).
  • Files always start with the project's MIT license header (a comment block of fixed shape).
  • Header guards use #pragma once.

Example:

class MyClass : public RefCounted {
    GDCLASS(MyClass, RefCounted);

public:
    void compute(int p_value);
    Vector<String> get_results(int p_index, int *r_count) const;

protected:
    static void _bind_methods();

private:
    int counter = 0;
    HashMap<StringName, int> map;
};

Object pattern

Every native class derived from Object:

  1. Uses the GDCLASS(MyClass, ParentClass) macro at the top of the class body. This declares the metadata ClassDB reads.
  2. Provides a static void _bind_methods() that calls ClassDB::bind_method, ADD_PROPERTY, ADD_SIGNAL, BIND_ENUM_CONSTANT etc.
  3. May provide _get, _set, _get_property_list, _property_can_revert, _property_get_revert as virtual hooks for dynamic properties.
  4. Uses _notification(int p_what) rather than overriding constructor/destructor logic where possible — NOTIFICATION_ENTER_TREE, NOTIFICATION_READY, NOTIFICATION_EXIT_TREE are the lifecycle hooks.

Method binding example:

void MyClass::_bind_methods() {
    ClassDB::bind_method(D_METHOD("compute", "value"), &MyClass::compute);
    ClassDB::bind_method(D_METHOD("get_results", "index"), &MyClass::get_results);
    ADD_PROPERTY(PropertyInfo(Variant::INT, "counter"), "set_counter", "get_counter");
    ADD_SIGNAL(MethodInfo("computed", PropertyInfo(Variant::INT, "value")));
}

D_METHOD("name", "arg1", "arg2") declares argument names for tooling.

Memory management

  • Use memnew(T) / memfree(p) for raw allocations (these go through Godot's allocator, which adds tracking in dev builds).
  • Reference-counted resources use Ref<T> (where T extends RefCounted). Never store a raw pointer to a Resource long-term.
  • Node is owned by its parent Node. Use queue_free() to delete a Node safely; don't memdelete it directly while it is in the tree.
  • Containers (Vector<T>, HashMap<K,V>, RBMap<K,V>, LocalVector<T>) live in core/templates/. Prefer them over the C++ STL — they have tuned ABIs and predictable memory characteristics.

Error handling

There are no exceptions in Godot. Errors are reported via macros from core/error/error_macros.h:

  • ERR_FAIL_COND(cond) — print an error and return; if cond is true.
  • ERR_FAIL_COND_V(cond, ret) — same, but return ret.
  • ERR_FAIL_COND_MSG(cond, "message") — with a custom error message.
  • ERR_FAIL_INDEX(idx, size) — bound check with auto-message.
  • ERR_PRINT("message") — print without returning.
  • WARN_PRINT_ONCE("message") — single-shot warning (for spammy hot paths).
  • CRASH_NOW_MSG("message") — abort the engine; only when something is structurally broken.

Error is a typed enum (OK, FAILED, ERR_FILE_NOT_FOUND, …) defined in core/error/error_list.h. Functions that can fail return Error.

Pointers and references

  • Pass small POD types by value, larger types by const T &.
  • Use raw pointers (Object *) where ownership is shared or unspecified; use Ref<T> for resources.
  • Output parameters use the r_ prefix and are passed by pointer (int *r_count) so callers can see they may be written.

Threading

  • Many APIs are thread-safe via internal locks (servers, message queue, resource loader). Most node code is not — assume Node operations must happen on the main thread unless documented otherwise.
  • Synchronization primitives live in core/os/: Mutex, BinaryMutex, RWLock, Semaphore, SafeNumeric<T>, SafeFlag.
  • WorkerThreadPool (core/object/worker_thread_pool.h) is the standard way to run parallel CPU work; don't spawn ad-hoc threads.
  • MessageQueue (core/object/message_queue.h) is what call_deferred and set_deferred use. Calls flush between frame phases.

Includes and headers

  • Project headers use double quotes and full paths from the repo root: #include "core/object/object.h".
  • System headers use angle brackets: #include <atomic>.
  • Includes are sorted in groups by clang-format's IncludeBlocks: Regroup rule. The order is: project (core/, drivers/, editor/, …) → modules/ & platform/<thirdparty/...> → platform headers → standard library → others.
  • .compat.inc files are reserved for compatibility shims and have priority 0 so they sort to the top.

Naming for Variant-exposed APIs

Methods and properties exposed to ClassDB follow snake_case as the source of truth. Bindings in C# camelCase'ize them; in GDScript they stay snake_case. Property and signal names appear in scripts, the inspector, and the docs — pick names users will read.

Documentation

Public scripting APIs need XML documentation under doc/classes/<ClassName>.xml. The expected sections (description, methods, signals, properties, constants, theme items) match what the inspector and docs site render. A pre-commit hook flags missing entries when you add a new method/property/signal.

Tooling expectations

  • Run clang-format before committing.
  • Run clang-tidy for new C++ files (.clang-tidy in the repo root has the rule set).
  • Run ruff for any Python file under pyproject.toml's scope (SConstruct, methods.py, build scripts, doc tools).
  • Run mypy for typed Python.
  • See Tooling for invocation specifics.

Compatibility hooks

Adding a new method or property is straightforward, but changing or removing existing scripting APIs requires a compatibility shim so existing projects keep working. The pattern lives in *.compat.inc files alongside each header that has had API changes. The new method is bound normally; the old shape is re-bound under a hash that preserves backwards compatibility. See core/extension/gdextension_compat_hashes.h for the centralized hash registry, and look at any .compat.inc for an example of how to declare the shim.

When in doubt

Read the file you are modifying and a sibling file. Godot is a large codebase but it is internally consistent — the local conventions of a directory are usually a good guide for any change made there.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Godot wiki | Factory