L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

gdscript-patterns

gdscript-patterns

Use when writing GDScript — static typing, await/coroutines, lambdas, match patterns, export annotations, inner classes, and common idioms

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: gdscript-patterns description: Use when writing GDScript — static typing, await/coroutines, lambdas, match patterns, export annotations, inner classes, and common idioms

GDScript Patterns in Godot 4.3+

All examples target Godot 4.3+ with no deprecated APIs.

Related skills: gdscript-advanced for production-grade depth (performance idioms, metaprogramming, @tool lifecycle, profiler-driven idioms), godot-code-review for style rules and anti-patterns, csharp-godot for GDScript-to-C# translation, state-machine for state patterns, event-bus for signal architecture.

Note: This skill is GDScript-specific by design. For C# patterns, see csharp-godot and csharp-signals.


1. Static Typing

Type Hints

Always add type hints — they catch bugs at parse time, improve autocomplete, and boost performance.

# Variables
var health: int = 100
var speed: float = 200.0
var player_name: String = "Hero"
var direction: Vector2 = Vector2.ZERO

# Constants
const MAX_HEALTH: int = 100
const GRAVITY: float = 980.0

# Functions — parameters and return type
func take_damage(amount: int) -> void:
    health -= amount

func get_direction() -> Vector2:
    return Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")

# Inferred typing with :=
var pos := Vector2(100, 200)     # inferred as Vector2
var items := []                  # inferred as Array (untyped)
var count := 0                   # inferred as int

Typed Collections

# Typed arrays — only accepts the specified type
var enemies: Array[Enemy] = []
var scores: Array[int] = [10, 20, 30]
var names: Array[String] = ["Alice", "Bob"]

# Typed dictionaries (Godot 4.4+)
var inventory: Dictionary[String, int] = {"sword": 1, "potion": 5}

# Typed loop variable
for enemy: Enemy in enemies:
    enemy.take_damage(10)

# Typed array methods work with type safety
var filtered: Array[Enemy] = enemies.filter(func(e: Enemy) -> bool: return e.health > 0)

Casting with as and is

# 'is' — type check (returns bool)
func _on_body_entered(body: Node2D) -> void:
    if body is Player:
        var player: Player = body as Player
        player.take_damage(10)

# 'as' — cast (returns null on failure, no error)
var sprite := get_node("Sprite") as Sprite2D
if sprite:
    sprite.modulate = Color.RED

# Prefer 'is' check + cast over bare 'as' to avoid null surprises

Enabling Strict Typing Warnings

In Project > Project Settings > Debug > GDScript:

WarningEffect
UNTYPED_DECLARATIONWarns on any untyped variable/parameter
INFERRED_DECLARATIONWarns on := (prefers explicit types)
UNSAFE_CASTWarns on unsafe as casts
UNSAFE_CALL_ARGUMENTWarns when passing wrong type to a function

Set warnings to Error for strict enforcement in team projects.

Typed Return Inheritance in Overrides

⚠️ Changed in Godot 4.7: Methods that override a method with a typed return now inherit the return type, so an override without an explicit return statement becomes an error. Add return null (or a typed return value) at the end of the override. See the 4.7 migration guide.

class Enemy:
    var weapon: Node
    func get_weapon() -> Node:
        return weapon

class UnarmedEnemy extends Enemy:
    func get_weapon():  # 4.7+: inherits -> Node from Enemy
        return null     # explicit return now required — omitting it is an error

2. Await & Coroutines

Awaiting Signals

await pauses the function until a signal fires, then resumes. The function becomes a coroutine.

func death_sequence() -> void:
    $AnimationPlayer.play("death")
    await $AnimationPlayer.animation_finished  # pauses here

    $Sprite2D.visible = false
    await get_tree().create_timer(1.0).timeout  # wait 1 second

    queue_free()

Awaiting with Return Values

# Signal that passes data
signal dialogue_choice_made(choice: int)

func show_dialogue(options: Array[String]) -> int:
    # ... display UI ...
    var choice: int = await dialogue_choice_made
    return choice

# Caller:
func _on_npc_interact() -> void:
    var result := await show_dialogue(["Yes", "No"])
    if result == 0:
        print("Player said yes")

Timer Patterns

# One-shot delay
await get_tree().create_timer(0.5).timeout

# Repeating with await (simple but blocks the function)
for i in 5:
    do_something()
    await get_tree().create_timer(0.2).timeout

# Non-blocking timer — use SceneTreeTimer or Tween instead
get_tree().create_timer(2.0).timeout.connect(_on_delayed_action)

Coroutine Safety

# DANGER: `target` may be freed while this coroutine waits
func unsafe_coroutine(target: Node2D) -> void:
    await get_tree().create_timer(5.0).timeout
    target.position = Vector2.ZERO  # error: previously freed instance

# SAFE: re-check other objects after every await
func safe_coroutine(target: Node2D) -> void:
    await get_tree().create_timer(5.0).timeout
    if not is_instance_valid(target):
        return
    target.position = Vector2.ZERO

A freed node's own coroutine never resumes, so is_instance_valid(self) after await guards nothing.


3. Lambda Functions

Lambdas are inline anonymous functions for callbacks, sorting, and filtering, called with .call(). The reference covers single- and multi-line syntax, inline signal connections including CONNECT_ONE_SHOT, Array methods (filter, map, reduce, any, all, sort_custom), and closures, where locals are captured by value.

See references/lambda-functions.md for the syntax, signal, Array method, and closure examples.


4. Match / Pattern Matching

GDScript's match is like switch but with pattern support.

Basic Match

match state:
    State.IDLE:
        play_idle()
    State.RUNNING:
        play_run()
    State.JUMPING, State.FALLING:  # multiple patterns
        play_air()
    _:  # default (wildcard)
        push_warning("Unknown state: %s" % state)

Pattern Types

# Literal patterns
match value:
    42:
        print("The answer")
    "hello":
        print("Greeting")
    true:
        print("Boolean true")

# Binding pattern — captures value into a variable
match command:
    ["move", var direction]:
        move(direction)
    ["attack", var target, var damage]:
        attack(target, damage)

# Array pattern
match input:
    [1, 2, 3]:
        print("Exact match")
    [1, ..]:
        print("Starts with 1")
    [var first, _, var last]:
        print("First: %s, Last: %s" % [first, last])

# Dictionary pattern
match event:
    {"type": "damage", "amount": var amt}:
        take_damage(amt)
    {"type": "heal", "amount": var amt}:
        heal(amt)

# Nested condition inside a branch
match enemy_type:
    "boss":
        if health < 50:
            enter_rage_mode()
        else:
            normal_attack()

5. Export Annotations

@export exposes a variable to the Inspector. Hint variants (@export_range, @export_enum, @export_file) constrain editor input. Use @export_group and @export_subgroup to organize. Node and Resource exports use NodePath / typed Resource references.

See references/export-annotations.md for the full export annotation catalog (basic exports, range/hint variants, groups, node and Resource exports).


6. Inner Classes & class_name

class_name

Register a script as a global class name — available everywhere without preload.

# item_data.gd
class_name ItemData
extends Resource

@export var name: String
@export var icon: Texture2D
@export var value: int

# Now usable anywhere:
# var item: ItemData = ItemData.new()
# var items: Array[ItemData] = []

Inner Classes

# Define a class inside another script
class HitResult:
    var damage: int
    var critical: bool
    var knockback: Vector2

    func _init(dmg: int, crit: bool, kb: Vector2 = Vector2.ZERO) -> void:
        damage = dmg
        critical = crit
        knockback = kb

# Usage
func calculate_hit() -> HitResult:
    var crit := randf() < 0.2
    var dmg := 10 * (2 if crit else 1)
    return HitResult.new(dmg, crit, Vector2.RIGHT * 50)

7. super() in Virtual Methods

When you override a virtual method that the engine calls (_ready, _process, _input, etc.) and your parent class also implements it, call super() to chain the parent's behavior. Forgetting to call super._ready() is the most common cause of "my base class init didn't run" bugs.

See references/super-in-virtual-methods.md for the full pattern (problem / fix), the C# base.X() equivalent, and a catalog of bugs from missing super calls.


8. Common Idioms

The recurring small patterns: ternary expressions (value if cond else other), printf-style string formatting ("%s %d" % [a, b]), null/empty checks (is_instance_valid vs != null, empty Array/String checks), Dictionary access (get(key, default)), Array operations (Array.has, Array.find, Array.has_all), setget via set and get accessors.

See references/common-idioms.md for full code examples of each idiom.


9. Annotations Reference

AnnotationPurpose
@exportExpose variable in Inspector
@export_rangeNumeric with slider
@export_enumDropdown from string list
@export_fileFile path picker
@export_dirDirectory picker
@export_multilineMulti-line text box
@export_groupGroup heading in Inspector
@export_subgroupSubgroup heading
@export_categoryCategory divider
@onreadyInitialize when node enters tree, just before _ready() body runs
@toolRun script in editor
@iconCustom icon for the script
@warning_ignoreSuppress specific warning on next line
@static_unloadAllow static variables to be freed

10. Common Pitfalls

SymptomCauseFix
as cast silently returns nullType mismatch — as doesn't errorUse is check first, then cast
Await never resumesSignal never emitted, or this node was freed (coroutine dropped)Check the condition before awaiting; race slow signals against a timer
Lambda's change to a variable is lostLocals are captured by valueKeep shared state in a member, Array or Dictionary
UNTYPED_DECLARATION warnings floodWarning enabled but codebase isn't typedType incrementally; use @warning_ignore for legacy code
Typed array rejects valid itemsItem type doesn't match exactlyEnsure items match the declared type (no implicit upcasting)
@onready is nullAccessed before _ready() runsNever access @onready vars in _init() or variable declarations
Match doesn't enter any branchNo matching pattern and no _: wildcardAlways add _: default branch
class_name conflictTwo scripts with same class_nameUse unique names; check for duplicates in Project
Export group applies to wrong varsGroup scope continues until next groupAdd a new @export_group("") to end the group scope
Parent _ready() logic doesn't run in childMissing super() call in child's _ready()Add super() as first line; see Section 7
type_exists() flagged as deprecatedDeprecated in Godot 4.7Use ClassDB.class_exists() instead

⚠️ Changed in Godot 4.7: The global type_exists() function is deprecated — replace type_exists("Sprite2D") with ClassDB.class_exists("Sprite2D"). See GH-116899.


11. Variadic Functions (Godot 4.5+)

Godot 4.5 added trailing-argument arrays via ...args. The args are collected into an Array. Useful for printf-style helpers and flexible APIs without overloads.

See references/variadic-functions.md for the full syntax, common patterns, and notes on when to prefer overloads.


12. Abstract Classes and Methods (Godot 4.5+)

The @abstract annotation prevents direct instantiation of a class and forces subclasses to implement any @abstract-annotated method (similar to C#'s abstract keyword).

See references/abstract-classes.md for full base-class patterns and subclass implementation rules.


13. Implementation Checklist

  • All variables, parameters, and return types have explicit type hints
  • Typed arrays (Array[Type]) are used instead of untyped Array where possible
  • Other nodes used after an await are re-checked with is_instance_valid() (self needs no check)
  • Lambdas connected to signals are simple — complex logic goes in named methods
  • match statements include a _: default branch
  • @export variables use appropriate hints (@export_range, @export_enum, etc.)
  • @export_group organizes Inspector properties into logical sections
  • class_name is only used for scripts that need global visibility
  • is type check precedes as cast when the type isn't guaranteed
  • Properties with setters validate and clamp values
  • Overridden virtual methods call super() when extending non-built-in base classes
  • Variadic functions (...args) used when the number of trailing arguments is open-ended (Godot 4.5+)
  • Base classes that must not be instantiated use @abstract; required methods use @abstract func (Godot 4.5+)
Skills enregistrées