Skip to content
Development
Skill

/gdscript-patterns

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

From plugin
godot-prompter
54157 skills9 agents1 hook
Install
$ npx -y skills add jame581/GodotPrompter --skill gdscript-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/gdscript-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

gdscript-patterns.SKILL.md
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**:

| Warning | Effect | |-------------------------|---------------------------------------------| | `UNTYPED_DECLARATION` | Warns on any untyped variable/parameter | | `INFERRED_DECLARATION` | Warns on `:=` (prefers explicit types) | | `UNSAFE_CAST` | Warns on unsafe `as` casts | | `UNSAFE_CALL_ARGUMENT` | Warns 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](https://docs.godotengine.org/en/latest/tutorials/migrating/upgrading_to_godot_4.7.html).

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: node may be freed while awaiting
func unsafe_coroutine() -> void:
    await get_tree().create_timer(5.0).timeout
    position = Vector2.ZERO  # crash if node was freed during wait!

# SAFE: check validity after await
func safe_coroutine() -> void:
    await get_tree().create_timer(5.0).timeout
    if not is_instance_valid(self):
        return
    position = Vector2.ZERO

---

3. Lambda Functions

Lambdas are inline anonymous functions, useful for callbacks, sorting, filtering.

Basic Syntax

# Single-expression lambda
var double := func(x: int) -> int: return x * 2

# Multi-line lambda
var greet := func(name: String) -> void:
    print("Hello, %s!" % name)
    print("Welcome!")

# Calling a lambda
double.call(5)  # returns 10
greet.call("Player")

With Signals

# Inline signal connection (one-off use)
$Button.pressed.connect(func(): print("Button pressed!"))

# With arguments
$Timer.timeout.connect(func():
    health -= 1
    if health <= 0:
        die()
)

# One-shot connection (auto-disconnects after first call)
$Timer.timeout.connect(func(): print("Once!"), CON
Read more
Ships withgodot-prompter

Agentic skills framework for Godot 4.x game development. Gives AI coding agents domain-specific expertise for GDScript and C# projects.

Get the whole plugin