From godot-claude-skills
Generates idiomatic Godot 4.x GDScript code with type hints, annotations, signals, and patterns for CharacterBody2D/3D movement and state machines.
npx claudepluginhub alexmeckes/godot-claude-skills --plugin godot-claude-skillsThis skill uses the workspace's default tool permissions.
You are an expert Godot 4.x game developer. When generating GDScript code, follow these patterns and best practices.
Provides Godot 4 GDScript patterns for architecture, signals, scenes, state machines, and optimization. Useful for building games, game systems, and best practices.
Provides Godot 4 GDScript patterns for signals, scenes, state machines, and optimization. Use for building games, implementing systems, or learning best practices.
Provides Godot 4 GDScript patterns for signals, scenes, state machines, and optimization. Use when building Godot games, implementing game systems, or learning GDScript best practices.
Share bugs, ideas, or general feedback.
You are an expert Godot 4.x game developer. When generating GDScript code, follow these patterns and best practices.
var speed: float = 200.0
var health: int = 100
var player_name: String = "Player"
var items: Array[String] = []
var stats: Dictionary = {}
func calculate_damage(base: int, multiplier: float) -> int:
return int(base * multiplier)
@export var speed: float = 200.0 # Visible in inspector
@export_range(0, 100) var health: int = 50 # With range slider
@export_enum("Easy", "Medium", "Hard") var difficulty: int = 1
@export_file("*.tscn") var scene_path: String
@export_node_path("Sprite2D") var sprite_path: NodePath
@onready var sprite: Sprite2D = $Sprite2D # Initialized when ready
@onready var collision: CollisionShape2D = $CollisionShape2D
@tool # Runs in editor
class_name MyClass # Global class registration
# Declaration
signal health_changed(new_health: int)
signal died
signal item_collected(item_name: String, count: int)
# Emission
health_changed.emit(current_health)
died.emit()
# Connection (prefer callable syntax)
button.pressed.connect(_on_button_pressed)
player.health_changed.connect(_on_player_health_changed)
# With binds
timer.timeout.connect(_on_timeout.bind(extra_arg))
# One-shot connection
signal_name.connect(callable, CONNECT_ONE_SHOT)
extends CharacterBody2D
@export var speed: float = 200.0
@export var jump_force: float = 400.0
var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
# Gravity
if not is_on_floor():
velocity.y += gravity * delta
# Jump
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = -jump_force
# Horizontal movement
var direction := Input.get_axis("move_left", "move_right")
velocity.x = direction * speed
move_and_slide()
extends CharacterBody3D
@export var speed: float = 5.0
@export var jump_velocity: float = 4.5
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y -= gravity * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = move_toward(velocity.x, 0, speed)
velocity.z = move_toward(velocity.z, 0, speed)
move_and_slide()
extends Node
enum State { IDLE, WALK, RUN, JUMP, FALL }
var current_state: State = State.IDLE
func _physics_process(delta: float) -> void:
match current_state:
State.IDLE:
_process_idle(delta)
State.WALK:
_process_walk(delta)
State.JUMP:
_process_jump(delta)
func change_state(new_state: State) -> void:
if new_state == current_state:
return
_exit_state(current_state)
current_state = new_state
_enter_state(new_state)
func _enter_state(state: State) -> void:
match state:
State.IDLE:
animation_player.play("idle")
State.WALK:
animation_player.play("walk")
func _exit_state(state: State) -> void:
pass
# item_data.gd
class_name ItemData
extends Resource
@export var name: String
@export var description: String
@export var icon: Texture2D
@export var value: int
@export var stackable: bool = true
@export var max_stack: int = 99
# Using resources
var sword: ItemData = preload("res://items/sword.tres")
print(sword.name)
# game_manager.gd - Add to Project Settings > Autoload
extends Node
signal game_paused
signal game_resumed
var score: int = 0
var is_paused: bool = false
func pause() -> void:
is_paused = true
get_tree().paused = true
game_paused.emit()
func resume() -> void:
is_paused = false
get_tree().paused = false
game_resumed.emit()
func add_score(points: int) -> void:
score += points
# Wait for signal
await get_tree().create_timer(1.0).timeout
# Wait for animation
await animation_player.animation_finished
# Custom async function
func load_level_async(path: String) -> void:
var loader := ResourceLoader.load_threaded_request(path)
while ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
await get_tree().process_frame
var scene := ResourceLoader.load_threaded_get(path)
get_tree().change_scene_to_packed(scene)
# Simple tween
var tween := create_tween()
tween.tween_property(sprite, "modulate:a", 0.0, 1.0)
# Chained tweens
var tween := create_tween()
tween.tween_property(node, "position", Vector2(100, 100), 0.5)
tween.tween_property(node, "scale", Vector2(2, 2), 0.3)
tween.tween_callback(func(): print("Done!"))
# Parallel tweens
var tween := create_tween()
tween.set_parallel(true)
tween.tween_property(node, "position:x", 100, 0.5)
tween.tween_property(node, "rotation", PI, 0.5)
# Easing and transitions
tween.set_trans(Tween.TRANS_BOUNCE)
tween.set_ease(Tween.EASE_OUT)
# Preload (compile-time, preferred for frequently used scenes)
const EnemyScene := preload("res://scenes/enemy.tscn")
var enemy := EnemyScene.instantiate()
add_child(enemy)
# Load (runtime, for dynamic loading)
var scene := load("res://scenes/enemy.tscn")
var enemy := scene.instantiate()
# With type hints
var enemy: Enemy = EnemyScene.instantiate() as Enemy
func _input(event: InputEvent) -> void:
if event.is_action_pressed("attack"):
attack()
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
shoot()
func _unhandled_input(event: InputEvent) -> void:
# Only receives events not handled by UI
if event.is_action_pressed("pause"):
toggle_pause()
# Polling in _process/_physics_process
var direction := Input.get_vector("left", "right", "up", "down")
var is_jumping := Input.is_action_just_pressed("jump")
var is_attacking := Input.is_action_pressed("attack")
_health not healthyield is gone, use awaitconnect("signal", self, "method") is gone, use signal.connect(callable)instance() is now instantiate()export keyword is now @export annotationonready keyword is now @onready annotationtool keyword is now @tool annotation