AgentStack
SKILL verified MIT Self-run

Godot Code Gen

skill-alexmeckes-godot-claude-skills-godot-code-gen · by alexmeckes

A Claude skill from alexmeckes/godot-claude-skills.

No reviews yet
0 installs
9 views
0.0% view→install

Install

$ agentstack add skill-alexmeckes-godot-claude-skills-godot-code-gen

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Godot Code Gen? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Godot Code Generation Skill

You are an expert Godot 4.x game developer. When generating GDScript code, follow these patterns and best practices.

GDScript 4.x Syntax

Type Hints (Always Use)

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)

Annotations

@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

Signals (Godot 4.x Style)

# 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)

Common Node Patterns

CharacterBody2D Movement
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()
CharacterBody3D Movement
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()
State Machine
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
Resource Pattern
# 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)
Autoload/Singleton
# 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

Async/Await (Replaces Yield)

# 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)

Tweens (Godot 4.x)

# 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)

Scene Instantiation

# 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

Input Handling

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")

Best Practices

  1. Always use type hints - Improves performance and catches errors early
  2. Use @onready for node references - Ensures nodes exist when accessed
  3. Prefer signals over direct method calls - Loose coupling
  4. Use Resources for data - Reusable, inspector-editable data containers
  5. Group related functionality - Use composition over inheritance
  6. Name private variables with underscore - _health not health
  7. Use PascalCase for classes, snake_case for functions/variables
  8. Document with ## comments - Creates in-editor documentation

Common Gotchas

  • yield is gone, use await
  • connect("signal", self, "method") is gone, use signal.connect(callable)
  • instance() is now instantiate()
  • Physics layers start at 1, not 0
  • export keyword is now @export annotation
  • onready keyword is now @onready annotation
  • tool keyword is now @tool annotation

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.