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.

On this page · 21 sections▾
- What this skill does
- The cookbook
- Install + README
- Watch it built
- 01 · 2D platformer with CharacterBody2D + TileMap
- 02 · Top-down shooter with signal-driven damage
- 03 · Hierarchical FSM as a Node tree
- 04 · Inventory system with custom Resource subclasses
- 05 · JSON-driven dialogue system
- 06 · Procedural terrain with FastNoiseLite
- 07 · Mobile touch controls + viewport scaling
- 08 · LAN multiplayer with ENetMultiplayerPeer
- 09 · Save/load with ConfigFile + FileAccess
- 10 · AnimationTree with a state-machine for character moves
- Community signal
- The contrarian take
- Real games shipped
- Gotchas
- Pairs well with
- FAQ
- 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 pageInstall
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.zipInstalls 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.
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.
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`.
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.
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.2One-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.
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.
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).
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 = 1One-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`.
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.
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.
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.
“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.
“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.
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.
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.
- Brotato (Blobfish) — top-down roguelike shipped on Steam, PS, Xbox, Switch, mobile — built with Godot
- Cassette Beasts (Bytten Studio) — open-world monster-tamer RPG shipped on Steam and consoles — built with Godot
- Dome Keeper (Bippinbits) — tower-defence + mining roguelite shipped on Steam — built with Godot
- Halls of Torment (Chasing Carrots) — Diablo-meets-Vampire-Survivors action game — built with Godot
- Pixelorama (Orama Interactive) — full pixel-art editor distributed via Steam and itch.io, built entirely in Godot as an application
- Discounter Strike (mariusseufzer) — App Store title shipped in 2023, name-checked verbatim by the developer on the Godot 4.1 HN thread
- GodotOS (popcar2) — fake-OS desktop demo that hit 534 points on HN, demonstrating Godot as a cross-platform application framework
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.
Related skills
Related MCP servers
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
- godot SKILL.md (the skill manifest)
- godotengine.org — official tool homepage
- Godot 4 official documentation
- GDScript basics — signals, _ready, type hints
- Godot showcase — shipped commercial titles
Community
- apitman — Hacker News
- sen — Hacker News
- mariusseufzer — Hacker News
- appstorelottery — Hacker News
- gopher_space — Hacker News
- appstorelottery — Hacker News
Critical and contrarian
Internal