Help us improve
Share bugs, ideas, or general feedback.
From godot-dev
Godot 4.x development guidance for architecture, scenes, nodes, GDScript, signals, and Resources. Use when the project has .gd/.tscn/.tres files or the user mentions Godot or GDScript. Not for Unity, Unreal, or other engines.
npx claudepluginhub astrosteveo/astrosteveo-claude-pluginsHow this skill is triggered — by the user, by Claude, or both
Slash command
/godot-dev:godot-devThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Development guidance for Godot 4.x projects. The core goal: build games using Godot's node and scene system so the editor reflects the actual structure of the game. Scripts add behavior to nodes — they do not replace them.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Share bugs, ideas, or general feedback.
Development guidance for Godot 4.x projects. The core goal: build games using Godot's node and scene system so the editor reflects the actual structure of the game. Scripts add behavior to nodes — they do not replace them.
Claude's default behavior in Godot projects is to put everything in .gd scripts. Instead of creating nodes in the editor or via MCP, it writes scripts that programmatically construct the entire scene tree at runtime. The user opens their project and sees a barren scene tree — one node, one script, everything hidden inside code.
This is wrong. Godot's strength is its node and scene system. A game's structure should be visible, inspectable, and editable in the editor. Scripts add logic to nodes. They do not replace the scene tree.
Build with nodes and scenes first. Add scripts for behavior.
.tscn), not as runtime code in a scriptgodot-mcp:create_scene, godot-mcp:add_node, etc. to build the scene tree — do not write a script that calls add_child() for everything.tscn file and see what it contains, select nodes, move them, inspect their propertiesWrong — everything hidden in a script:
main.tscn
└── GameManager (Node) ← one node, one script, everything else created at runtime
Right — scene tree reflects the game:
main.tscn
├── Player (CharacterBody3D)
│ ├── CollisionShape3D
│ ├── MeshInstance3D
│ ├── HealthComponent (Node)
│ └── Camera3D
├── Environment (Node3D)
│ ├── Ground (StaticBody3D)
│ ├── DirectionalLight3D
│ └── WorldEnvironment
├── EnemySpawner (Node3D)
└── HUD (CanvasLayer)
├── HealthBar (ProgressBar)
└── ScoreLabel (Label)
| Godot Type | Role | Example |
|---|---|---|
| Node / Scene (.tscn) | Visible structure, composition, editor-editable | enemy.tscn, projectile.tscn, main_level.tscn |
| Script (.gd) | Behavior and logic attached to nodes | enemy_controller.gd, health_component.gd |
| Resource (.tres) | Pure data, serializable, shareable, inspector-editable | gear_data.tres, spawn_config.tres |
Reusable behaviors are Node-based components added as children, not deep inheritance trees.
enemy.tscn
├── EnemyController (CharacterBody3D)
│ ├── HealthComponent
│ ├── HitFlashComponent
│ ├── DamageNumberSpawner
│ ├── DropComponent
│ ├── CollisionShape3D
│ └── MeshInstance3D
Components connect to siblings via signals. To deal damage to anything: find its HealthComponent and call take_damage(). See references/conventions.md for the full component pattern.
Nodes communicate through signals, not direct references. A component emits signals about what happened; the parent or siblings decide what to do.
Use groups ("player", "enemies", "zone_root") for runtime lookups across the tree. Access via get_tree().get_first_node_in_group() or get_tree().get_nodes_in_group().
@export every tunable value — no exceptions. Distances, durations, colors, sizes, thresholds, speeds, counts, multipliers. If a designer might ever want to change it, it MUST be an @export or a Resource property. Scripts should contain zero magic numbers that represent design decisions. The inspector is the design tool — not the code editor.@tool — makes a script run in the editor. Use it for editor plugins, custom Resource previews, and scripts that need to show something in the viewport at edit time (e.g., procedural generators that preview in the editor viewport). Guard editor-only code with Engine.is_editor_hint(). Whether to use @tool on gameplay scripts depends on the project — some projects use it everywhere for editor visibility, others restrict it.class_name — registers a global class. Use it on types you reference elsewhere (custom Resources, components used via is checks, classes used as type hints). Skip it on scripts that are only attached to one specific scene node. Critical: never use class_name on autoload singleton scripts — Godot 4 will error with "Class hides an autoload singleton" because the autoload name and class_name conflict. Access autoloads via get_node_or_null("/root/AutoloadName") instead.See references/conventions.md for the complete convention set with examples.
This is non-negotiable. Scripts are logic engines. Resources are data. Every piece of content — enemy stats, loot tables, spawn rules, zone definitions, structure parameters, placement constraints — lives in custom Resource subclasses (.tres files), editable in the inspector. Scripts read Resources and execute behavior. Scripts never decide what content looks like — Resources do.
The designer builds the game through the inspector and .tres files. If a value is buried in a script, the designer can't tune it without reading code. That is a failure.
@export fields defining rules and parameters. Pure data, no side effects. Designers edit it in the inspector. Saved as .tres files.RandomNumberGenerator when randomness is involved. The script contains zero design decisions — only algorithms.PackedScene or Resource arrays. Editor-authored .tscn and .tres files.class_name SpawnConfig
extends Resource
## Enemy scenes to choose from (editor-authored .tscn files)
@export var enemy_scenes: Array[PackedScene] = []
@export var enemy_weights: Array[float] = []
@export var min_count: int = 3
@export var max_count: int = 8
@export var spawn_radius: float = 10.0
A spawner node holds a SpawnConfig export, reads the config at runtime, and instantiates the referenced scenes. The key: enemy visuals are existing .tscn scenes built in the editor, not meshes generated in code.
See references/data-driven-resources.md for detailed examples covering spawning, loot, and prop placement.
Godot provides SurfaceTool, ArrayMesh, ImmediateMesh, and MeshDataTool for generating meshes in code. These are legitimate tools for terrain, structures, cables, trails, water surfaces, and any mesh whose shape is computed rather than authored.
When procedural geometry is appropriate: the mesh itself needs to be algorithmic — its shape is computed, not authored.
When it is NOT appropriate: using BoxMesh.new() or SphereMesh.new() as lazy placeholders because creating a proper .tscn scene feels like more work. If an artist could author it, it should be a scene.
When building procedural generators, the same data-driven rule applies: the Resource defines what to generate, the script defines how.
RandomNumberGenerator seeded from the Resource for deterministic results.@tool) and runtime. The Resource parameters are the source of truth; the mesh is derived data. Don't embed generated meshes into scene files (don't set owner on generated children) — they bloat .tscn files with serialized vertex data.Example: a StructureResource defines wall segments, pillar count, decay level, colors. A StructureGenerator (MeshInstance3D with @tool) reads it and builds an ArrayMesh. Change the Resource in the inspector → the structure updates in the viewport. Same Resource + same seed = same structure every time.
When using Godot MCP tools for editor integration:
godot-mcp:open_scene before godot-mcp:play_scene — play runs the last-opened sceneglobal_position after add_child, not before — node must be in treecall_deferred() for add_child during _ready to avoid "parent busy" errorsscript.reload() + EditorInterface.get_resource_filesystem().scan()godot-mcp:execute_editor_script to set PackedScene exports on instanced scene nodesgodot-mcp:create_scene, godot-mcp:add_node, etc..tscn) with the appropriate node types — use MCP or guide the user to create it in the editor.tres@tool to that specific script.tscn sceneBoxMesh.new() as a placeholder? Stop. Create a scene instead.Wrong: Write a script that creates MeshInstance3D nodes with BoxMesh.new() at runtime.
Right:
.tscn scenes in the editor with proper meshes, collision, and componentsSpawnConfig Resource with exported arrays for enemy scenes, weights, countsWrong: Write a script that programmatically creates StaticBody3D, MeshInstance3D, lights, etc.
Right:
Wrong: Hardcode drop rates and item stats in a script.
Right:
GearData Resource subclass with exported stats (damage, rarity, etc.).tres files — editable in the inspectorLootConfig Resource with item pool references and drop weightsAutomate what you can. Hand off what you can't with clear playtest instructions.
See references/testing.md for framework setup, test structure, CI patterns, and when to hand off to the user.
Scene tree is empty / everything is runtime-generated This is the core problem. Identify what was created in scripts and move it to scenes/nodes. Use MCP to build the scene tree. Scripts should reference nodes, not create them.
"But I need things to spawn at runtime"
Runtime instantiation is fine — PackedScene.instantiate() to spawn pre-authored scenes. The scene itself (the enemy, the projectile, the pickup) should be a .tscn file, not geometry assembled in code.
"The user just wants a quick prototype" Even prototypes benefit from a visible scene tree. Create simple scenes with basic meshes in the editor. It takes seconds and the user can actually see and modify what they have.
MCP tool errors
godot-mcp:tool_name)call_deferred() for add_child in _readyRead references/anti-patterns.md for a catalog of common mistakes and how to avoid them.