/gdscript-patterns
Use when writing GDScript — static typing, await/coroutines, lambdas, match patterns, export annotations, inner classes, and common idioms
$ npx -y skills add jame581/GodotPrompter --skill gdscript-patterns --agent claude-codeHow 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.mdname: 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 intTyped 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 surprisesEnabling 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!"), CONRead more
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 intTyped 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 surprisesEnabling 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!"), CONAgentic skills framework for Godot 4.x game development. Gives AI coding agents domain-specific expertise for GDScript and C# projects.
Other skills on godot-prompter.
- /authoring-godot-prompter-skills
Use when writing or editing a SKILL.md or an agent definition in this repo — required frontmatter, section ordering, and the GDScript-then-C# example convention.
Open skill - /releasing-godot-prompter
Use when cutting a GodotPrompter release or bumping its version — the version-bump sequence, tag-triggered workflow, and the marketplace manifests that must follow.
Open skill - /2d-essentials
Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D, custom drawing, and 2D meshes in Godot 4.3+
Open skill - /3d-essentials
Use when working with 3D-specific systems — materials, lighting, shadows, environment, global illumination, fog, LOD, occlusion culling, and decals in Godot 4.3+
Open skill - /ability-system
Use when building character abilities — Resource-based abilities with cost/cooldown/cast, buffs/debuffs, stat modifiers, gameplay tags, and HUD binding
Open skill - /addon-development
Use when creating Godot editor plugins — EditorPlugin, @tool scripts, custom inspectors, and dock panels
Open skill

