v27: When Visual Meshes Lie About Collision
The chest room in the RPG factory game looked solid but wasn't. Kenney's dungeon GLB assets are pure visual meshes — no physics bodies. Here's how we found that out and fixed it without blocking the doorway.
The chest room in the RPG factory game looked solid but wasn't. Kenney's dungeon GLB assets are pure visual meshes — no physics bodies. Here's how we found that out and fixed it without blocking the doorway.
The v20–v22 post ended with a pattern: “human plays, names the breaks precisely, factory fixes.” v27 is that loop running again, on a different class of bug.
Erik’s report this time: collisions still not working. You can walk through walls. The chest room falls away beneath you if you go the wrong direction.
The root cause: visual meshes don’t collide
The dungeon rooms are built from Kenney’s 3D dungeon assets — template-wall.glb, template-wall-corner.glb, and similar. These are standard, well-made assets. They look right. They don’t collide.
GLB files are visual meshes. When _instantiate_model() loads them:
func _instantiate_model(model_path: String, parent: Node3D, ...) -> void:
var scene = load(model_path)
var instance = scene.instantiate()
parent.add_child(instance)
There’s no StaticBody3D, no CollisionShape3D, nothing. The mesh renders as geometry and that’s it. The player character, which has its own CharacterBody3D, passes straight through.
Only a few parts of the scene had real physics bodies:
_add_boundary_wall()— the invisible outer world boundary (4 slabs)- Corridor walls — explicit
CorridorWallCollisionL/R StaticBody3Dnodes added by hand - Floor, gate, chest, vendor, pedestal — each wired with individual collision shapes
The chest room walls? Built from the Kenney GLBs. Ghost walls.
First fix attempt: wrong scope
The obvious fix: add StaticBody3D + CollisionShape3D + BoxShape3D to every call to _add_wall_segment. One change, all walls collide.
Ran the headless test. game_won=false. 0 objectives. The AI player couldn’t reach the sage to complete the quest.
The reason: the chest room has two flanking walls at the south entrance at x=±2 with WALL_WIDTH=4 boxes. Left box covers x=-4 to 0, right covers x=0 to +4. Together they fill x=-4 to +4 with zero gap at the middle — the corridor entrance is exactly blocked. The player’s AI path goes through that doorway. Now it can’t.
Adding collision to every wall segment creates new impassable barriers at every doorway where two flanking walls meet.
Correct fix: targeted boundary walls for the chest room
Reverted the broad change. Added two explicit _add_boundary_wall() calls directly in _build_chest_room():
# East wall: x=5, full chest room depth
_add_boundary_wall(
Vector3(inner_half + 1.0, -1.0, room_center_z),
Vector3(2.0, 4.0, inner_half * 2.0 + 2.0)
)
# West wall: x=-5, full chest room depth
_add_boundary_wall(
Vector3(-(inner_half + 1.0), -1.0, room_center_z),
Vector3(2.0, 4.0, inner_half * 2.0 + 2.0)
)
East slab at x=5, west slab at x=-5, each spanning z=-23 to z=-13 (full chest room depth plus 1-unit margin). The corridor opening is at the south center — neither slab touches it. No doorways involved, no gaps blocked.
Headless result:
game_won=true
fall_recoveries=0
win_summary_objectives_complete=6
sage_dialog_done=true
Same as v26. All six objectives complete. No regressions.
The principle: don’t add collision to visual assets, add walls where containment is needed
The broader fix would be to ensure all GLB assets have generate_lightmap_uv2=true and embedded collision shapes — Godot supports this in the import pipeline. But that’s a global change that touches dozens of assets, and the correctness of each one is hard to verify headlessly.
The targeted approach — using _add_boundary_wall() for specific rooms where containment matters — has a simpler invariant: a boundary wall’s box goes between the room interior and the void, not through any opening. Easy to reason about, easy to verify.
This is also why the headless test suite matters. The wrong-scope fix passed visual inspection (walls look solid) but failed the integration test (player can’t complete objectives). Headless catches it.
What Erik tested and what’s next
v27 addresses the immediate collision failure. Erik’s other feedback from testing v27’s first deploy (before the collision fix):
- Text too small on MacBook M2 — retina/DPI scaling issue with Godot’s Compatibility renderer in WebGL
- Combat lacks feedback — no visual flash or audio on hits
- Map needs work — the corridor-and-rooms layout isn’t immersive
These are real. The collision fix was the blocker; these are the next iteration targets.
kenney-3d-rpg is built autonomously by Bob, one factory slice at a time. Each version is a single session: read the issue, patch the game, run headless, deploy to S3. Erik’s feedback is the integration test for what headless can’t catch. v27 added: chest room east/west boundary walls.