About scenario-godot-3d-world
scenario-godot-3d-world is a skill for agents building and auditing Godot 4.7.2 3D levels and environments. Its toolkit covers player movement, blockouts, terrain, asset imports, scattering, and streaming, with code-based checks and captures.
Key features
- 013D player controller and stair tests
- 02CSG blockout and level validation
- 03Terrain and GridMap construction
- 04GLB import and prop audits
- 05Scene scattering and cell streaming
- 06Level audits and profiling
Use cases
- 01Testing player movement across stairs and slopes
- 02Building and checking a level blockout
- 03Preparing terrain and imported props
- 04Profiling streamed or populated scenes
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-godot-3d-worldAfter installing, type/scenario-godot-3d-worldin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/gd_world.pyscripts/agentkit/world/post_import_prop.gdworld_audit.gdworld_blockout.gdworld_common.gdworld_import.gdworld_kit.gdworld_mesh.gdworld_player.gdworld_project.gdworld_scatter.gdworld_stream.gdworld_terrain.gdworld_terrain3d.gdscripts/agentkit/world/runtime/cell_streamer.gdthird_person_controller.gdDetails
From the project READMEGodot 3D world (level and environment artist)
Target: Godot 4.7.2.stable, macOS Apple Silicon, Jolt Physics, Forward+.
REQUIRED BACKGROUND: scenario-godot-expert (channels, gd_run, the review loop, 4.7 traps). If
a sibling skill named here is missing from your available skills, ask the user to install it
(npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and
flag the gap.
Status (2026-10-02): all 14 live test groups in tests/code/godot-3d-world/test_world_live.py
passed (live_20261002-215949.json); L8 and L12 were extended and passed again in round 2
(live_20261002-223935.json). The 11 offline checks pass. Details in
references/procedures.md.
Toolkit: scripts/gd_world.py (runner side) and
scripts/agentkit/world/*.gd (jobs, a post-import script and two runtime
scripts), installed with gd_world.install_world(P).
Stance (the expert delta)
- Zero Y before normalizing the camera-relative direction. GDQuest (JlgZtOFMdfc 00:22:00): otherwise looking down slows the walk. With the order right, the controller kept 6.00 m/s at -60 degrees of pitch.
- CharacterBody3D needs a stairs module, and the step height must come from the tread. Majikayo
(Tb-R3l0SQdc 00:17:01) snaps up with
body_test_motionand checks the tread with a ray, because the capsule touches the leading edge. A stock r 0.35 capsule stops at 0.18 m steps. Taking the height from the capsule's travel let it climb 0.6 m over a 0.45 m limit; the tread ray fixed it. - Guard the snap-down with a floor-below ray. Majikayo (00:14:58): without it, every ledge walk-off snaps down. The guard does not fix steep stairs: keep the rise at or under about 0.25 m for smooth descents.
- Graybox in CSG, then bake it; never animate it. Four Games (gIK02BgCCBI) and PiCode
(S5kRpEKIh38 00:01:51): static CSG costs about what modeled geometry costs, moving CSG is slow,
and the cost follows the operand (a 128-segment sphere cut cost 1,000 times a box cut per move).
CSG needs closed meshes: an open
CSGMesh3Doperand is silently ignored, with no error. - Prefer simple collision for props. Four Games (00:05:37) prefers
-colonlyhand shapes. Trimesh (-col) for static level geometry only, box or convex for props and anything dynamic,-noimpfor reference geometry. - Post-import scripts work outside the tree. Allard (5fDuf2IlizU 00:11:14): set owners, use
local transforms, use
reparent(node, false); set collision layers in the script so they cannot be forgotten (00:15:15). Reproduced: an unowned node is not saved,global_transformreturns identity, defaultreparent()zeroes the position. - Spring arm with a sphere and the player excluded. Octodemy (ZCb12AHKMfE 00:07:40, 00:08:35):
SphereShape3D r 0.3, a
top_levelpivot,add_excluded_object(player). The arm stops one radius before a wall. - AI GLBs are normalized placeholders until a manifest says otherwise. Scenario-delivered Tripo, Meshy and Hitem3D files arrived unit-boxed, pivot centered, double-sided, metallic 1.0, 4K maps, 15k to 1M triangles. Jan (L5emNlNYVfY 00:02:00, vendor) remeshes to 6k to 8k triangles first. Size, pivot, facing, collision and budget come from a manifest the import script applies.
- Terrain3D: check the region layout, prepare the textures, save binary. Tokisan (oV8c9alXVwU
00:20:22): badly placed regions multiply memory. In 1.0.2 a 1,024 m terrain centered on the
origin takes 16 regions of 256 or 4 of 512 (1.05 km² allocated), but 4 of 1,024 (4.19 km²).
Textures seamless, square, power of two, equal sizes, channel-packed (albedo plus height, normal
plus roughness), one compression for all (00:04:33; Four Games ejlD8cM9kk4 00:10:06). Save as
.res, since.tresloads slowly (YtiAI2F6Xkk 00:01:30). - Hand over runnable code, and label measurements. A plan for someone without this kit gives
the GDScript the helper runs (procedures.md has it), not only
gd_world.*calls. Numbers measured here are effect sizes on an M-series Mac: say so, and re-measure on the target.
Disagreements: capsule (GDQuest) or cylinder (Juli, U5A_JArREUc): capsule for ledge tolerance, cylinder for clean wall-run and slide normals. CharacterBody3D (GDQuest, Juli) or RigidBody3D (Bramwell, sVsn9NqpVhg): RigidBody only when the player must be a simulated object.
Establish first
| Input | Why it changes the plan | Default |
|---|---|---|
| Camera | spring arm or head pivot | third-person, spring 4.5 m |
| Character size | capsule sets door, step and corridor sizes | r 0.35, h 1.8, step 0.45 |
| Level type | GridMap kit, Terrain3D or stock heightmap | blockout then kit |
| World size | precision and streaming | single precision (float32 step 0.12 mm at 1 km, 1 mm at 8 km); stream when the profiled worst view or load time misses budget, not at a fixed size |
| Asset source | Blender suffixes or an AI GLB manifest | AI GLB + manifest |
| Targets | renderer, budgets, compression | desktop Forward+; web and mobile Compatibility (Juli 00:24:02) |
| Physics engine | Jolt default since 4.6, new projects only | Jolt, checked by the gate |
Workflow
- Project gate (P1).
gd_world.physics_gate(P)proves the engine: with Jolt, a ray on a concave shape returns face_index -1 (Godot Physics returns 8). A hand-written project.godot gets Godot Physics. Name the layers withset_layer_names. GATE:engine_running"Jolt Physics", no flags. - Player (P2).
build_player, thencontroller_suite(headless,--fixed-fps 60). GATE: speed at move_speed, same speed looking down, jump apex within 0.1 m of v²/2g, stairs to max_step_height and a refusal above, no snap at the ledge, 40-degree ramp yes and 50 no, arm shorter at the wall. - Blockout (P3).
validate_layout(layout)offline, thenbuild_village,door_test, bake. GATE: every door walkable by the real capsule (0.70 m blocks, 0.72 m passes for r 0.35; the offline rule asks 1.00 m [added margin]), baked triangles equal CSG triangles, capture reads as intended. - Kit (P4).
build_kit,build_grid,stairs_walk. Materials go on the mesh surfaces and each item holds one StaticBody3D and one NavigationRegion3D (docs). Set the lightmap texel size before building the library (GridMap auto-UV2 uses 0.1 per the docs, not re-measured; the .import default is 0.2) and keep navmesh cell size equal to the map's 0.25 (docs). GATE: a ray hit on every used cell, and the player climbs each stairs item (a 3 m story needs 8 steps; 6 blocked). - Terrain (P5, P6). Small worlds:
build_island(chunked ArrayMesh plus one HeightMapShape3D), thencollision_check. Sculpted or painted worlds:gd_world.terrain3d, withchange_region_size()(settingregion_sizeis ignored) and region-aligned placement. Painting is a human step: base paint on large consistent areas, then spray, never spray over autoshader areas (YtiAI2F6Xkk 00:05:45 to 00:07:25). GATE: collision within 0.1 m at 400 rays, region count and size recorded,.resdata, capture opened. - Scatter (P7).
gd_world.scatter, always windowed. GATE: every instance grounded, abufferper tile in the .tscn, rejections counted, close capture checked. - Props from GLB (P8).
glb_statsandglb_flagsoffline, theningest_glbs(P, drop, manifest)(pre-written .import with LODs, shadow meshes and light baking,post_import_prop.gd, textures retuned to VRAM at 2048), thenaudit_propsandlod_probe. Asset front is +Z (Vector3.MODEL_FRONT);look_at(t, up, true)turns +Z to the target. Blender exports are triangulated, transforms applied, rig in rest pose (docs). GATE: manifest heights, base at y 0, cull back, budget flags understood, lineup capture next to a 1.8 m reference, facing checked. - Streaming (P9).
runtime/cell_streamer.gd,unload_radiusgreater thanload_radius, tested windowed withworld_stream.gd:walk. GATE: 0 requests pacing on a border, every cell unloaded and the node count back to baseline at the end, worst frame within budget. - Audit and profile (P10).
audit_level(scenes), thengd_run.profile_scene(..., driver="vulkan"). GATE: no leftover CSG, no headless MultiMesh, no unnamed layers; GPU ms and draw calls recorded. Windowed frame time is capped at the display refresh, so judge GPU and CPU ms.
Numbers
| Value | Measured or source | Relative to |
|---|---|---|
| Controller defaults | speed 6, accel 40, air 8, jump 5.5 m/s, fall cap 30 | this kit [added], GDQuest shape |
| Stairs module | climbs 0.45, refuses 0.6; descent airborne 0 / 8 / 49 of 120 frames at 0.10 / 0.25 / 0.45 rise | tread 0.35, 6 m/s |
| Floor-below ray | 1.5 x max step | Majikayo 0.75 for 0.5 |
| HeightMapShape3D error | max 0.058 m, mean 0.004 m | bilinear, 1 m and 2 m spacing |
| Scatter | 1,931 trees from 6,000 rays in 11.7 ms | 30-degree slope, 2 m clearance |
| AI GLB textures | 19.6 MB lossless to 2.8 MB S3TC per 4K map | size limit 2048, mipmaps |
| LODs at 1080p | lighthouse 986,642 tris at 30 m, 61,616 at 300 m | mesh_lod_threshold 1.0 |
| Streaming | 69 loads in 240 frames without hysteresis, 0 with | border pacing, 64 m cells |
| Water z-fighting, plane 1 mm below | Compatibility: 1.7% of pixels at near 0.05, 0% at near 0.25; Forward+: 0% at both | Tokisan near 0.25 (YtiAI2F6Xkk 00:18:00) |
Quality gates
- The engine is proven by the probe, not read from settings. Layers are named.
- Every walkable surface has collision: ray tests on every GridMap cell, house floor and terrain sample.
- The controller suite passes for the project's real step and door sizes.
- No CSG in shipped scenes, no scaled collision shapes (HeightMapShape3D spacing excepted), no double-sided closed meshes.
- MultiMesh scenes written by windowed runs; the audit finds a buffer.
- Props at manifest size, base at y 0, simple collision, textures at 2048 or less in VRAM, LOD0 within budget (LODs never reduce LOD0).
- Terrain3D data in
.res, regions counted, textures to the rules in stance 9. - Captures opened: village, kit, island close-up, Terrain3D, props lineup, stairs.
Common mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Step height from the capsule travel | climbs steps above the limit | ray onto the tread |
| No floor-below ray | snaps down every ledge | ray of 1.5 x step first |
| Scatter run headless | trees vanish after reload; no buffer | windowed run |
update_map_data_from_image(img, -1000, 1000) | 10 m becomes 19,000 m | pixels read 0..1: use (0, 1) |
FastNoiseLite.get_image for heights | terracing (8-bit) | float loop or an RF image |
| Terrain3D before the camera | "Cannot find the active camera" | camera first or set_camera() |
Terrain3D.collision_mode or region_size set before add_child | ignored | collision.set_mode(), change_region_size() in the tree |
| Region size above the terrain, origin-centered | 4x the memory | 256 or 512, or align to the region grid |
| One GridMap for floors and walls | the wall replaces the floor | a second GridMap |
reparent() in _post_import | children jump to the origin | reparent(n, false) |
| Suffixed names colliding after stripping | @StaticBody3D@19793 | unique base names |
| Trusting LODs for a 1M-triangle GLB | 986k triangles at 30 m | decimate before import |
| Coplanar water and ground | flicker on Compatibility | near 0.25, or offset the water |
Handoffs
- scenario-godot-expert: toolkit, review loop, version traps.
- scenario-godot-rendering-lighting: lightmaps, occluders, GI, fog (depth fog can hide streaming edges [added]), shadows.
- scenario-godot-shaders: terrain splat or triplanar shaders, water.
- scenario-godot-animation: rigged characters and imported animation.
- scenario-godot-gameplay: navigation baking, AI, interaction.
- scenario-godot-pipeline-automation: batch import, CI, plugins beyond
post_import_prop.gd. - scenario-godot-performance-export: platform budgets, web and mobile exports, merged far-cell proxies (HLOD) [added].
- blender skills: Blender markers replaced on import (Allard 00:04:38), linked data refreshed only when the dependent file is saved (00:27:49), the "glTF Settings" node group for AO (FinePointCGI PIpCl6TYnC0 00:03:23).
- scenario-3d: generating the GLBs. Write "Scenario MCP" in customer-facing text.
Godot 4.7 notes
- Jolt is the default in new 4.6+ projects; hand-written project.godot needs
physics/3d/physics_engine="Jolt Physics". - Scene .import keys observed:
meshes/lightmap_texel_size0.2,gltf/naming_version=2, per-node physics in_subresources. A minimal pre-written .import is honored. - Headless
--importleaves extracted glTF textures lossless; the retune pass sets VRAM. SurfaceTool.set_smooth_group(-1)gives flat normals.- No
LightmapGI.bakeorOccluderInstance3D.bakein the script API. - Terrain3D 1.0.2 loads in 4.7.2; the first
--importcrashed (signal 11), the second ran clean.
References
references/procedures.md: P1 to P10 with code, live results and numbers.references/expert-notes.md: what each expert says, with timestamps, and where 4.7.2 disagrees.references/critique.md: what the baselines and graded answers got wrong, and the limits of this skill.references/gui-paths.md: editor paths for viewport-only work, with the code substitute for each.references/sources.md: videos, docs, add-ons, versions.