计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
scenario-godot-3d-world【效率与协作】Use when building 3D levels and worlds in Godot 4.7: third-person or FPS controller, player stuck on stairs, spring arm camera clipping, blockout with CSG, GridMap and MeshLibrary, terrain (heightmap, HeightMapShape3D, Terrain3D regions, textures, painting), scattering trees with MultiMesh, streaming a big world, procedural meshes with SurfaceTool, J…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
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).
body_test_motion and 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.CSGMesh3D operand is silently ignored, with no error.-colonly hand shapes. Trimesh (-col) for static level geometry only, box or convex for props and anything dynamic, -noimp for reference geometry.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_transform returns identity, default reparent() zeroes the position.top_level pivot, add_excluded_object(player). The arm stops one radius before a wall..res, since .tres loads slowly (YtiAI2F6Xkk 00:01:30).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.
| 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 |
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 with set_layer_names. GATE: engine_running "Jolt Physics", no flags.build_player, then controller_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.validate_layout(layout) offline, then build_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.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).build_island (chunked ArrayMesh plus one HeightMapShape3D), then collision_check. Sculpted or painted worlds: gd_world.terrain3d, with change_region_size() (setting region_size is 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, .res data, capture opened.gd_world.scatter, always windowed. GATE: every instance grounded, a buffer per tile in the .tscn, rejections counted, close capture checked.glb_stats and glb_flags offline, then ingest_glbs(P, drop, manifest) (pre-written .import with LODs, shadow meshes and light baking, post_import_prop.gd, textures retuned to VRAM at 2048), then audit_props and lod_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.runtime/cell_streamer.gd, unload_radius greater than load_radius, tested windowed with world_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_level(scenes), then gd_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.| 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) |
.res, regions counted, textures to the rules in stance 9.| 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 |
post_import_prop.gd.physics/3d/physics_engine="Jolt Physics".meshes/lightmap_texel_size 0.2, gltf/naming_version=2, per-node physics in _subresources. A minimal pre-written .import is honored.--import leaves extracted glTF textures lossless; the retune pass sets VRAM.SurfaceTool.set_smooth_group(-1) gives flat normals.LightmapGI.bake or OccluderInstance3D.bake in the script API.--import crashed (signal 11), the second ran clean.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.