Updated April 2026Cookbook19 min read

Claude Godot skill: 10 game prototypes you can ship today

Ten real Godot 4 prototypes — 2D platformer, top-down shooter, hierarchical FSM, Resource-based inventory, JSON dialogue system, FastNoiseLite terrain, mobile touch HUD, ENet multiplayer lobby, ConfigFile save system, AnimationTree state-machine — each as a single Claude prompt with the exact .gd and .tscn it produces.

Already know what skills are? Skip to the cookbook. First time? Read the explainer then come back. Need the install? It’s on the /skills/godot page.

Editorial illustration: a luminous teal node-graph glyph connected to a game-controller glyph by a teal flow arc, on a midnight navy background.
On this page · 21 sections
  1. What this skill does
  2. The cookbook
  3. Install + README
  4. Watch it built
  5. 01 · 2D platformer with CharacterBody2D + TileMap
  6. 02 · Top-down shooter with signal-driven damage
  7. 03 · Hierarchical FSM as a Node tree
  8. 04 · Inventory system with custom Resource subclasses
  9. 05 · JSON-driven dialogue system
  10. 06 · Procedural terrain with FastNoiseLite
  11. 07 · Mobile touch controls + viewport scaling
  12. 08 · LAN multiplayer with ENetMultiplayerPeer
  13. 09 · Save/load with ConfigFile + FileAccess
  14. 10 · AnimationTree with a state-machine for character moves
  15. Community signal
  16. The contrarian take
  17. Real games shipped
  18. Gotchas
  19. Pairs well with
  20. FAQ
  21. Sources

What this skill actually does

Sixty seconds of context before the cookbook — what the godot skill is, what Claude returns when you invoke it, and the one thing it does NOT do for you.

What this skill actually does

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows.

bfollington, the skill author · /skills/godot

What Claude returns

When triggered, Claude returns a Godot 4.x project tree: GDScript files (`extends Node` / `Node2D` / `CharacterBody2D`, typed signals, `_ready()`/`_process()`/`_physics_process()` lifecycle, `@onready` for child refs), scene files (`.tscn` with `[gd_scene]` headers, `[node]` entries, transforms, and `ExtResource("id")` references — never `preload()` inside a .tscn), and resource files (`.tres` for items, animations, save data). Includes `project.godot` config, AnimationPlayer / AnimationTree / CollisionShape / RigidBody / Area2D wiring, and validation via the bundled `validate_tscn.py` and `validate_tres.py` scripts before files land.

What it does NOT do

It does not install the Godot editor or run `godot --export-release` for you — you still need the binary on your machine, and any Android/iOS export templates remain a one-time editor download.

How you trigger it

scaffold a Godot 2D platformer with CharacterBody2D and a TileMapadd a signal-driven state machine to this Godot enemyconvert this GDScript inventory to typed Resource subclasses (.tres)

Cost when idle

~100 tokens at idle (skill name + description in the system prompt). The body of SKILL.md, the validation scripts, and the file-format / architecture-patterns reference docs load only when the skill is triggered.

The cookbook

Each entry below is a prototype you could ship this week. They run in the order I’d teach them — the early ones cover the everyday Godot surface (CharacterBody2D, signals, state machines), the later ones lean on features you only need when the prototype is becoming a game (multiplayer, save systems, AnimationTree). Every entry pairs with one or two skills or MCP servers you already have on mcp.directory.

Install + README

If the skill isn’t on your machine yet, here’s the one-liner. The full install panel (Codex, Copilot, Antigravity variants) is on the skill page.

One-line install · by bfollington

Open skill page

Install

mkdir -p .claude/skills/godot && curl -L -o skill.zip "https://mcp.directory/api/skills/download/235" && unzip -o skill.zip -d .claude/skills/godot && rm skill.zip

Installs to .claude/skills/godot

Watch it built

A clean walkthrough of the Godot 4 editor and the scene-tree model — useful before the cookbook because it anchors what each output snippet looks like once you open it inside Godot.

01

2D platformer with CharacterBody2D + TileMap

Scaffold a Godot 4 side-scrolling platformer — a CharacterBody2D player with gravity, jump, and coyote-time, and a TileMap level — as a runnable scene tree.

ForSolo devs and game-jam teams who want a working 2D base in under an hour.

The prompt

Use the godot skill. Read @docs/design/platformer.md for the controls spec. Create a Godot 4.x project under `game/` with a `Main.tscn` scene tree: a CharacterBody2D player at (200, 200) with a CollisionShape2D (CapsuleShape2D 16x32), a Sprite2D, an AnimationPlayer, plus a Camera2D child smoothing horizontal only. Add a TileMap node with one TileSet resource referencing `assets/tiles.png` (32px tiles). Author `scripts/Player.gd` extending CharacterBody2D with: SPEED=300, JUMP_VELOCITY=-450, gravity from `ProjectSettings.get_setting("physics/2d/default_gravity")`, coyote-time=0.1, jump-buffer=0.1. Implement `_physics_process(delta)` reading `ui_left/ui_right/ui_accept`, applying friction on the floor, and calling `move_and_slide()`. Validate the .tscn with the bundled `validate_tscn.py` script before saving.

What slides.md looks like

extends CharacterBody2D

const SPEED := 300.0
const JUMP_VELOCITY := -450.0
var coyote := 0.0
@onready var anim: AnimationPlayer = $AnimationPlayer

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity.y += ProjectSettings.get_setting("physics/2d/default_gravity") * delta
		coyote = max(coyote - delta, 0.0)
	else:
		coyote = 0.1
	if Input.is_action_just_pressed("ui_accept") and coyote > 0:
		velocity.y = JUMP_VELOCITY
	velocity.x = Input.get_axis("ui_left", "ui_right") * SPEED
	move_and_slide()

One-line tweak

Swap to a Metroidvania feel by adding a wall-jump branch — read `is_on_wall_only()` in `_physics_process` and apply a horizontal kick of `-sign(get_wall_normal().x) * SPEED * 1.2` when the player jumps off a wall.

02

Top-down shooter with signal-driven damage

Build a top-down twin-stick shooter where a Player emits a `damage_dealt` signal that an Enemy listens to, decoupling combat through signals instead of direct refs.

ForDevs starting their first signal-driven Godot project and wanting the canonical shape.

The prompt

Use the godot skill. Output a Godot 4.x project at `shooter/` with three scenes: `Player.tscn` (CharacterBody2D + AnimatedSprite2D + Marker2D as muzzle), `Bullet.tscn` (Area2D + CollisionShape2D + Sprite2D), `Enemy.tscn` (CharacterBody2D + CollisionShape2D + a `health: int` exported var). Author `Player.gd` with `signal damage_dealt(amount: int, target: Node)`, fire bullets on `mouse_left` toward `get_global_mouse_position()`. The Bullet's `_on_body_entered(body)` calls `body.take_damage(10)` and queues free. Enemy.gd defines `func take_damage(amount: int) -> void` that decrements health and emits `died` when health <= 0. Wire signals in `_ready` via `connect`, never via direct node-path refs. Save scripts under `scripts/`. Validate every .tscn before writing.

What slides.md looks like

extends CharacterBody2D
class_name Enemy

signal died(enemy: Enemy)

@export var health: int = 30

func take_damage(amount: int) -> void:
	health -= amount
	if health <= 0:
		died.emit(self)
		queue_free()

func _physics_process(delta: float) -> void:
	velocity = (get_tree().get_first_node_in_group("player").global_position - global_position).normalized() * 80
	move_and_slide()

One-line tweak

Swap the chase AI for a state-machine enemy by replacing `_physics_process` with a `current_state.tick(delta)` call, where `current_state` is one of three Node children: `IdleState`, `ChaseState`, `AttackState`.

03

Hierarchical FSM as a Node tree

Convert an enemy AI from a switch-statement to a hierarchical finite state machine implemented as Node children — Idle, Patrol, Chase, Attack — so each state is its own .gd file.

ForDevs whose `match state` blocks have grown past 100 lines and need a cleaner shape.

The prompt

Use the godot skill. Read @scripts/enemy/legacy_ai.gd for the existing logic. Refactor it into a hierarchical FSM: a `StateMachine` Node child of the enemy, with `Idle`, `Patrol`, `Chase`, `Attack` as Node children of `StateMachine`. Each state extends a base `State` class with `func enter() -> void`, `func exit() -> void`, `func tick(delta: float) -> void`, and `func handle_input(event: InputEvent) -> State`. The `StateMachine.gd` holds `current_state: State`, calls `current_state.tick(delta)` in `_physics_process`, and transitions via `transition_to(new_state_name: String)`. Wire `state_changed(from, to)` signal for debug. Save under `scripts/states/`. Each state file must be ≤40 lines. Validate the resulting `Enemy.tscn` with `validate_tscn.py`.

What slides.md looks like

extends Node
class_name StateMachine

signal state_changed(from: String, to: String)

@export var initial_state: NodePath
var current_state: State

func _ready() -> void:
	for child in get_children():
		if child is State:
			child.machine = self
	current_state = get_node(initial_state) as State
	current_state.enter()

func transition_to(name: String) -> void:
	var next := get_node_or_null(name) as State
	if next == null: return
	current_state.exit()
	state_changed.emit(current_state.name, next.name)
	current_state = next
	current_state.enter()

One-line tweak

Promote it to a hierarchical state machine by letting each state own a sub-`StateMachine` child — useful for `Attack` containing `Windup`, `Strike`, `Recover` sub-states without polluting the top level.

04

Inventory system with custom Resource subclasses

Author an inventory system where each item is a `.tres` Resource (typed, editor-editable) and the inventory itself is an `Array[Item]` exported from a singleton — no JSON parsing, no string keys.

ForDevs designing RPG/loot systems who want item data the editor can edit and Git can diff.

The prompt

Use the godot skill. Output `scripts/items/Item.gd`: `class_name Item extends Resource` with `@export var id: StringName`, `@export var display_name: String`, `@export var icon: Texture2D`, `@export var max_stack: int = 1`, `@export var weight: float`. Then `scripts/items/Weapon.gd extends Item` adds `@export var damage: int`, `@export var attack_speed: float`. Output `assets/items/sword_iron.tres` and `potion_health.tres` as concrete .tres files using `ExtResource` for the icon — never `preload()` inside a .tres. Author an `Inventory.gd` autoload with `var items: Array[Item] = []`, `signal item_added(item: Item, count: int)`, and `func add(item: Item, count: int = 1) -> void`. Run `validate_tres.py` against both .tres files before declaring done.

What slides.md looks like

[gd_resource type="Resource" script_class="Weapon" load_steps=3 format=3]

[ext_resource type="Script" path="res://scripts/items/Weapon.gd" id="1_weapon"]
[ext_resource type="Texture2D" path="res://assets/icons/sword_iron.png" id="2_icon"]

[resource]
script = ExtResource("1_weapon")
id = &"sword_iron"
display_name = "Iron Sword"
icon = ExtResource("2_icon")
max_stack = 1
weight = 3.5
damage = 12
attack_speed = 1.2

One-line tweak

Add rarity tiers as a second Resource subclass — `class_name Rarity extends Resource` with `color: Color` and `weight_multiplier: float` — and reference it via `@export var rarity: Rarity` so item drops can roll on the rarity table.

05

JSON-driven dialogue system

Build a branching dialogue system with a CanvasLayer UI, dialogue lines loaded from a JSON file, and a typewriter effect — all wired through a Dialogue autoload.

ForNarrative designers and devs who want writers editing dialogue without touching .gd files.

The prompt

Use the godot skill. Output `dialogue/scenes/DialogueBox.tscn`: a CanvasLayer > PanelContainer > VBoxContainer with a Label (speaker), a RichTextLabel (line), and a HBoxContainer of Buttons (choices). Author `Dialogue.gd` autoload that loads `res://dialogue/scripts/intro.json` via `FileAccess.open(...).get_as_text()` then `JSON.parse_string(...)`. The JSON is `{ "start": { "speaker": "Mira", "text": "...", "choices": [{"label": "...", "goto": "node_id"}] } }`. Implement a typewriter via a Timer node that appends one char per 0.04s using `RichTextLabel.text` + BBCode `[wave]` for emphasis. On choice button press, jump to the referenced node. Emit `signal dialogue_finished` when the conversation ends.

What slides.md looks like

extends CanvasLayer
class_name DialogueBox

@onready var speaker: Label = $Panel/VBox/Speaker
@onready var line: RichTextLabel = $Panel/VBox/Line
@onready var choices: HBoxContainer = $Panel/VBox/Choices
var nodes: Dictionary

func load_script(path: String) -> void:
	var text := FileAccess.open(path, FileAccess.READ).get_as_text()
	nodes = JSON.parse_string(text)
	show_node("start")

func show_node(id: String) -> void:
	var node: Dictionary = nodes[id]
	speaker.text = node["speaker"]
	line.text = node["text"]

One-line tweak

Replace the JSON loader with a `.tres` DialogueGraph Resource so writers can edit dialogue inside the Godot editor instead of an external text editor — same `show_node(id)` interface, different storage backend.

06

Procedural terrain with FastNoiseLite

Generate a procedural top-down island with FastNoiseLite — sand, grass, forest, mountain biomes — painted to a TileMap on `_ready`, deterministic from a seed.

ForRoguelike devs and infinite-world prototypers who want a 5-minute terrain backbone.

The prompt

Use the godot skill. Output `terrain/scenes/IslandGenerator.tscn`: a Node2D with a TileMap child (TileSet referencing `assets/biomes.png` 32px). Author `IslandGenerator.gd`: in `_ready`, instantiate a FastNoiseLite, set `seed = 1234`, `noise_type = FastNoiseLite.TYPE_PERLIN`, `frequency = 0.04`, `fractal_octaves = 4`. For x in 0..127, for y in 0..127, sample `noise.get_noise_2d(x, y)`, map the [-1, 1] value to a tile index: <-0.2 sand (id 0), <0.1 grass (id 1), <0.4 forest (id 2), else mountain (id 3). Apply a radial falloff (`distance_to(center) / 64`) so the result is an island, not a continent. Use `set_cell(0, Vector2i(x, y), tile_id, Vector2i.ZERO)`. Make `seed` an @export var so the editor can re-roll terrain by changing one field.

What slides.md looks like

extends Node2D

@export var seed: int = 1234
@onready var tilemap: TileMap = $TileMap

func _ready() -> void:
	var noise := FastNoiseLite.new()
	noise.seed = seed
	noise.frequency = 0.04
	noise.fractal_octaves = 4
	for x in 128:
		for y in 128:
			var n := noise.get_noise_2d(x, y)
			var falloff := Vector2(x - 64, y - 64).length() / 64.0
			var v := n - falloff * 0.6
			var id := 0 if v < -0.2 else 1 if v < 0.1 else 2 if v < 0.4 else 3
			tilemap.set_cell(0, Vector2i(x, y), id, Vector2i.ZERO)

One-line tweak

Promote it to layered noise by adding a second FastNoiseLite for moisture and a third for elevation, then mapping (elevation, moisture) to a 6-biome Whittaker diagram (desert, grassland, forest, taiga, tundra, swamp).

07

Mobile touch controls + viewport scaling

Author a mobile virtual joystick + jump button using TouchScreenButton, with viewport stretch mode set to `viewport` so the game fits any aspect ratio.

ForDevs preparing an Android/iOS export of an existing desktop prototype.

The prompt

Use the godot skill. Edit `project.godot`: set `display/window/stretch/mode = "viewport"`, `display/window/stretch/aspect = "keep"`, `display/window/handheld/orientation = "landscape"`. Output `ui/scenes/TouchHUD.tscn`: a CanvasLayer with two TouchScreenButton children — a virtual stick anchored bottom-left (passby `move_left`/`move_right`/`move_up`/`move_down` actions) and a jump button bottom-right emitting `ui_accept`. Wire each TouchScreenButton's `action` property so `Input.is_action_pressed` works identically to keyboard input — no extra branching in the player controller. Add `InputMap` actions for `move_left/right/up/down/jump` in `project.godot`. Add a `Viewport.size_changed` listener that re-positions the HUD anchors when the device rotates.

What slides.md looks like

[gd_scene load_steps=3 format=3]

[node name="TouchHUD" type="CanvasLayer"]

[node name="StickLeft" type="TouchScreenButton" parent="."]
position = Vector2(120, 600)
action = "move_left"

[node name="JumpBtn" type="TouchScreenButton" parent="."]
position = Vector2(1080, 600)
action = "ui_accept"
visibility_mode = 1

One-line tweak

Replace the static stick with a virtual-stick that follows the thumb on first touch by writing a `_input(event)` handler that captures `InputEventScreenTouch` and offsets the StickLeft node to `event.position`.

08

LAN multiplayer with ENetMultiplayerPeer

Stand up a 2-to-4-player LAN co-op prototype using Godot's high-level multiplayer API — server hosts, clients join via IP, player nodes are MultiplayerSpawner-replicated.

ForDevs prototyping a co-op idea for a game jam without paying for Steamworks or a relay service.

The prompt

Use the godot skill. Output `net/scenes/Lobby.tscn`: a Control with two Buttons ("Host" / "Join") and a LineEdit for the host IP. Author `Lobby.gd`: on Host, `var peer := ENetMultiplayerPeer.new(); peer.create_server(7777, 4); multiplayer.multiplayer_peer = peer`. On Join, `peer.create_client(ip_text, 7777)`. Output `net/scenes/Game.tscn` with a MultiplayerSpawner whose `spawn_path` points at a `Players` Node2D and whose `spawnable_scenes` includes `Player.tscn`. Wire `multiplayer.peer_connected` to `Game.gd._on_peer_connected(id)` which spawns a Player with `name = str(id)` and `set_multiplayer_authority(id)`. Use `MultiplayerSynchronizer` with `position` and `velocity` properties replicated. Validate the .tscn with `validate_tscn.py`.

What slides.md looks like

extends Node
class_name Game

@export var player_scene: PackedScene
@onready var players: Node2D = $Players

func _ready() -> void:
	multiplayer.peer_connected.connect(_on_peer_connected)
	multiplayer.peer_disconnected.connect(_on_peer_disconnected)
	if multiplayer.is_server():
		_on_peer_connected(1) # spawn host

func _on_peer_connected(id: int) -> void:
	var p := player_scene.instantiate()
	p.name = str(id)
	p.set_multiplayer_authority(id)
	players.add_child(p, true)

One-line tweak

Swap LAN for WAN by replacing `ENetMultiplayerPeer.create_server` with `WebSocketMultiplayerPeer` and binding to a public host — same MultiplayerSpawner / MultiplayerSynchronizer, different transport.

09

Save/load with ConfigFile + FileAccess

Add a save system using ConfigFile for player settings (resolution, volume, key bindings) and FileAccess + JSON for the world state (player position, inventory, quest flags).

ForDevs at the moment they realise their playtest builds need saving and loading.

The prompt

Use the godot skill. Author `save/scripts/Settings.gd` autoload using ConfigFile: `func load_settings() -> void` reads `user://settings.cfg` with `cfg.get_value("audio", "master_volume", 1.0)`, `func save_settings()` writes the inverse. Then `save/scripts/SaveGame.gd` autoload using FileAccess + JSON for world state: `func save(slot: int) -> void` collects `{ "player": { "position": [x, y], "hp": ... }, "inventory": [item_ids], "quests": { ... } }` and writes to `user://save_%d.json` % slot. `func load(slot: int) -> Dictionary` does the inverse with `FileAccess.file_exists` guard. Hook `tree_exiting` on the Game scene to autosave to slot 0. Slot 1-3 are manual. Test that `user://` resolves to `~/.local/share/godot/app_userdata/<project>/` on Linux.

What slides.md looks like

extends Node
# Settings.gd autoload

const PATH := "user://settings.cfg"

func save_settings(volume: float, fullscreen: bool) -> void:
	var cfg := ConfigFile.new()
	cfg.set_value("audio", "master_volume", volume)
	cfg.set_value("display", "fullscreen", fullscreen)
	cfg.save(PATH)

func load_settings() -> Dictionary:
	var cfg := ConfigFile.new()
	if cfg.load(PATH) != OK:
		return { "master_volume": 1.0, "fullscreen": false }
	return {
		"master_volume": cfg.get_value("audio", "master_volume", 1.0),
		"fullscreen": cfg.get_value("display", "fullscreen", false),
	}

One-line tweak

Encrypt save files with `FileAccess.open_encrypted_with_pass(...)` and a per-install key derived from `OS.get_unique_id()` — same JSON shape, harder to cheat.

10

AnimationTree with a state-machine for character moves

Drive a character's idle / run / jump / fall / attack animations through an AnimationTree node whose state-machine reads the player's velocity and `is_on_floor()` — no `play("name")` calls in player code.

ForAnimators and devs whose `match` blocks of `anim.play("...")` have become unreadable.

The prompt

Use the godot skill. Output `Player.tscn` with an AnimationPlayer holding clips `idle`, `run`, `jump`, `fall`, `attack`, plus an AnimationTree node referencing the AnimationPlayer. Inside AnimationTree, configure an AnimationNodeStateMachine root with five states (idle, run, jump, fall, attack) and transitions: idle <-> run on `velocity_x != 0`, idle/run -> jump on `pressed_jump`, jump -> fall on `velocity_y > 0`, fall -> idle on `is_on_floor`, any -> attack on `pressed_attack` (advance condition expression). Author `Player.gd` to write `state_machine.set("parameters/conditions/pressed_jump", ...)` instead of `anim.play("jump")`. Save the AnimationTree state-machine as a `.tres` resource so it can be edited in the Inspector. Validate the .tres with `validate_tres.py`.

What slides.md looks like

extends CharacterBody2D

@onready var sm: AnimationNodeStateMachinePlayback = $AnimationTree.get(
	"parameters/playback")
@onready var tree: AnimationTree = $AnimationTree

func _physics_process(delta: float) -> void:
	tree.set("parameters/conditions/pressed_jump",
		Input.is_action_just_pressed("ui_accept") and is_on_floor())
	tree.set("parameters/conditions/is_on_floor", is_on_floor())
	tree.set("parameters/conditions/velocity_x_nonzero", abs(velocity.x) > 1.0)
	tree.set("parameters/conditions/velocity_y_falling", velocity.y > 0.0)
	move_and_slide()

One-line tweak

Add a 2D BlendSpace inside the `run` state so `velocity.length()` blends between walk, jog, and sprint clips smoothly — no extra states required.

Community signal

Three voices from people using Godot for shipped work. The first is the canonical first-person endorsement from the Godot 4.0 release thread; the second is the four-adjective summary that keeps recurring in adoption stories; the third is a shipped-game datapoint from a paid App Store title.

It's simply a joy to work with. From the single (relatively) lightweight executable model, to the dogfooding of implementing the UI with the engine itself, to the design of GDScript, etc etc.

apitman · Hacker News

Top-rated comment on the Godot 4.0 release thread — the canonical first-person endorsement that the engine, GDScript, and the editor all reinforce each other.

Source
It's such a breath of fresh air. It's light, fast, easy to learn, and can do 90% of what you need

sen · Hacker News

From the Godot 4.1 thread — the four-adjective summary that keeps showing up in adoption stories: light, fast, easy, sufficient.

Source
I shipped Discounter Strike to the AppStore last week. Built with Godot 4!

mariusseufzer · Hacker News

Shipped-game datapoint on the Godot 4.1 release thread — a paid App Store title built with Godot 4 in 2023, the existence proof for mobile export.

Source

The contrarian take

Not everyone reaches for Godot. The most honest critique on the Hacker News Godot 4.0 release thread is from hesdeadjim:

It's a toy language with almost zero open source library support. And trying to hire someone to work in it and implicitly agree that they don't care about their CV and future hireability is a tough sell.

hesdeadjim · Hacker News

From the Hacker News Godot 4.0 Released thread.

Source

Fair if you’re staffing a 30-person studio in 2026 — GDScript’s Stack Overflow tag is real but small, and a senior C# engineer’s CV does have more weight than a senior GDScript one. The godot skill earns its keep on the other side of the team-size cut: the solo dev or 2-3 person team where Claude can scaffold a working CharacterBody2D + signal-driven combat in twenty minutes, and where GDScript’s terseness is an asset, not a hiring liability. C# is also a first-class option in Godot 4 if the language is the blocker, and the cookbook above pairs cleanly with the csharp-developer skill on that path.

One more alternative worth naming: there is a Godot MCP server. If you want Claude to introspect the live editor’s scene tree (verify a `.tscn` loaded cleanly, list current node hierarchy, query script properties at runtime), install the godot-mcp server alongside this skill. The pair is the strongest composition: the skill scaffolds files, the server gives Claude the editor’s view of what those files compiled into. The related godot-documentation server pulls docs at runtime so the AnimationTree property paths in use case 10 never go stale.

Real games shipped with Godot

Concrete examples from public stores. None of these used the Claude skill specifically — they’re here so you have a target shape in mind when you write the prompt.

Gotchas (the four that bite)

Sourced from the godotengine/godot issue tracker and the file-format reference inside the skill itself.

GDScript syntax inside .tres files breaks the loader

A .tres file is a strict serialization format — no `var`, `const`, or `func` declarations, no `preload()`. Use `ExtResource("id")` for external references and `SubResource("id")` for inline ones. The bundled `validate_tres.py` catches every common offender; run it before you commit.

Untyped arrays leak through .tscn re-saves

An `Array` (untyped) in a .tscn loses its type info when the editor re-saves. Always use `Array[Type]([...])` syntax — `Array[Item]`, `Array[NodePath]`. The skill emits typed arrays by default; if you hand-edit a .tscn, keep the typing.

@onready var fires before child _ready callbacks

An `@onready var foo = $Child` runs before any `_ready()` on `Child`. If you need values that `Child._ready()` computed, move the read into your own `_ready()` or use `await get_tree().process_frame`. The 'common-pitfalls.md' reference in the skill spells this out.

Hand-edited .tscn hierarchies break on re-save

Manually adding a [node] without a matching `parent =` field, or with a stale ExtResource id, will load fine once and corrupt on the next editor save. Always validate with `validate_tscn.py` after any non-editor edit.

Pairs well with

Curated to match the cookbook’s actual integrations: the game-dev-adjacent skills (godot-gdscript-patterns, game-development, game-design, game-art, csharp-developer) plus the MCP servers the longer use cases (1, 2, 5, 9, 10) lean on.

Two posts that compose well with this cookbook: What are Claude Code skills? covers the underlying mechanism, and the drawio skill cookbook shows how to render the system-architecture diagrams that pair with use cases 3 and 8 of this post.

Frequently asked questions

What is the godot skill and what does it produce?

The godot skill (by bfollington) is a Claude Code skill that authors Godot 4.x project files — GDScript `.gd` source, `.tscn` scene files (the strict serialization format with `[gd_scene]` headers and `[node]` entries), `.tres` resource files for items / animations / save data, and `project.godot` config. It bundles `validate_tscn.py` and `validate_tres.py` validation scripts and code templates for the component-based, signal-driven, resource-based patterns that the Godot docs recommend.

Does the godot skill install the Godot editor for me?

No. The skill authors the project files and validates their syntax — it does not install the editor or download Android/iOS export templates. Grab the editor from godotengine.org/download (it's a single ~80MB binary, no installer) or `brew install --cask godot`. For the cookbook above, Godot 4.2 or newer is the minimum.

GDScript or C# — which does the godot skill prefer?

GDScript by default — the skill's templates and validation scripts assume `.gd` files. C# is a first-class option in Godot 4.x and you can ask Claude to author `.cs` files instead, but the validation tooling (`validate_tres.py`, `validate_tscn.py`) is GDScript-aware. If your team's hiring story makes C# non-negotiable, pair the godot skill with the csharp-developer skill for the C# bindings.

Can the godot skill scaffold a multi-scene game in one prompt?

Yes — the cookbook's use cases 1, 2, and 8 each scaffold three or four interconnected scenes (`Player.tscn` + `Bullet.tscn` + `Enemy.tscn`, or `Lobby.tscn` + `Game.tscn` + `Player.tscn`). The skill threads `ExtResource()` references between them correctly, runs `validate_tscn.py` against every scene, and writes scripts under `scripts/` so the project opens cleanly in the editor.

Does it support mobile export (Android / iOS) and the touch input layer?

Yes for the input layer (use case 7 in the cookbook covers TouchScreenButton + viewport stretch mode + landscape orientation), no for the export step itself. The skill writes the `project.godot` mobile overrides, but you still run `godot --export-release "Android" build.apk` from your terminal because the export templates are a one-time editor download.

Godot 4 vs Godot 3 — which version does the skill target?

Godot 4.x. The reference templates use Godot 4 conventions: `CharacterBody2D` (not `KinematicBody2D`), `move_and_slide()` returning void with `velocity` as a property (not a return value), `@export` (not `export var`), and typed arrays in `.tres` files. If you're on a legacy Godot 3 codebase, ask Claude to scaffold a fresh Godot 4 module alongside it instead of trying to mix the two.

Are there real games shipped on Steam built with Godot?

Plenty. Brotato (Blobfish), Cassette Beasts (Bytten Studio), Dome Keeper (Bippinbits), Halls of Torment (Chasing Carrots), and the cross-platform Pixelorama editor are all production Godot 4 / late-Godot 3 titles with real revenue. The Godot Showcase page (godotengine.org/showcase) is the canonical list. The skill's use cases mirror the patterns these shipped games rely on — TileMap-based 2D, signal-driven combat, Resource-based loot.

Is there a Godot MCP server I should use instead of the skill?

Both. The skill authors the source files; the godot MCP server (`/servers/godot-mcp`) and `godot-documentation` server let Claude introspect the live editor's scene tree and pull docs at runtime. The combination is best — the skill scaffolds the project, the MCP server verifies the .tscn loaded cleanly and gives Claude the editor's view of the current node hierarchy. The godot-mcp server is on mcp.directory; install both.

Sources

Primary

Community

Critical and contrarian

Internal

Keep reading