godot について
Godotスキルは、Godotプロジェクトの調査や変更に使うワークフロー、スクリプト、テンプレート、資料を提供します。プロジェクト作成、ゲーム機能、UI、アセット、テスト、デバッグ、エクスポートを扱い、タスク別の検証手順も示します。
主な機能
- 01プリセット設定でプロジェクトを作成
- 02正確なGodot APIシグネチャを検索
- 03型付きGDScriptテンプレートと手順を提供
- 04シーン、UI、アセットの作業に対応
- 05Lint、テスト、実行時チェックを収録
- 06プロジェクトのデバッグとエクスポートを案内
活用シーン
- 01プリセットからGodotプロジェクトを開始
- 02プレイヤー、メニュー、会話フローを実装
- 03シーンやスクリプトのエラーを確認
- 04プロジェクトのエクスポートを準備・検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ haxqer/ godot-skill --skill godotインストール後、Claude Code で/godotを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/api_lookup.mdarchitecture_qframework_lite.mdarchitecture_templates.mdasset_pipeline.mdaudio.mdauthoring_recipes.mdautomation_api.mdci.mddebugging.mdexport_targets.mdgame_ui.mdgdscript_conventions.mdgodot3_to_4.mdlocalization.mdnode_config.mdpixel_art.mdplaybooks.mdshaders.mdspatial_verification.mdtesting.mdtscn_format.mdtween.mdvfx_2d.mdscripts/assets/chroma_key_cutout.pydraw_image.gdmake_music.pymake_sfx.pyprocess_image.gdscripts/audio/setup_audio_buses.gdREADME(原文)English
プロジェクトの README よりこの README の日本語訳はまだありません。以下は英語の原文です。日本語の紹介はページ上部をご覧ください。
Godot
Use this skill to inspect and modify Godot projects with the bundled workflows, scripts, and any available host-native Godot automation tooling, including gameplay presentation work such as art generation, frame animation integration, and project-native integration of provided or separately generated music and story content.
Start
Route By Task
| Task | Read / run | Verify |
|---|---|---|
| New project from nothing | scripts/project/scaffold_project.py DEST --preset pixel2d|hd2d|3d|ui (hand-run version: references/playbooks.md §1) | its validate.counts → errors and warnings both 0 |
| Any engine class, method, signal or constant you are about to type | scripts/docs/api_lookup.py CharacterBody2D.move_and_slide (references/api_lookup.md) | exit 0 prints the exact 4.7 signature; a wrong name exits 1 with the nearest real ones |
| Player controller (2D/3D) | templates/gdscript/player_*.gd, playbooks §2, §3, §12, §21 | run_scenario.py → "ok": true |
| Enemy, damage, health, projectiles, waves | templates/gdscript/{enemy_patrol_2d,enemy_chase_nav_2d,health,hitbox,hurtbox,projectile,shooter,wave_spawner}.gd, playbooks §4, §19 | run_scenario.py → "ok": true |
| State machine | templates/gdscript/{state,state_machine}.gd, playbook §4 | run_project.py → counts.errors == 0 |
| Tile level | build_tileset + paint_tilemap ascii_map, playbook §5 | inspect_tilemap "format":"text" rows match |
| Menu / theme | references/game_ui.md, playbook §6 | run_scenario.py ui_report → findings=0 |
| HUD, pause, dialog, branching dialogue, settings | templates/gdscript/{hud,pause_menu,dialog_box,dialogue_runner,choice_list,settings,settings_menu,input_remap}.gd, playbooks §7–§9, §16, §17 | run_scenario.py ui_report → findings=0 |
| Items, inventory, loot, save / load | templates/gdscript/{item_data,inventory,loot_table,inventory_ui,save_manager}.gd, playbooks §10, §15 | run_tests.py → "ok": true |
| A whole small game (collectathon, wave shooter, sokoban, 3D third-person) | references/playbooks.md §18–§21, finishing checklist §22 | the scripted scenario wins the level; §22's five commands exit 0 |
| Art with no image generator; cleaning generated art | references/pixel_art.md, draw_image, process_image, playbook §14 | payload frame_reports[*].rows reads back what you drew; inspect_image expect |
| Sound effects and music | references/audio.md, scripts/assets/make_sfx.py, scripts/assets/make_music.py, playbook §13 | inspect_audio expect → exit 0 |
| Shader effect (flash, outline, dissolve, CRT, toon, water…) | templates/shaders/*.gdshader, references/shaders.md | check_project → failed_count == 0; screenshot expect |
| Scene fades, music, SFX wiring | templates/gdscript/{scene_transition,audio_manager}.gd, playbook §11 | validate_project.py → counts.warnings == 0 |
Writing or editing any .gd | references/gdscript_conventions.md, then lint_project.py | lint_project.py → counts.errors == 0 |
| Project written for Godot 3 | references/godot3_to_4.md | lint_project.py --only godot3_api,godot3_shader → counts.errors == 0 |
Custom data (class_name X extends Resource) | resource_batch with script, typed JSON __script | inspect_resource → script_class |
Hand-writing .tscn/.tres text | references/tscn_format.md | inspect_scene nesting matches intent |
| Moving, renaming or reorganising files | never mv — scripts/project/move_resource.py PROJECT SRC DST (--map for many, --dry-run to preview) | its verify.new_missing == [], then validate_project.py → "ok": true |
| Forgot an op's parameters, or what tools exist | dispatcher help '{}' / help '{"op":"add_node"}' | the printed command runs as-is |
| No op fits, or a quick logic check | run_gdscript '{"code":"return …"}' (references/api_lookup.md) | "ok": true; a parse or runtime error exits 1 with code:<line> |
| Player falls through the floor, sprite or particles silently do nothing, collisions never fire | references/node_config.md, validate_project.py | check_project → config_warning_count == 0, physics_layers.findings == [] |
| Runtime errors or a project that will not boot | references/debugging.md | run_project.py → "ok": true |
| Errors in scenes and input paths a boot never reaches; leaks, hangs | scripts/debug/smoke_scenes.py PROJECT --seconds 2 --jobs 4 --fuzz | exit 0, every scenes[] entry ok |
| Unit-testing game logic (no addon, no download) | references/testing.md, scripts/test/run_tests.py PROJECT --init-mini | "ok": true, counts.tests > 0 |
| Verifying anything without vision or hearing | references/automation_api.md "Verification Without Vision" | dump_tree / ui_report / spatial_report / inspect_image / inspect_tilemap / inspect_audio |
| Is it on screen, in the floor, lit, in front of the camera (2D/3D) | references/spatial_verification.md | run_scenario.py spatial_report fail_on → findings=0 |
| Packaging a build, from no preset at all | references/export_targets.md, add_export_preset, scripts/export/export_project.py | artifact exists; scripts/export/serve_web.py DIR --check → "ok": true |
Read only the playbook section or reference the table names; each is self-contained.
- Verify which Godot automation path is available before planning edits. Prefer any native or mapped Godot tools in the host agent. Use the bundled headless dispatcher only for the supported file and scene operations listed below.
- Resolve
project_pathto an absolute project directory when a host tool requires it. - Normalize scene and resource paths to
res://...when working directly with the bundled Godot scripts in this skill. - Inspect unfamiliar projects with any available project-discovery tools, or fall back to reading
project.godot, scene files, and scripts directly. - When the task involves art, music, or story, inspect the existing visual style, palette, audio direction, and narrative format before creating new content.
- If the user does not specify a style, preserve any clearly established project direction first. If there is no established direction to follow, default new visual work to a pixel-art style and keep related presentation choices consistent with it.
- Read
references/asset_pipeline.mdonly when the task involves generated art, cutouts, or frame animation assets. - Read
references/architecture_qframework_lite.mdonly when the task involves Godot architecture design, modular refactoring, feature boundaries, controller or system or model responsibilities, command and query separation, or QFramework-style structure. - Read
references/architecture_templates.mdonly when you need a ready folder layout or starter skeletons for GodotGDScriptorC#architecture work. - Read
export_presets.cfgbefore planning export work. Reuse the preset names, bundle identifiers, signing settings, and feature tags that already exist instead of inventing replacements. - Require a local
godotCLI with shell access before using the bundled dispatcher fallback, runtime runner, or CLI export wrapper. The bundled APIs are designed against the current stable Godot docs and verified on Godot4.7(compatible with Godot 4.x). - Read
references/export_targets.mdonly when the task involves packaging, signing, or shipping builds for Android, iOS, Web, Windows, or macOS. - Copy a template from
templates/gdscript/instead of writing a player, state machine, HUD, save system, menu, or dialog controller from memory. Each file is Godot 4.7, fully typed, and verified to compile with zero warnings; its header states the scene tree, autoloads, and input actions it needs and gives theattach_scriptcall. Templates with aclass_nameneedgodot --headless --path /absolute/project --importonce after copying. - Read
references/playbooks.mdwhen the task matches a common shape (new project, player, enemy, tile level, menu, HUD, pause, dialog, save/load, transitions, 3D starter, generated sound and art, inventory, branching dialogue, settings, or a whole small game — collectathon platformer, wave shooter, sokoban, third-person 3D). Each playbook is a numbered list of runnable command blocks plus a Verify block with the exact expected result — follow it literally rather than improvising an order. Each opens with a**Requires.**line naming the earlier sections to replay first, into the same project; every file a command reads is written by an earlier block (scenarios as heredocs, art bydraw_image), so never hand-save a fenced block or invent a path. All 22 are replayed, Verify blocks included, bytests/test_playbooks_replay.py. - Run
python3 /absolute/path/to/godot/scripts/debug/lint_project.py /absolute/path/to/project --prettybefore and after touching any.gd,.tscn,.tres, or.gdshader. It needs no Godot, finishes in well under a second, and reports what Godot's own messages hide: Godot 3 API, syntax and shader names with the exact 4.x replacement, input actions, groups,res://paths and animation names that resolve to nothing,:=reads that will not parse,$Path/%Namereferences to nodes that do not exist in the scene the script is attached to (with the real children listed),%Namewithoutunique_name_in_owner,[connection]targets whose method is missing, andext_resource/autoload paths that are not on disk.errormeans Godot will refuse to parse or the reference cannot resolve;warningmeans it compiles but is risky. - Read
references/godot3_to_4.mdwhen the linter reportsgodot3_api, when a project still carries Godot 3 names, or before writing any API from memory — it is the rename table (nodes, resources, syntax, math, Control properties, signals, file/OS) the linter's rules are generated from. - Run
help '{}'through the dispatcher before guessing an operation or parameter name;help '{"op":"add_node"}'prints the parameters ((required)/(default: X)plus value shapes), a runnable example, the gotchas, and a ready-to-paste command. The same listing names every bundled python tool (linter, runners, API lookup, asset generators, file mover, scaffold, export) with a runnable command, so it is the one place to discover what exists. - Look an engine name up before you type it:
python3 /absolute/path/to/godot/scripts/docs/api_lookup.py CharacterBody2D.move_and_slide Area2D.body_entered String.begins_with KEY_SPACEprints the installed engine's exact signatures (classes, inherited members, Variant types, globals, theme items via--kind theme_items,--search TEXTfor a half-remembered name). A name that does not exist exits 1 with the nearest real names and, for a Godot 3 name, its 4.x replacement. It has signatures, not prose. Readreferences/api_lookup.mdfor it and forrun_gdscript. - Start a new project with
python3 /absolute/path/to/godot/scripts/project/scaffold_project.py /absolute/new_project --preset pixel2d(hd2d,3d,ui): settings, input map with keyboard and gamepad, layer names, a main scene (the3done already has a camera, a light and an environment), autoload templates,.gitignore, a boot-check scenario — validated with zero errors and zero warnings before it returns. - Never
mv,cpor rename a file inside a project from the shell: runpython3 /absolute/path/to/godot/scripts/project/move_resource.py /absolute/project art/player.png art/sprites/(--map moves.jsonfor a reorganisation,--dry-runto preview). It moves the.import/.uidsidecars, rewrites every reference, re-imports, and proves with the linter that nothing broke. A baremvcan break a project silently: auid=reference keeps loading while its recorded path rots. - Read
references/node_config.mdwhen something silently does nothing — a player that falls through the floor, an invisible sprite, particles that never emit, a collision that never fires.check_projectre-derives the editor's node configuration warnings (the yellow triangles, which no script can read) over every instantiated scene, prints each with afix:that is a runnable dispatcher call, and surveys physics layers and masks across the project. - Read
references/pixel_art.mdwhen the task needs sprites, tiles, icons or UI panels and no image generator is available (draw_imageturns ASCII rows and shapes into a PNG and reads it back character for character), or when generated art has soft edges, off-grid pixels or a baked background (process_image:remove_background,trim,pixelate,quantize,pack_frames,split_sheet). - Read
references/audio.mdwhen the game needs sound and none was supplied:scripts/assets/make_sfx.py(20 sfxr-style presets) andscripts/assets/make_music.py(looping chiptune from a preset or a JSON song) write WAVs with nothing but the python standard library, andinspect_audioverifies them as numbers, an ASCII envelope andexpectgates. - Read
references/shaders.mdand copy fromtemplates/shaders/(24 verified.gdshaderfiles) instead of writing a shader from memory. A shader that fails to compile renders as the default material with no runtime error, socheck_project— which compiles every shader and fails the file — is the check, not "it looks fine". - Read
references/testing.mdwhen logic is worth a unit test:scripts/test/run_tests.py PROJECT --init-miniinstalls a one-file, zero-download framework; a test killed by a runtime error reads aserror, never as a pass. - Read
references/spatial_verification.mdwhen you need to know where things are in the running game without looking: the scenario stepspatial_reportgives every node's world rect or AABB, what the camera sees, whether a body is embedded in the floor, and whether a 3D scene has a camera and a light at all. - Read
references/gdscript_conventions.mdbefore writing or editing any.gdfile. Generated GDScript must parse and boot first-try: annotate every variable, parameter, and return type, and never use:=where the right-hand side has no concrete static type ($Node,%Unique,get_node(),instantiate(),Dictionary/Arrayreads,JSON.parse_string(),null, or a call into an untyped function). Godot rejects those at parse time — withinference_on_variantshipping set to error — so the project will not start at all. - Read
references/debugging.mdwhen the task involves running the project, reading the Godot debugger's errors, and fixing them. - Read
references/automation_api.mdwhen using inspection,resource_batch,project_batch, tileset/tilemap/gridmap/theme/animation/audio-bus authoring, collision/CSG/navmesh baking, glTF export, replication config, unit-test running, import audit, scenario, environment probe, validation, or export preflight APIs. - Read
references/authoring_recipes.mdwhen the task involves shaders/ShaderMaterial, particles, environment/sky, fonts, multiplayer replication, 3D import options, GDExtension, or feature tags — it gives the generic-op recipes and lists what is editor-only and must not be attempted headlessly (LightmapGI/occluder/reflection-probe bakes, VisualShader graphs). - Read
references/vfx_2d.mdwhen the task involves 2D lighting, particles, parallax, trails, paths, or tile-based levels — it also lists the 4.3+ node deprecations (TileMap→TileMapLayer,ParallaxBackground→Parallax2D). - Read
references/game_ui.mdwhen the task involves menus, HUDs, dialogs, inventories, or anyControlwork — it gives the container-first layout doctrine (the fix for sibling controls piling up at(0, 0)), runnable title/HUD/pause/dialogscene_batchskeletons, a complete gameTheme, and the pixel-art UI rules. - Read
references/tscn_format.mdwhenever a.tscnor.treswill be written or patched as text — a host without shell access cannot run the bundled dispatcher, or an entire new scene is being authored from scratch. Hand-written node hierarchies fail silently: a tree where every node carriesparent="."loads with zero errors and stacks everyControlat (0,0), andcheck_projectdoes not detect it. - Read
references/tween.mdwhen adding code-driven juice (punches, fades, shakes); Tweens are runtime-only and ship inside scripts viaattach_script. - Read
references/localization.mdwhen translating game text or wiring translation files (note: POT template generation is editor-only in Godot 4.x). - Read
references/ci.mdwhen setting up automated test or export pipelines. - Install this skill in a folder named
godotso the folder name matchesname: godotin hosts that validate skill naming.
Godot 4.7 Notes
- Verified on
godot 4.7.stable. The scene and control operations set node properties and instantiate node classes throughClassDB, so Godot 4.7 additions work with the existing operations without special-casing: new node types such asAreaLight3D,VirtualJoystick, andDrawableTexture2Dcan be added withadd_node/scene_batch, and new properties such as theControloffset transforms (offset_transform_enabled,offset_transform_position,offset_transform_rotation,offset_transform_scale,offset_transform_pivot— translate/rotate/scale a control without disturbing container layout) andCollisionShape2D.one_way_collision_directioncan be set withconfigure_node/configure_controlusing the typed-value format below. - The runtime runner and log parser key off the stable
SCRIPT ERROR:/ERROR:/WARNING:/Parse Error:output shapes, so they keep working across Godot 4.x while being verified against 4.7.
Portable CLI Fallback
Use these paths in shell-capable environments such as Claude Antigravity when dedicated Godot tools are not exposed.
Replace /absolute/path/to/godot with the absolute path to the installed godot Skill root.
Scene Operations Through The Dispatcher
godot --headless --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
scene_batch '{"scene_path":"scenes/main.tscn","create_if_missing":true,"root_node_type":"Node2D","actions":[{"type":"add_node","node_type":"Camera2D","node_name":"Camera"}]}'
- Replace
scene_batchwith any supported operation:inspect_project,inspect_scene,inspect_resource,resource_batch,project_batch,audit_imports,set_import_options,build_tileset,paint_tilemap,paint_gridmap,build_theme,bake_collision,collision_from_sprite,bake_csg,gltf_export,build_replication_config,build_animation,build_animation_tree,setup_audio_buses,scene_batch,create_scene,add_node,instantiate_scene,configure_node,configure_control,attach_script,connect_signal,disconnect_signal,remove_node,reparent_node,reorder_node,load_sprite,build_sprite_frames,save_scene,export_mesh_library,get_uid,resave_resources,check_project,inspect_tilemap,inspect_image,inspect_audio,draw_image,process_image,run_gdscript,add_export_preset, orhelp. Navigation-mesh baking is aresource_batchbake_navmeshaction; global shader uniforms areproject_batchset_shader_globalactions. - Every
helpexample is executed on every test run against one small coherent project (the catalog's_example_worldnote describes it:scenes/main.tscnwithroot/Player,scenes/menu.tscnwithroot/Panel/StartButton,scenes/kit.tscn,scenes/dungeon.tscn, …), so the printedcommandruns as written once those files exist.connect_signalrefuses a method the target's script does not declare — putattach_scriptbefore it in the samescene_batch;bake_csgis the one scene operation that is not ascene_batchaction;gltf_exportfails on a scene with no 3D meshes. - Run
help '{}'to list every operation with a one-line summary, andhelp '{"op":"add_node"}'for one operation's parameters — each with(required)/(default: X)and its value shape — plus a runnableexample,notes, andcommand, the complete shell line with the example inlined. A batch operation also prints one schema peractions[*].type. Add"format":"text"for a compact plain-text rendering. - An unknown operation or parameter name is rejected with the nearest valid names and the
helpcommand that prints the right ones (Unknown operation: scene_bacth (did you mean scene_batch, save_scene?). Run: help '{}' to list operations), so a misspelling never turns into a silent no-op. - Every dispatcher operation exits
0on success and1after any logged error, so shell callers can gate on the exit code. - Pass parameters as a single JSON object using the snake_case field names expected by the bundled GDScript. A key the operation does not read is rejected with the nearest valid names suggested (
Unknown parameter for add_node: parent_path (did you mean parent_node_path, ...)), and nothing is written — an unrecognised key used to be ignored silently, so the operation fell back to its default and still reported success. The same check applies to each entry of a batchactionsarray; the free-form value dictionaries inside a parameter (properties,constants,colors, …) are never checked, since those keys are project data.--skip-param-checkafter the JSON disables the check. - A key that belongs to a different operation or batch action is rejected too (
Misplaced parameter for scene_batch action "configure_node": actions[0].script_properties is not read by it (it belongs to: attach_script); did you mean properties?). It used to be accepted, ignored, and reported as saved. - The dispatcher covers file, scene, and static-validation operations. It does not run gameplay or export builds: use the runtime runner (
scripts/debug/run_project.py) to run and capture debugger errors, and the export wrapper (scripts/export/export_project.py) for builds.
Run And Capture Debugger Errors
python3 /absolute/path/to/godot/scripts/debug/run_project.py \
/absolute/path/to/project scenes/main.tscn \
--quit-after 120 --timeout 60
- Runs the project headlessly for a bounded number of frames, captures the exact stdout/stderr the Godot debugger prints, and returns JSON:
ok,counts, and adiagnosticsarray where each entry hasseverity,category,message,file,line,function,stack, and asuggested_fix. - The scene is the optional second positional argument (
scenes/main.tscnabove, orres://scenes/level.tscn): it runs and debugs just that scene. Drop it to boot the project'srun/main_scene, which is what a player's launch does — a project with no main scene then has nothing to run and sits there until--timeout. Add--no-headlesswhen an error only appears with real rendering,--log-file <path>to persist the raw log, and--no-warningsto drop warnings. - Two kinds of engine noise are downgraded to severity
infoand never count as an error or flipok.exit_leak: shutdown bookkeeping —N resources still in use at exit,ObjectDB instances were leaked at exit— which looping music still playing when--quit-afterfires produces on every run.host_capability: a display or audio driver's own failed probe on a machine with no GPU or no sound card (a container, a CI runner, a box you SSH into) —Required Vulkan instance extension VK_KHR_surface not foundand theCondition … is trueassertions that follow it. Nothing in the project causes those and nothing in the project can fix them; the twoswitching to OpenGL 3/falling back to the dummy driverlines stay warnings, because the fallback swaps the renderer under you:ProjectSettings.get_setting("rendering/renderer/rendering_method")still reportsforward_plusthere, and onlyRenderingServer.get_current_rendering_method()tells you what you actually got. - Runs Godot with
-d --ignore-error-breaks. GDScript warnings (unused variable, shadowed variable, integer division, standalone expression, …) reach stdout only through the script debugger channel, so a plain headless run reports none of what the editor's Errors panel shows;--ignore-error-breakskeeps-dfrom stopping at an interactivedebug>prompt on the first error. Pass--no-debuggerto opt out. Seereferences/debugging.mdfor the full run→diagnose→fix→re-run loop and a message→cause→fix table.
Validate Scripts And Scenes Without Running
python3 /absolute/path/to/godot/scripts/debug/lint_project.py /absolute/path/to/project --pretty
- Run the linter first: it needs no Godot, and its diagnostics carry the fix (
KinematicBody2D → CharacterBody2D; set velocity then call move_and_slide() with no arguments,Panel has children: Title, Icon). Categories:godot3_api,godot3_shader,inference,node_ref,unique_name,signal_target,missing_resource, and the cross-reference passinput_action,group_ref,res_path,animation_ref— names that resolve to nothing, which Godot never reports: an action no[input]entry, built-inui_*orInputMap.add_actiondefines (error, with the defined actions, the nearest name, and theproject_batchcommand that creates it), a group nothing ever joins, ares://literal that is not on disk (case-sensitively, so a macOS pass means a Linux pass), an animation the node'sSpriteFrames/AnimationLibrarydoes not hold, and Godot 3 shader names (hint_color,SCREEN_TEXTURE,WORLD_MATRIX, …) with their verified 4.x replacement. Silence one line with# lint:ignore <category>on it or on the line above (;in.tscn,//in.gdshader); unused suppressions are listed undersuppressions.unusedand never fail the run. A folder holding a.gdignoreis skipped, as Godot skips it.--only cat1,cat2narrows,--warnings-as-errorsmakes warnings fail the run,--path subdirscopes it. Output is the same{ok, counts, diagnostics[]}shape as the runtime parser.validate_project.pyruns the same lint pass first and merges its findings (--no-lintopts out).
godot --headless --debug --ignore-error-breaks --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
check_project '{}' 2>&1 \
| python3 /absolute/path/to/godot/scripts/debug/godot_log_parser.py -
check_projectstatically loads every GDScript, scene, shader, resource, GDExtension, and editor plugin (or just a{"project_path":"subdir"}subtree) and prints a JSON summary of failures. Piping its combined output throughgodot_log_parser.pyyields line-level diagnostics. Keep--debug --ignore-error-breakson the command: because this loads every file, it is the pass that reports the warnings of every script, and without it Godot emits no warnings at all.- Scenes are instantiated as well as loaded (
{"instantiate": false}opts out).load()accepts every broken node hierarchy; onlyPackedScene.instantiate()returns null on a root that carriesparent=or a non-root node with noparent=(ERROR: Invalid scene: …, reported infailed[]), and only instantiating prints theWARNING: Parent path … has vanisheda mistypedparent=produces. A fully flat tree is still valid and silent — onlyinspect_scenecatches it. Instantiating runs each scene root script's_init()and its stored-property setters, not_ready; autoloads are available, so that is not a source of false failures. - The same pass reports node configuration warnings — the editor's yellow triangles, re-derived because
Node.get_configuration_warnings()is not callable from a script: a body with no shape, aCollisionShape2Dwith noshape, anAnimatedSprite2Dwith no frames, particles with no material, aPathFollow2Dunder the wrong parent, aScrollContainerwith two children, a 3D scene with no camera or light (hint). Each line isWARNING: [node_config:<rule>] <scene>::<node_path>: <message>followed byfix:and a runnable dispatcher call; the payload carriesconfig_warnings[],config_warning_count, and a project-widephysics_layerssurvey whosemask_targets_empty_layerfinding is the usual reason a collision never fires.{"config_warnings": false}/{"physics_layers": false}opt out;inspect_scenetakes"config_warnings": truefor one scene. Seereferences/node_config.md. - A
failed_countof 0 is not by itself a pass. Godot degrades gracefully where the editor is fatal (a scene with a missing[ext_resource]still loads and instantiates), so read the parsed diagnostics too. - Shaders are compiled, not just loaded:
check_projectassigns each.gdshaderto aShaderMaterialto force the compile, becauseload()alone accepts a file full of syntax errors. A shader that does not compile is afailed[]entry with its line and message, and the op exits 1 — at draw time the engine would silently fall back to the default material. The resultingSHADER ERROR:carries no path of its own, so the op prints aCompiling shader: <path>marker thatgodot_log_parser.pyuses to attribute it — keep the two on the same captured stream. - Run
python3 scripts/debug/validate_project.py /absolute/project --prettyfor the comprehensive pass: it runscheck_projectwith the debugger attached and scene instantiation on, parses the captured log intocounts/diagnostics, refuses to reportokwhile any error-level diagnostic is present, and builds C# solutions when a.csprojexists. Add--warnings-as-errorsto make warnings fail the run (this is what turns a vanished-parent warning into a failure);--no-instantiateskips the instantiate pass. - Use
godot_log_parser.pyon its own to structure any Godot log you already have:python3 /absolute/path/to/godot/scripts/debug/godot_log_parser.py path/to/run.log. - Run every script you generate or edit through this pass before finishing. Parse errors are the most common cause of a project that will not boot, and
references/gdscript_conventions.mdplus the table inreferences/debugging.mdmap each message Godot emits to its cause and fix. - After adding any script with a
class_name, rungodot --headless --path /absolute/path/to/project --importbefore validating. Global class names resolve from.godot/global_script_class_cache.cfg, which a--scriptrun does not rebuild — until then every other script referencing the new class fails withParse Error: Identifier "Foo" not declared in the current scope.
Probe And Run Deterministic Scenarios
python3 /absolute/path/to/godot/scripts/debug/probe_environment.py /absolute/path/to/project --pretty
A scenario is a JSON file you write next to the project and hand to the runner:
mkdir -p /absolute/path/to/project/scenarios
cat > /absolute/path/to/project/scenarios/boot_check.json <<'JSON'
{
"scene_path": "res://scenes/existing_ui.tscn",
"viewport_size": {"width": 320, "height": 180},
"settle_frames": 4,
"steps": [
{"type": "dump_tree", "properties": ["text"], "label": "tree"},
{"type": "assert", "assertion": "property", "node_path": "StatusLabel",
"property": "text", "expected": "Pending"},
{"type": "ui_report", "ascii": true, "fail_on": ["zero_size", "offscreen"]}
]
}
JSON
python3 /absolute/path/to/godot/scripts/debug/run_scenario.py /absolute/path/to/project \
/absolute/path/to/project/scenarios/boot_check.json --pretty
- Use the environment probe before platform or native-code work to report the Godot version, CLI capabilities, export templates, host tools, and project languages/extensions/plugins.
- Use
run_scenario.pyfor action/key/mouse/joypad input, NodePath property assertions, log assertions,dump_treenode-tree dumps,ui_reporttext UI dumps, Viewport PNG capture, and performance thresholds. It selects rendered mode automatically when screenshots are present and stays headless otherwise. Screenshot steps also return a textsummaryof the PNG (blank / opaque ratio / content bbox / dominant colours) and acceptexpectgates such as{"not_blank": true, "min_opaque_ratio": 0.1, "max_diff_ratio": 0.02, "compare_to": "res://ref.png"}, so a caller that cannot look at images still verifies the render. - Start any scenario against an unfamiliar scene with a
dump_treestep — it prints the indented node tree with the properties you name and returnstree_dumps[], which is where the exactnode_pathvalues for laterassertsteps come from. Pass"ascii": trueto aui_reportstep for a character map of the laid-out screen (named boxes, children drawn over parents) when the rect list alone does not show where controls sit. - An
actionstep only moves polled input state (Input.is_action_pressed); it never fires_input/_unhandled_input/_gui_input, so pause menus, dialog advance, and interact prompts need akeystep. Built-inui_*actions matchkeycode; actions this skill creates matchphysical_keycode.wait_framescounts process frames, which run far faster than the physics tick headless — usewait_secondsorwait_untilfor anything driven by gravity,move_and_slide, or a Tween. - Add a
spatial_reportstep to read the game world as text: every 2D node's world rect and whether the active camera sees it, every 3D node's AABB,in_frustumandscreen_pos, plus a top-down ASCII map ("ascii": true).{"type": "spatial_report", "expect_on_screen": ["Player"], "fail_on": ["not_on_screen", "embedded_in_static"]}catches a player spawned outside the view or inside the floor;"fail_on": ["no_camera_3d", "no_light_3d", "not_in_frustum", "behind_camera"]covers the four reasons a 3D scene renders an empty frame with no error. Headless, no screenshot. Seereferences/spatial_verification.md. - Add a
ui_reportstep to read the laid-out UI as text: every visible Control's path, class, and post-layout global rect, plusfindingsfor zero-sized, fully offscreen, and overlapping siblings."fail_on": ["overlap", "zero_size", "offscreen"]turns a broken layout into a failed scenario — the reliable way to catch UI where every control stacks at (0, 0). It resolves headless and never forces a rendered window. - When you cannot look at images, do not rely on screenshots: instrument with
log_markerplus targeted prints, run the session with--log-file, dumpui_reportat each moment that matters, assert NodePath properties, and parse the log withscripts/debug/godot_log_parser.py. For image files,inspect_imagereports size, blankness, opaque ratio, content bbox, palette size, and an ASCII rendering, and itsexpectblock turns those into a pass/fail exit code. For levels,inspect_tilemap '{"scene_path":"scenes/level.tscn","format":"text"}'prints the paintedTileMapLayerorGridMapas rows you can compare with what you meant to paint. See "Verification Without Vision" inreferences/automation_api.md. - A scenario also fails on any error-level line in its log (
SCRIPT ERROR, parse error, engineERROR:) even when every assertion passed — the result carries the parseddiagnosticsandcounts. When the error is the point of the scenario, require it with alog_assertionsentry (min_count≥ 1) or set the top-level"log_errors": "allow". - The scenario's top-level keys are
scene_path,viewport_size,settle_frames,steps,assertions,log_assertions,log_errors,performance_frames,performance_assertions(plus free-formname/description/comment/notes); any other key —sceneforscene_path— fails the run and names these. - Read
references/automation_api.mdfor the scenario and assertion schema.
Smoke-Run Every Scene, And Unit Tests
python3 /absolute/path/to/godot/scripts/debug/smoke_scenes.py /absolute/path/to/project --seconds 2 --jobs 4 --pretty
The unit-test runner installs the bundled framework on first use, then runs it:
python3 /absolute/path/to/godot/scripts/test/run_tests.py /absolute/path/to/project --init-mini
python3 /absolute/path/to/godot/scripts/test/run_tests.py /absolute/path/to/project --pretty
smoke_scenes.pyboots every.tscnin its own Godot process for--secondsof game time (--fixed-fpsmakes that nearly free: 13 scenes × 3 s in about a second) and reports per scene{ok, diagnostics[], findings[], perf{}}. It reaches whatrun_project.py(main scene only) andcheck_project(never runs_ready/_process) cannot: the pause menu's_ready, level 3, the game-over screen.--fuzz --fuzz-mouse --fuzz-seed 7replays the project's own InputMap actions and clicks visibleButtons through real input events; the same seed replays the same events. It is what findsFunction blocked during in/out signal-class bugs no scripted scenario hits. Findings beyond the log:node_growth(a spawner that never frees),orphan_nodes,timed_out,crashed, and informationalscene_changed/quit_called.run_tests.pyauto-detects GUT, GdUnit4, then the bundled zero-install runner.--init-minicopiesres://tests/test_case.gdplus an example suite into the project; write suites asextends "res://tests/test_case.gd"withfunc test_*()methods (assert_eq,watch_signals+assert_signal_emitted,add_scene,await wait_physics_frames(45),press_action). A GDScript runtime error aborts a test function without raising, so the runner brackets each test and reports it aserrorwith the engine's file:line; a test with no assertion isrisky, a hung onetimeout, and running zero tests is never a pass. Seereferences/testing.md.
Make Art And Sound
godot --headless --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
draw_image '{"output_path":"art/coin.png","palette":{"o":"#1a1c2c","y":"#ffcd75","w":"#f4f4f4"},"rows":[".oooo.","oywyyo","oyyyyo","oyyyyo","oyyyyo",".oooo."],"scale":1}'
python3 /absolute/path/to/godot/scripts/assets/make_sfx.py --preset coin --out /absolute/path/to/project/audio/
python3 /absolute/path/to/godot/scripts/assets/make_music.py --preset overworld --out /absolute/path/to/project/audio/
draw_imagewrites a PNG from ASCII rows (one character, one pixel) or orderedshapes;framespacks a spritesheet and returns thegridbuild_sprite_framestakes;mirror_x,outline,palette_name(pico8,sweetie16,db16,db32,endesga32,gameboy,nes, …) andtile_checkdo the rest. The payload reads the written file back as the same characters.process_imageruns an ordered pipeline over one image, a list or a directory —pixelate+quantize+alpha_thresholdturns soft generated "pixel art" into a true grid with a fixed palette. Any error writes nothing. Seereferences/pixel_art.md.make_sfx.py --list-presetsshows the presets (coin, jump, laser, explosion, hit, powerup, blip, confirm, cancel, footstep, …) and their parameters;make_music.py --preset menu|overworld|battle|victoryrenders a seamlessly looping WAV from a JSON song you can edit. Both print anexpectblock: paste it intoinspect_audio '{"audio_path":"audio/coin.wav","expect":{…}}'and a silent, clipped or wrong-length sound exits 1. Looping needsset_import_options{"edit/loop_mode": 2}plus a re-import. Seereferences/audio.md.- Everything written has no
.importsidecar yet: rungodot --headless --path /absolute/path/to/project --importbefore a.tscn/.trespoints at it.
Project Export Through The Wrapper
godot --headless --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
add_export_preset '{"platform": "windows"}'
That writes the preset under the platform's conventional name — Windows Desktop, Web, macOS, Linux — which is the name the wrapper takes:
python3 /absolute/path/to/godot/scripts/export/export_project.py \
/absolute/path/to/project \
"Windows Desktop" \
/absolute/build/windows/game.exe
- The wrapper resolves absolute paths, creates the output directory, and shells out to
godot --headless --path ... --export-release .... - After a run that exits
0it verifies an artifact actually exists at the output path and fails otherwise. Godot's exporter has reported success while writing nothing (a missing template variant, an unwritable target), so the exit code alone is not proof of a build. - No preset yet? Create one without the editor:
add_export_preset '{"platform":"web"}'(web,windows,linux,macosare verified by real exports;android/iosare written and come back"verified": falsewith the missing prerequisites). It editsexport_presets.cfgas text, so presets you did not name keep their exact bytes. Every preflight blocker names its fix on stderr and infixes[]. - Test a Web build with
python3 /absolute/path/to/godot/scripts/export/serve_web.py /absolute/build/web --check(cross-origin isolation headers,application/wasm); never open one fromfile://. Boot a native artifact to prove it runs:Game.app/Contents/MacOS/<Name> --headless --quit-after 20— a build with norun/main_scenehangs instead of erroring. - Run with
--preflight-onlybefore changing or executing a preset. Real exports preflight by default; use--skip-preflightonly after independently verifying the environment. - Pass
--mode debugfor smoke builds,--mode packfor a.pck/ZIP data export, or--mode patch --patches base.pckfor a changed-files patch. - Platform support comes from the preset name already defined in
export_presets.cfg. Common platforms include Android, iOS, Web, Windows Desktop, Linux, macOS, dedicated server presets, and visionOS.
Follow The Main Workflows
Design Or Refactor Architecture
- Identify the bounded context or feature slice before moving files. Name it by business capability, not by scene title or widget name.
- Inspect the current scripting language, autoloads, scene tree, and feature directories before proposing a new structure. Adapt to an existing architecture when one is already coherent.
- In Godot projects, treat scene scripts, input handlers, and UI binding code as
Controller-edge work; keep domain state inModeltypes or state-owning services; keep reusable workflows inSystem; and keep persistence, HTTP, SDK, serialization, clocks, or platform adapters inUtility. - For non-trivial features, put writes behind explicit commands or command-like use cases and keep complex reads in queries or read services instead of bloating node scripts.
- Prefer typed signals, events, or observable state for upward notification after state changes. Do not use global singletons as catch-all mutable state bags.
- Read
references/architecture_qframework_lite.mdfor the responsibility map and review rules, thenreferences/architecture_templates.mdfor Godot-oriented skeletons and folder layouts when you need concrete scaffolding. - After refactors, run the project or the deepest available smoke test and confirm scene scripts, autoload wiring, and feature entrypoints still load cleanly.
Build Or Modify A Scene
- Prefer
scene_batchfor multi-step work so the scene loads once, actions run in memory, and the scene saves only if every action succeeds. - Use
create_scenewhen you only need a root scene, then follow with standalone operations if batching is unnecessary. - Use
add_nodeorinstantiate_sceneto build structure,configure_nodefor general properties and metadata,configure_controlforControllayout and theme overrides, andattach_scriptplusconnect_signalto finish behavior wiring. - For
Controlwork, readreferences/game_ui.mdand build the layout out of containers before touching anchors. A bareControldoes not position its children, so absolutely-positioned siblings all resolve to(0, 0)and overlap — that single mistake is the most common cause of broken game UI. - Keep
load_spritefor compatibility, but preferconfigure_nodefor directtextureassignment on sprite-compatible nodes. - Run the project after non-trivial edits instead of assuming the scene still loads. If any part of the scene was hand-written as text, also run
inspect_sceneand confirm the reported nodepaths nest as intended —check_projectand a clean boot both pass on a silently flattened hierarchy (seereferences/tscn_format.md). - After any functional change, actively open the game and exercise the changed feature or flow instead of stopping at a successful boot. Check whether the behavior matches the request, whether the UI or gameplay state updates correctly, and whether obvious regressions or bugs appear.
- After the validation run, inspect the logs for
errorandwarningoutput. If the feature misbehaves or either log level appears, treat the task as unfinished, fix the issue, and rerun until the behavior and logs are both clean.
Add Art, Music, Or Story
- Reuse the project's existing art pipeline, palette, sprite sizing, font choices, audio buses, dialogue system, localization format, and cutscene structure before inventing new conventions.
- If the request does not specify a style and the project does not already establish one, default new visual content to pixel art. Keep sprites, tiles, UI embellishments, VFX, and promo-style mockups coherent with that direction.
- For generated sprite-like art, use the
imagegenskill first and request transparentpngoutput. With no image generator, draw it:draw_imagetakes ASCII rows and shapes and is enough for sprites, frame sheets, tilesets, icons and 9-patch panels (references/pixel_art.md). If transparent output is unavailable or unreliable, fall back to a flat chroma-key background and remove it withprocess_image(remove_background, thentrim;pixelate+quantizewhen the result is soft or off-grid) —scripts/assets/chroma_key_cutout.pyis the Pillow/NumPy alternative. - Use pure green
#00FF00as the default chroma-key background so AI-generated assets are easier to cut out locally. If the subject itself contains strong green regions, switch to pure magenta#FF00FF. Avoid gradients, shadows, white, or black backgrounds when the next step is Python cutout. - After local cutout or repair work, write the final PNG frames back into the Godot project tree and reference them with project-relative paths such as
res://textures/...before callingload_spriteorbuild_sprite_frames. - Check every generated or cut-out image with
inspect_imagebefore wiring it up:{"image_path": "textures/hero.png", "expect": {"not_blank": true, "has_alpha": true}}catches an empty generation and a chroma-key pass that did nothing, and{"image_paths": ["textures/hero_idle"], "expect": {"frames_consistent": true}}catches a frame sequence that changed canvas size beforebuild_sprite_framesbakes it in. - For music and SFX, prefer integrating provided or separately generated audio through the project's existing import settings, audio buses, and player nodes instead of inventing a new audio pipeline. When no audio was provided, do not ship a silent game:
scripts/assets/make_sfx.pyandscripts/assets/make_music.pysynthesize effects and a looping track, andinspect_audiowith theexpectblock they print is how you confirm a sound you cannot hear (references/audio.md). - Implement story content through the project's existing dialogue, quest, event, or cutscene data flow when possible. This skill guides integration, not open-ended narrative system generation. If none exists, add only the smallest linear dialogue or event path needed to make the request playable unless the user explicitly asks for a larger system.
- Default frame animation delivery to independent frame files instead of sprite sheets so frames are easier to cut out, repair, and validate individually.
- Wire new assets and narrative content into a reachable gameplay path instead of leaving them as unused files in the repository.
- Run the game and verify that visual assets render at the intended scale, music and SFX trigger correctly, and story content is reachable and readable without runtime errors. When you cannot look at the running game,
inspect_imageon the source art plus a scenarioscreenshotstep withexpectis the substitute.
Run And Debug
- Use host-native runtime tools such as
run_project,get_debug_output, orstop_projectwhen the host agent exposes them. Otherwise use the bundled runner to capture the debugger's errors as structured diagnostics:python3 /absolute/path/to/godot/scripts/debug/run_project.py /absolute/path/to/project --quit-after 120 --timeout 60. - Read the returned
diagnosticsand fix in order: parse errors first, then resource/load errors, then runtime script errors, then warnings — a single parse error usually cascades into several later errors. For each entry, openfileatline, usefunction/stackfor context, and apply the fix indicated bycategory/suggested_fix(details and a message→cause→fix table are inreferences/debugging.md). - When the host exposes input, browser, window, screenshot, or desktop automation tools, use them to interact with the running game so you can verify the changed feature in a live session instead of relying only on static inspection.
- Re-run the same command after fixing and confirm
"ok": truewithcounts.errors == 0andcounts.parse_errors == 0. Do not assume the fix worked — the runner is the check. A"timed_out": trueresult is itself a finding (a hang or infinite loop). - Boot every scene, not only the main one:
python3 /absolute/path/to/godot/scripts/debug/smoke_scenes.py /absolute/path/to/project --seconds 2 --jobs 4 --fuzzruns each.tscnin its own process with seeded input and reports errors, hangs, leaks and crashes per scene. - Do not stop at a successful launch. Verify the implemented behavior in the running game, then read the diagnostics from the validation run and fix any reported
errororwarningbefore you finish. For a fast whole-project sanity pass without running gameplay, use thecheck_projectoperation to load every script and scene and surface parse/load failures; widen coverage for code paths a short boot never reaches by running the specific scene or raising--quit-after. - Launch Godot with
-d --ignore-error-breaks, never-don its own: without-dthe engine reports no GDScript warnings at all, and without--ignore-error-breaksthe local debugger stops at an interactivedebug>prompt on the first error (the bundled runners already pass both and redirect stdin from/dev/null). If a full interactive test is not possible in the current environment, still launch the project when feasible, perform the deepest smoke test available, and state exactly what you could not verify.
Prepare And Export Builds
- Read
export_presets.cfg,project.godot, and any existing CI scripts before editing build settings. Preserve the project's preset names and signing flow whenever possible. - Confirm that the local Godot version matches the project's export templates and that the required platform SDKs or certificates are already configured for the target preset.
- Prefer the bundled wrapper at
scripts/export/export_project.pyfor repeatable CLI exports, and use--mode debugbefore--mode releasewhen you need a quick device, browser, or desktop smoke test. - Keep export outputs outside the project root unless the repository already stores them in a known build directory.
- When the user asks for Android, iOS, Web, Windows, Linux, macOS, dedicated server, or visionOS builds, read
references/export_targets.mdfor the platform-specific checklist before changing presets or signing settings. - Do not hand-write
export_presets.cfg. Patch the existing preset file, or create a preset headlessly withadd_export_preset '{"platform":"web"}', whose per-platform option blocks were derived from real exports on 4.7.
Use The Specialized Operations
-
Use
scene_batchas the default scene editing entrypoint for script and UI work. -
Use
configure_controlwhen aControlnode needs presets, anchors, offsets, size flags, minimum size, or theme overrides. -
Use
attach_script,connect_signal, anddisconnect_signalto wire scene logic without hand-editing.tscnfiles. When a host cannot run the dispatcher at all, followreferences/tscn_format.mdfor the text format and its mandatory verification loop. -
Use
remove_node,reparent_node, andreorder_nodeto refactor hierarchy after the scene already exists. -
Use
build_sprite_framesto turn a frame directory or explicit frame list into aSpriteFramesresource on anAnimatedSprite2D. -
Use
export_mesh_libraryto build aMeshLibraryfrom a 3D scene forGridMap. -
Use
get_uidto inspect a resource UID sidecar and any engine-reported UID metadata when a project uses.uidfiles. -
Use
resave_resourcesor the server's equivalent project-wide resave operation to attempt.uidsidecar regeneration, then verify the reported created and still-missing counts instead of assuming every resave produced a UID. -
Use
run_project.pyto run the project and capture the debugger's runtime errors as structured diagnostics, andcheck_projectto validate that every script, scene, shader, and resource compiles, loads, and — for scenes — instantiates. -
Use
inspect_project,inspect_scene, andinspect_resourcebefore editing unfamiliar projects; request full file lists or property schemas only when needed. -
Use
resource_batchfor transactional Resource creation, duplication, property/indexed-property updates, metadata, builder-method calls (call_methodfor APIs likeGradient.add_point,Theme.set_color,Animation.add_track,TileSet.add_source), and save. It creates engine resources withresource_typeand custom ones with"script": "res://items/item_data.gd"(aclass_name X extends Resource);duplicate_fromkeeps the script.inspect_resourcereportsscript_path,script_class, andscript_propertiesfor script-backed.tresfiles, so a custom resource's real type and accepted properties are verifiable as text. -
Use
project_batchfor structured ProjectSettings, InputMap, autoload, layer-name, main-scene, and translation edits. -
Use
audit_importsorscripts/import/import_project.pyto detect missing, invalid, stale, and orphaned import artifacts and optionally reimport first. -
Use
validate_project.pyfor GDScript/C#/GDExtension/plugin validation, andrun_scenario.pyfor deterministic behavior, text UI-layout (ui_report), screenshot, log, and performance checks. -
Use
build_tilesetto author aTileSet(atlas sources, exposed tiles, per-tile collision/custom-data/terrains viatile_defaults) andpaint_tilemapto assign it and paint/fill/erase cells or runterrain_fillsautotiling on aTileMapLayer. Tiles without collision polygons are decorative — pass"collision": "full_cell"for solid ground. -
Use
paint_gridmapto paint aGridMap(the 3D parallel topaint_tilemap) from aMeshLibrarybuilt withexport_mesh_library. -
Design levels as ASCII.
paint_tilemaptakes"ascii_map": {"legend": {"#": {"source_id": 0, "atlas_coords": {"x": 0, "y": 0}}, ".": null}, "rows": ["#####", "#...#", "#####"], "origin": {"x": 0, "y": 0}}andpaint_gridmaptakes"ascii_layers": [{"y": 0, "rows": [...]}]with the samelegend. Rows are top-to-bottom (y, or z for GridMap), characters left-to-right (x);.and space leave a cell untouched ("erase_unlisted": trueclears them instead). An unknown legend character aborts the paint and leaves the scene on disk untouched, so a typo can never half-paint a level. Write the map as a picture instead of a coordinate list — a wrong coordinate list looks fine, a wrong picture does not. -
Use
inspect_tilemapto read aTileMapLayerorGridMapback as rows ("format":"text") or as JSON with alegendyou can paste straight back into a paint call; it auto-picks the node whennode_pathis omitted. A terrain fill that matches no tile paints nothing and now warns ([WARN] ... left N of M cells empty) — give the tiles peering bits inbuild_tilesetor paintsource_id+atlas_coordsdirectly. -
Use
inspect_imageto read any PNG as numbers (size,blank,opaque_ratio,content_bbox,dominant_colors,unique_colors, quadrant occupancy) and optionally as ASCII, and gate it withexpect(not_blank,has_alpha,max_unique_colors,width/height,compare_to+max_diff_ratio,frames_consistentoverimage_paths). JPEG input getsbackground_tolerance0.12 by default so codec ringing stays out ofcontent_bbox; pass it explicitly for lossy WebP. -
Use
build_themeto author aThemegrouped by control type (inline styleboxes, hex or typed colors, type variations); wire it project-wide withproject_batchset_settingongui/theme/custom. Author every interactive state (normal/hover/pressed/focus/disabled), not justnormal; seereferences/game_ui.md. -
Use
bake_collisionto generate aStaticBody3D/CollisionShape3Dfrom aMeshInstance3D(trimesh/convex/multi_convex),collision_from_spriteto traceCollisionPolygon2Dshapes from a sprite's alpha, andbake_csgto freeze aCSGShape3Dtree into a static mesh (+ optional collision, optional in-place replacement). -
Use
resource_batchbake_navmeshto bake aNavigationPolygon(2D) orNavigationMesh(3D) from procedural outlines/faces (sync bake, headless-safe), then assign it to aNavigationRegion2D/3Dwithconfigure_node. -
Use
gltf_exportto write a.glb/.gltffrom an edited scene,project_batchset_shader_globalto author[shader_globals]uniforms, andbuild_replication_configto author aSceneReplicationConfigfor aMultiplayerSynchronizer. -
Use
build_animation_treefor AnimationPlayer-backed state machines (states, transitions, auto Start wiring), andset_import_optionsto patch.importparams such as audio loop modes before a reimport. -
Use
build_sprite_frameswithspritesheet+grid+animationsto slice an atlas into multiple named animations with per-frame durations; the legacy one-file-per-frame form still works. -
Use
build_animationfor AnimationPlayer keyframe clips (value/method/bezier tracks) saved standalone and/or attached through anAnimationLibrary. -
Use
setup_audio_busesto create the Master/Music/SFX routing, save theAudioBusLayout, and register it in project settings. -
Use
scripts/test/run_tests.pyto run a project's GUT or GdUnit4 suite headlessly with normalized exit codes; Godot has no built-in project test runner. -
It refuses to run when the resolved tests directory holds no test scripts, and reports
test_script_count. Both frameworks exit0when they collect nothing, so a wrong--tests-dirotherwise reads as a full pass. Pass--allow-emptywhen an empty suite is genuinely expected. -
Use
scripts/docs/api_lookup.pybefore calling any engine API you have not just read, andrun_gdscript(code,expression, orscript_path+method+args) when no operation fits or a function is worth one quick call: it runs inside the project with autoloads loaded, maps a parse error back to your snippet's line, and cannot report a crashed snippet as success. It is not a sandbox. -
Use
draw_image/process_imagefor art,make_sfx.py/make_music.pyfor sound,inspect_image/inspect_audioto verify either as text. -
Use
add_export_presetto create an export preset without the editor,scripts/project/scaffold_project.pyto create a project,scripts/project/move_resource.pyfor every move or rename,scripts/debug/smoke_scenes.pyto boot every scene. -
Use the scenario step
spatial_reportnext toui_reportanddump_tree:ui_reportis the UI's layout,spatial_reportthe world's.
Typed JSON Values
- Use plain JSON scalars, arrays, and objects for ordinary values.
- Use
{"__resource":"res://path/to/resource"}to load a Godot resource before assignment. - Use
{"__resource_type":"InputEventKey","properties":{"physical_keycode":32}}to instantiate and configure a Resource value. The resource's own properties go under"properties": a key written next to__resource_type({"__resource_type":"RectangleShape2D","size":…}) is an error — it used to build a default-sized shape and report success. Add an ordered"method_calls":[{"method":"add_point","args":[...]}]for builder-only sub-resources. - Use
{"__script":"res://items/item_data.gd","properties":{...}}to build an instance of a project's ownclass_name X extends Resource—__resource_typeonly reaches engine classes. Sameproperties/method_callskeys, nests anywhere a resource value is accepted (includingattach_script.script_properties). A misspelled or wrongly typed export fails the op and names the script's exported properties; untyped JSON arrays are converted intoArray[T]exports (a rawset()silently drops them). - Use
{"__curve":{"points":[{"x":0,"y":0},{"x":1,"y":1}]}}and{"__gradient":{"points":[{"offset":0,"color":"#fff"},{"offset":1,"color":"#000"}]}}to inlineCurve/Gradientramps (e.g. particle scale/color) without separate resource files. - Use typed wrappers for engine value types when the target property is not plain JSON:
{"__type":"Vector2","x":10,"y":20}{"__type":"Color","r":1,"g":0.5,"b":0.25,"a":1}{"__type":"NodePath","value":"root/Button"}
configure_node.properties,configure_node.indexed_properties,attach_script.script_properties,configure_control.theme_overrides, andscene_batch.actions[*]all accept the same typed-value format.- A colour may be
"#rrggbb"/"#rrggbbaa"wherever the target is declaredColor, includingshader_parameter/*uniforms (assignshaderfirst); a string that is not a colour fails the op instead of being stored asnull. - Indexed sub-paths use
:—{"shadow_offset:x": 4.0},{"material:shader_parameter/tint": "#ff0000"}— and are walked before anything is written: a hop that does not exist fails with the likely spelling ("offset/x"→did you mean "offset:x"?,"content_margin/left"→"content_margin_left") instead of silently changing nothing. - An indexed path that reaches into another saved file —
"material:shader_parameter/tint"on a node whose material isres://materials/flash.tres— edits and saves that file too (Also saved res://…), as the editor does; a path into an imported file (texture:…) is refused because nothing could save it. configure_control.theme_overridestakes grouped keys ({"constants": {"separation": 12}}) or flat ones ({"constants/separation": 12}).- The shared codec also supports Rect/Transform/Plane/Quaternion/AABB/Basis/Projection and packed array values. Read
references/automation_api.mdfor the complete surface.
Scene Editing Surface
scene_batch: sequential multi-action transaction withcreate_if_missing,root_node_type,root_node_name,save_path, andactions.create_scene: create and save a root scene with optionalroot_node_name,properties, andindexed_properties. An existingscene_pathis an error unless"overwrite": true— re-running it used to replace the scene with a bare root.add_node: add a new node underparent_node_path, optionally atindex, with typedpropertiesandindexed_properties.instantiate_scene: instance a child scene underparent_node_path, optionally rename it, move it to an index, and apply root properties.configure_node: set regular properties, indexed properties, groups, metadata, andunique_name_in_owneron an existing node.configure_control: configureControlpresets, anchors, offsets,position,size,custom_minimum_size, size flags, stretch ratio, and theme overrides.attach_script: assign a script and then write exported properties.connect_signal: connect a signal to a target node method with persistent connection flags by default and optionalbinds.disconnect_signal: remove persistent scene connections by source node, signal, target node, and method.load_sprite: load an existing texture resource into aSprite2D,Sprite3D, orTextureRect.build_sprite_frames: build or replace animations on anAnimatedSprite2Dfromframes_dir/frame_paths, or from aspritesheet+gridwith ananimationsarray (multiple named animations, per-frameduration), then optionally save theSpriteFramesresource toresource_save_path.paint_tilemap: assign aTileSetand paint/fill/erase cells — or anascii_map— on aTileMapLayernode (also available insidescene_batch).paint_gridmap: assign aMeshLibraryand paint/fill/erase cells (with orientation) — orascii_layers— on aGridMapnode (also available insidescene_batch).inspect_tilemap: dump aTileMapLayerorGridMapas ASCII rows with a paste-backlegend,bounds, and per-tilecounts.inspect_image: describe an image file as numbers and optional ASCII, withexpectgates and animage_pathsframe-sequence mode.help: list every operation, or print one operation's parameter schema, example, notes, and runnable command;{"check_examples":true}validates the catalog.bake_collision: add aStaticBody3D/CollisionShape3Dto aMeshInstance3Dvia trimesh/convex/multi_convex baking (owner-safe serialization; also inscene_batch).collision_from_sprite: traceCollisionPolygon2Dchildren from a sprite's alpha silhouette (also inscene_batch).bake_csg: freeze aCSGShape3Dtree into a staticArrayMesh(+ optional collision), optionally replacing the CSG node with aMeshInstance3D.build_animation: build anAnimationfrom declarative value/method/bezier tracks, save it standalone, and/or register it on anAnimationPlayervia anAnimationLibrary.build_tileset: author aTileSetresource with atlas sources, exposed tiles, and physics/custom-data layers.setup_audio_buses: author the project'sAudioBusLayout(buses, sends, volumes, effects) and register it in project settings.save_scene: load an existing scene fromscene_pathand save it back to the same path or an alternatesave_path; keepnew_pathonly as a compatibility alias for older callers.remove_node,reparent_node,reorder_node: mutate existing hierarchy without rewriting the scene by hand.get_uid: inspectfile_path, returning the.uidsidecar path, whether that sidecar exists, and any engine-reported UID text when available.resave_resources: resave scenes plus.gd,.shader, and.gdshaderresources underproject_path, then report how many.uidsidecars were actually created versus still missing.inspect_audio: describe a.wav/.ogg/.mp3as numbers (duration, peak and RMS dBFS, clipping, silence edges, loop seam, pitch contour) and a one-line ASCII envelope, withexpectgates and anaudio_pathsdirectory mode.draw_image: ASCII rows, named palettes, frames and shape primitives → PNG, read back as the same characters;process_image: orderedoperations(trim,crop,resize,pad,flip_h,flip_v,rotate90,alpha_threshold,replace_color,remove_background,quantize,pixelate,outline,pack_frames,split_sheet,tile,nine_patch_margins).run_gdscript: runcode(the body offunc run(tree: SceneTree) -> Variant, mayawait), oneexpression, orscript_path+method+argsinside the project and return the encoded result.add_export_preset: write or update oneexport_presets.cfgentry forweb,windows,linux,macos,androidorios, preserving every other preset byte for byte.check_project: load every GDScript, scene, shader, resource, GDExtension, and editor plugin underproject_path(defaultres://) and report failures by path, kind, and reason. It also fails a shader that does not compile, and reportsconfig_warnings[](node configuration warnings with runnable fixes) and aphysics_layerssurvey. Scenes are also instantiated, which is what catches an invalid node hierarchy; setinstantiatetofalsefor a load-only pass that runs no project code.
Respect The Bundled Implementation
- Read
scripts/core/dispatcher.gdwhen adding or changing Godot-side operations. - Add scene operations under
scripts/scene/, resource operations underscripts/resource/, project operations underscripts/project/, import helpers underscripts/import/, asset helpers underscripts/assets/, mesh operations underscripts/mesh/, diagnostics/runners underscripts/debug/, shared utilities underscripts/utils/, and codecs/editing helpers underscripts/core/. - When adding an operation, add its
matcharm inscripts/core/dispatcher.gdand an entry inscripts/core/op_examples.json(summary, aparamsschema of only the keys that operation uses,example,notes,see), then runhelp '{"check_examples":true}'— it fails when the schema or the example names a key the operation does not read. Read op parameters only throughparams.get("key", default)/params.has("key")literals: the dispatcher derives the accepted-key list from those. The derivation is a regex over raw source, comments included, across everything the op preloads — so never read an output or parsed dictionary with a quoted-literalget/has(usedict[&"key"]or a helper), never write that pattern in a comment, and keepscripts/core/utils.gd(preloaded by every op) free of it. The catalog's per-op and per-actionparamsare also what the dispatcher uses to reject a key that belongs to another operation, so keep them complete. - List every new python entry point in the catalog's
_toolsarray (script,summary,command);tests/test_help_op.pyfails when ascripts/**/*.pyfile is missing from it. - Keep
templates/shaders/*.gdshadercompiling (tests/test_shader_templates.pyalso proves each effect by pixels) and every playbook literally replayable (tests/test_playbooks_replay.pyextracts the fencedbashblocks and runs them in fresh projects).tests/test_help_examples.pydoes the same for everyhelpexample and for the dispatcher blocks in this file, andtests/test_reference_examples.pyfor every other documented command: - Every fenced
bashblock inSKILL.mdandreferences/*.mdis executed bytests/test_reference_examples.py— onebash -e -o pipefailsubprocess per block, in document order, against a fresh copy of the_example_worldproject. Blocks of one doc share that copy, so a later block may use a file an earlier block wrote; they never share shell variables, because a reader pastes one block at a time. What that asks of the prose:- One block is one thing a reader pastes: the whole command, no
…, no[--flag]grammar (put grammar in prose or a non-bash fence), no placeholder the reader is not told to replace. Output examples go in ajsonortextfence, never in the bash block. - The placeholders are
/absolute/path/to/godot(this skill's root, also spelled/absolute/godot),/absolute/path/to/project(the project, also/absolute/project), and/absolute/<word>for a scratch path beside the project (/absolute/build,/absolute/dest,/absolute/moves.json). Any other/absolute/…fails the test, as does a$VARthe block does not set itself (SKILL,PROJECT,GODOT_SKILL_CACHE,HOMEandPATHare provided). Write the dispatcher path out in full every time:godot --script <path that does not resolve>printsCan't load scriptand exits 0, so a block built around an unset variable passes while running nothing — the harness fails any block whose log says that, whatever its exit code. - A block that needs the example world to hold something builds it first, in an earlier block of the same doc — a
cat > … <<'JSON'heredoc, adraw_imagecall, acreate_scene. - Three directives, each an HTML comment on the line before the fence:
<!-- replay: fails -->when the block exists to show an error message (exit 0 then fails the test),<!-- replay: display -->when it needs a real framebuffer, and<!-- replay: skip — <reason> -->where<reason>starts withexport-templates,external-tool:<name>,network,destructive-outside-projectorinteractive(a server or watcher that runs until the reader stops it). No other skip reason is accepted: rewrite the block so it runs. displayandexport-templatesare conditional: the harness probes the machine (a real framebuffer; this engine's export templates, withprobe_environment.py's own search) and runs the block where it can, so a developer's Mac really exports a web, Windows and macOS build while a bare CI runner prints a SKIP line. A skip reason may add ahost:<darwin|linux|win32>qualifier —<!-- replay: skip — export-templates host:darwin (runs the exported .app) -->— and then the block also has to be on that platform.- While editing one doc:
python3 tests/test_reference_examples.py --doc automation_api.md --continue --keep, or--listto see every block and directive without running anything.
- One block is one thing a reader pastes: the whole command, no
- Keep
templates/gdscript/*.gdwarning-free undercheck_project --debug(tests/test_templates.pyenforces it) and reference every template fromreferences/playbooks.md. - Keep Godot-side parameter names in snake_case when editing these scripts, for example
scene_path,root_node_type,parent_node_path,node_type, andnode_name. - Preserve the current relative import pattern inside the GDScript files so the headless dispatcher keeps working.
Examples
Menu UI In One Batch
godot --headless --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
scene_batch '{
"scene_path":"scenes/menu.tscn",
"create_if_missing":true,
"root_node_type":"Control",
"root_node_name":"Menu",
"actions":[
{"type":"add_node","parent_node_path":"root","node_type":"PanelContainer","node_name":"Panel"},
{"type":"configure_control","node_path":"root/Panel","layout_preset":"FULL_RECT"},
{"type":"add_node","parent_node_path":"root/Panel","node_type":"Button","node_name":"StartButton","properties":{"text":"Start"}},
{"type":"configure_control","node_path":"root/Panel/StartButton","size_flags_horizontal":"EXPAND_FILL","custom_minimum_size":{"__type":"Vector2","x":240,"y":64}}
]
}'
Attach Script And Connect Button Signal
godot --headless --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
scene_batch '{
"scene_path":"scenes/menu.tscn",
"actions":[
{"type":"attach_script","node_path":"root","script_path":"scripts/menu_controller.gd","script_properties":{"menu_title":"Main Menu"}},
{"type":"connect_signal","node_path":"root/Panel/StartButton","signal_name":"pressed","target_node_path":"root","method_name":"_on_start_pressed","binds":["clicked"]}
]
}'
Export A Debug Android Build
python3 /absolute/path/to/godot/scripts/export/export_project.py \
/absolute/path/to/project \
"Android" \
/absolute/build/android/game.apk \
--mode debug
Run And Read The Debugger Errors
python3 /absolute/path/to/godot/scripts/debug/run_project.py \
/absolute/path/to/project scenes/main.tscn \
--quit-after 120 --timeout 60 --pretty
Returns JSON with a diagnostics array; each entry has severity, category, message, file, line, function, stack, and suggested_fix. Open the referenced file:line, apply the fix, then re-run until "ok": true.
Build AnimatedSprite2D Frames From A Directory
godot --headless --path /absolute/path/to/project \
--script /absolute/path/to/godot/scripts/core/dispatcher.gd \
build_sprite_frames '{
"scene_path":"scenes/player.tscn",
"node_path":"root/AnimatedSprite2D",
"frames_dir":"art/player_idle",
"animation_name":"idle",
"fps":12,
"loop":true,
"resource_save_path":"animations/player_idle_frames.tres"
}'
Check Before You Finish
- Confirm that every write target is inside the intended Godot project.
- Confirm that the scene still loads and that the project boots after structural edits.
- Confirm
lint_project.pyreportscounts.errors == 0on the project after your edits, and that any generated controller, manager, or UI script either came fromtemplates/gdscript/or passes the same bar:validate_project.pyreportingcounts.errors == 0andcounts.warnings == 0. - Confirm
validate_project.pyreports zeronode_configdiagnostics (check_project→config_warning_count == 0) and read itsphysics_layers.findings: a body with no shape and a mask that targets an empty layer both run without a single error. - Confirm every scene boots, not only the main one:
smoke_scenes.py PROJECT --seconds 2 --jobs 4 --fuzzexits 0.info-levelexit_leak(music still playing at quit) andhost_capability(no GPU / no sound card on this machine) diagnostics are not failures. - Confirm that the changed feature was exercised in a live run when the environment allowed it, and that the observed behavior matched the request without obvious regressions.
- Confirm gameplay placement as text when you cannot look: a
spatial_reportstep withexpect_on_screenand"fail_on": ["not_on_screen", "embedded_in_static"](2D) or["no_camera_3d", "no_light_3d", "not_in_frustum", "behind_camera"](3D) passes. - Confirm logic you wrote has a unit test that ran:
run_tests.py→"ok": truewithcounts.tests > 0and noriskytest standing in for an assertion. - Confirm that UI work was verified as laid out, not just saved: run
run_scenario'sui_reportstep with{"fail_on": ["any"]}at twoviewport_sizevalues and confirm zerozero_size,offscreen, andoverlapfindings, and confirm onescreenshotstep with{"expect": {"not_blank": true}}passed — a perfectly laid-out Control tree still renders an empty frame when the texture, material, or camera is wrong. Confirm a projectThemeis wired throughgui/theme/customwith a visibly distinctfocusstylebox — an unthemedControltree ships the engine's editor-gray default and reads as a web form, not game UI. - Confirm that any new art, music, or story content matches the requested direction, or the pixel-art default when no direction was provided and no project style overrode it.
- Confirm that generated sprite-like assets used transparent output first, or a flat pure-green chroma-key background plus local cutout as the fallback.
- Confirm every generated or synthesized sound passed
inspect_audiowith theexpectblock its generator printed (not_silent,no_clipping, duration bounds;loopablefor music), and that looping tracks carry theedit/loop_modeimport option. - Confirm that frame animation work landed as a validated frame sequence or
SpriteFramesresource, not an unusable loose asset dump, and thatinspect_imagewithexpectpassed on the frames. - Confirm painted levels by reading them back with
inspect_tilemapand comparing the rows to what you meant to paint — an off-by-oneorigin, a legend character on the wrong tile, or a terrain fill that matched nothing all save cleanly. - Confirm that the final validation run logs contain no
errororwarningoutput. If they do, fix the issue and rerun before finishing. Capture the run withscripts/debug/run_project.py(or the host runtime tools) so the check is on structured diagnostics, not a glance at the log. A boot-and-quit run only reports the files it loaded, so pair it withscripts/debug/validate_project.pyfor whole-project warning coverage. If a run reports zero warnings on a project the editor complains about, the debugger is not attached — check for a stray--no-debugger. - Confirm that every exported artifact came from the intended preset and that the artifact path matches the target platform's existing convention.
- Smoke test at least one exported build for the requested targets instead of assuming the preset is valid.
- Confirm every file you moved or renamed went through
move_resource.pyand that it reportedverify.new_missing == []. - Confirm architecture work did not collapse unrelated responsibilities into a single node script, autoload, or generic manager.
- Prefer incremental scene changes over rewriting
.tscnfiles manually. When a.tscnwas hand-written or hand-edited anyway, confirminspect_scenereports the intended nesting and that every[connection]source/targetmatches a real node path before finishing — both classes of mistake load and boot cleanly (references/tscn_format.md).