@thedivergentai/godot-gdscript-mastery
Expert GDScript best practices including static typing (var x: int, func returns void), signal architecture (signal up call down), unique node access (%NodeName, @onready), script structure (extends, class_name, signals, exports, methods), and performance patterns (dict.get with defaults, avoid get_node in loops). Use for code review, refactoring, or establishing project standards. Trigger keywords: static_typing, signal_architecture, unique_nodes, @onready, class_name, signal_up_call_down, gdscript_style_guide.
| name | godot-gdscript-mastery |
| description | Expert GDScript landmine guidance: static typing opcodes, signal-up/call-down, %UniqueName/@onready lifecycle, Callable bind/unbind, await sequences, typed collections, and safe Dictionary iteration. Use for code review, refactoring hot paths, or project standards. Trigger keywords: static_typing, signal_architecture, unique_nodes, @onready, class_name, signal_up_call_down, Callable.bind, typed_collections, await_sequence. |
GDScript Mastery
Expert guidance for writing performant, maintainable GDScript — Godot-landmine decision trees, not a style-guide reprint.
Do NOT Load
- Do not load this skill for general prose style or Godot engine version upgrades (3→4 / 4.x hops) — those live in godot-version-migration (plus official upgrading guides via that hub).
- Do not preload every script below; open only the MANDATORY pointer for the Core Directive you are implementing.
- Do not treat EditorScript utilities (
type_checker,performance_analyzer,signal_architecture_validator) as runtime gameplay code.
NEVER Do in GDScript
- NEVER use
@onreadyand@exporton the same variable — Initialization order will cause@onreadyto overwrite the Inspector value. - NEVER modify a Dictionary's size while iterating it — Use
dict.keys().duplicate()or iterate a clone to safely erase elements. - NEVER use string-based
connect("signal", ...)— Always use the Signal object syntax (button.pressed.connect(...)) for compile-time safety. - NEVER attempt to override non-virtual native engine methods — Overriding
queue_free()orget_class()is unsupported and will be ignored by engine callbacks. - NEVER use dynamic
get_node()or$inside_process()— Fetching paths every frame stalls the CPU. Cache and use@onready. - NEVER use
Parent.method()calls — Violates "Signal Up, Call Down". Use signals to communicate with parents. - NEVER use
isfollowed by a hard cast — If the type check passes but the object changes, it crashes. Useasand check for null. - NEVER use
print()for production debugging — Usepush_error(),push_warning(), or breakpoints. - NEVER pre-load huge resources in
_ready()— UseResourceLoader.load_threaded_request()for async loading. - NEVER use global variables in Autoloads when
static varis sufficient — Static variables offer better encapsulation.
Core Directives (decision trees + MANDATORY scripts)
1. Strong Typing & Performance
| Landmine | Decision |
|---|---|
Hot path still Variant? |
Annotate vars/returns; prefer typed collections |
Generic math in _process? |
Use typed helpers (absf, ceili, clampf) |
| Green safe-lines missing? | Fix inference with := or explicit types |
MANDATORY: typed_collections_mastery.gd, array_preallocation_perf.gd, type_checker.gd (EditorScript audit).
2. Signal Architecture
| Landmine | Decision |
|---|---|
| Child needs parent reaction? | Emit signal up — never call parent methods |
| Cross-script payload unsafe? | Typed signal name(arg: Type) |
| Connect visibility? | Prefer _ready() connects over invisible editor-only wiring |
MANDATORY: typed_signal_definitions.gd, signal_architecture_validator.gd.
3. Node Access & Lifecycle Safety
| Landmine | Decision |
|---|---|
| Need child nodes? | @onready / %UniqueName — never in _init() |
| Scene-instanced node with ctor args? | Use @export injection — _init(args) breaks PackedScene.instantiate() |
| Path lookup every frame? | Cache once; never $ / get_node in _process |
MANDATORY: safe_type_casting.gd.
4. Callable & Signal (First-Class)
| Landmine | Decision |
|---|---|
| Extra context on callback? | Callable.bind(...) |
| Discard unused signal args? | Callable.unbind(n) |
| One-off timeout logic? | Inline lambda OK; keep refs if create_callback-style longevity matters |
MANDATORY: callable_binding_context.gd, unbind_signal_args.gd, advanced_lambdas.gd, functional_lambda_logic.gd.
5. Async, Statics & Safe Collections
| Landmine | Decision |
|---|---|
| Sequence timers without threads? | await chains — see await manager |
| Global state without Autoload bloat? | static var (+ nullify large statics when done) |
| Erase while iterating Dictionary? | Clone keys first |
MANDATORY: await_sequence_manager.gd, static_var_singleton_alt.gd, dictionary_safe_iteration.gd, performance_analyzer.gd (EditorScript).
Script Catalog (all files)
| Script | When to open |
|---|---|
| typed_collections_mastery.gd | Typed Array/Dictionary opcodes |
| functional_lambda_logic.gd | reduce / all / any |
| advanced_lambdas.gd | Higher-order Callables |
| safe_type_casting.gd | as + null checks |
| typed_signal_definitions.gd | Typed signal boundaries |
| callable_binding_context.gd | bind() context injection |
| unbind_signal_args.gd | unbind() arity trim |
| await_sequence_manager.gd | Non-blocking await flows |
| array_preallocation_perf.gd | resize() pre-alloc |
| static_var_singleton_alt.gd | Lightweight global state |
| dictionary_safe_iteration.gd | Safe erase-while-iterate |
| type_checker.gd | EditorScript typing audit |
| performance_analyzer.gd | EditorScript hot-path scan |
| signal_architecture_validator.gd | EditorScript signal-up checks |
Quick Landmines
- Prefer
dict.get("key", default)overdict["key"]when presence is uncertain. - Toggle Access as Scene Unique Name and read via
%Namefor critical UI/nodes. - Script layout order:
extends→class_name→ signals/enums/consts → exports/onready → lifecycle → public →_private.
Expert knowledge (on demand)
LLM-ignorance rule: If a general agent would not know it before reading, load the reference — never delete expert deltas.
- gdscript-core-directives.md — restored baseline pedagogy (architecture, WHY, implementation depth)
Reference
Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
Official Documentation
- GDScript basics — Language core for typed vars/funcs,
signaldeclarations,await, and first-class Callables this skill standardizes. - GDScript style guide — Canonical script order (
extends→class_name→ signals → exports → lifecycle → methods) used in reviews and refactoring. - Static typing in GDScript — Why typed Arrays/Dictionaries and return types unlock optimized opcodes and editor safe-lines.
- GDScript: An introduction to dynamic languages — Lambdas, higher-order Callables, and advanced patterns behind filter/map/reduce helpers.
- GDScript warning system — Turn unsafe casts, unused signals, and untyped hot paths into CI-visible warnings.
- Logic preferences — When to prefer declarative signals vs imperative calls so scripts stay decoupled.
- Scene organization — Official “signal up, call down” ownership rules this skill enforces.
- Using signals — Connect/emit model and why string-based connect-by-name is avoided.
- Callable —
bind()/unbind()APIs for injecting or discarding callback arguments without wrapper nodes. - Array — Typed arrays,
resize(), and functional methods (filter/map/reduce/all/any) used in the scripts. - Dictionary — Safe
.get()defaults and why size must not change while iterating keys. - CPU optimization — Cache
@onready/%UniqueNameinstead ofget_node/$inside_processloops.
Related Skills
Prerequisites
- godot-project-foundations — Project layout, Autoload registration, and scene ownership conventions that typed GDScript scripts plug into.
- godot-composition — Component boundaries clarify which scripts own signals vs call-down APIs before style enforcement.
Complements
- godot-version-migration — Engine version upgrades (3→4 language breaks, 4.x hops); this skill stays on current GDScript 2.0 idioms.
- godot-signal-architecture — Deepens connect flags, buses, and sequencers after this skill’s typed signal/Callable basics.
- godot-autoload-architecture — Contrasts heavy Autoloads with the
static varsingleton alternatives shown here. - godot-resource-data-patterns — Prefer Resources for shared config; keep GDScript modules thin and typed around Resource payloads.
- godot-scene-management —
@onready, unique names, and await sequences must stay valid across scene swaps and loaders. - godot-testing-patterns — Typed signals and Callables make
watch_signals/ spies reliable in unit tests. - godot-debugging-profiling — Pair style/perf smells from this skill with profiler and custom monitors when hot paths remain slow.
- godot-state-machine-advanced — FSM enter/exit handlers should follow the same typed-signal and await sequencing conventions.
Downstream / consumers
- godot-performance-optimization — Escalate when typed GDScript alone is not enough; servers, pooling, and broader CPU/GPU tactics live there.
- godot-auditor — Project-wide audits consume the typing, signal-up, and hot-path rules codified in this skill.
- godot-ability-system — Abilities need typed signal payloads and await-safe cooldowns grounded in these language patterns.
- godot-combat-system — Damage/death fan-out depends on typed emits and safe casts taught here.
Master
- godot-master — Library router and mirrored module entry; open when discovering which Domain Skill owns a cross-cutting scripting concern.
Loading...
Select a file to preview
Analyzing security...
Checking scan reports and verification data.
Bill of Materials
Everything this skill can do — files, network, commands, and more.