Dungeons made of grey slabs are functional but not interesting to explore. Today was about making them look like actual underground environments.
Terrain: Attempt 1 (failed)
First attempt was straightforward: replace slab walls with Roblox terrain. Use Terrain:FillBlock() to create walls out of rock material. Sounds reasonable, right?
It wasn’t. Roblox terrain operates on a 4-stud voxel grid. Our rooms have 1-stud-thick walls. Terrain can’t represent geometry finer than 4 studs — everything gets rounded to the nearest voxel. The result: walls that were either 4 studs thick (too chunky) or missing entirely (too thin to register). Doorways were blobby approximations. Floor transitions were stepped.
We rolled it back and wrote a postmortem. The conclusion: terrain is great for large-scale environment, bad for room-scale precision.
Config cascade system
Before attempt 2, we built something we should have built weeks ago: centralized dimension configuration. Wall thickness, door size, room height, truss spacing, pad radius — all defined in one config object that cascades down to every system.
Previously, each module hardcoded its own dimensions. The room builder used wallThickness = 1. The door cutter used doorWidth = 8. The truss placer used wallThickness = 1 (separately). Change one, forget the others, and geometry doesn’t line up. The config cascade makes these values single-source-of-truth.
Terrain: Attempt 2 (fill-and-carve)
The second approach worked: use terrain as the exterior shell, not the walls. The algorithm:
- Compute the dungeon’s total bounding box (including a margin)
Terrain:FillBlock()the entire bounding box with rock — solid earth- For each room interior,
Terrain:FillBlock()with air — carve it out - The rooms are still built from Parts (slabs for floors, walls for collision)
From inside a room, you see clean Part walls. From the minimap, the dungeon is embedded in solid rock. The terrain provides the environmental context without trying to represent room-scale geometry.
Noise-based material mixing
Solid rock gets repetitive. We added 3D Perlin noise to vary terrain materials. As the noise function’s value changes across 3D space, the terrain material shifts between rock types — stone, slate, sandstone, basalt. This creates organic-looking material transitions without manual painting or per-room configuration.
The noise scale is tuned so transitions are gradual (you won’t see a sharp line between stone and sandstone) but noticeable over the span of a few rooms. Different noise octaves at different frequencies layer fine detail on top of broad regional shifts.
Cave color palettes
Ten themed palettes that cycle per region. Region 1 might be grey stone with cool lighting. Region 5 might be warm sandstone with amber tones. Region 8 might be dark basalt with sparse blue accents. The palette affects wall part colors, PointLight tint, and terrain material selection.
Cycling means every 10 regions, the palettes repeat. But since dungeon layouts are procedural, the same palette applied to a different layout feels fresh. It’s a cheap way to create visual variety without authoring 50 unique themes.
Other fixes
- Layout ordering bug —
generateLayoutnow runs beforeloadPlayerData, preventing a race where we tried to place a player in a dungeon that hadn’t been generated yet - UserId on AreaHUD — A debugging addition showing which player’s data is active, useful for multiplayer testing