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.