I set out to build a maze generator in Houdini. It didn't stay one — and it was never really about finding the shortest path between two points anyway. A maze-solving algorithm does that in a page of code. What I ended up building is a modular system where abstractions describe rules for how a maze — or any level — gets assembled, and every stage of that assembly supports both randomization and manual placement.
Right now, every wall in the demo is a straight 90-degree segment. That's a deliberate constraint, not a corner I haven't gotten to yet. Angled corridors and non-orthogonal geometry mean more junction types to test, more assets to author for each of them, and a wall-building step that has to account for arbitrary angles instead of one fixed grid increment. Orthogonal walls keep the module count small and the placement math simple. Angled geometry is a real option later — once the rule system underneath is proven, not before.
Orthogonal-only walls are a deliberate scope constraint, not an unfinished corner. Angled/non-orthogonal geometry is deferred until the rule system underneath is proven — not implemented and hidden.
Take a real map. It's divided into grid squares, and each of those squares divides again into smaller ones. That's not a metaphor — it's literally how military grid reference systems work: a large square gets a code, and inside it, smaller squares narrow the precision down further, from ten kilometers to one meter, depending on how exact you need to be.
That's one layer: a grid for locating things. It doesn't say anything about what belongs in any given square.
The second layer is what a level designer adds on top of it. Half-Life 2 is a clean example — its levels aren't laid out as generic "here's an area." Valve's own designers have described the process directly: an initial idea that a stretch of level should contain a puzzle or a combat encounter, checked against what comes immediately before and after to keep the overall pacing right, then corrected against what playtesting actually shows.
"We might have an initial idea that we want a certain level to contain a puzzle or combat. We try to look at what comes before and after and keep in mind what we think the overall pacing should be. We learn everything from playtests." — Phil Co, Valve
Resident Evil runs a version of the same idea with different vocabulary. Nearly every game in the series, going back to 1996, uses save rooms — deliberately monster-free spaces that exist purely as a breather. Resident Evil Village layers a shop on top of that same pattern, through The Duke's Emporium — though that's specific to Village. The original RE7 has the save rooms but no merchant.
Neither game invented the grid. What both add is a layer of intent on top of spatial subdivision. The same square can be a shop, a fight, or a breather — and a level doesn't need to be built on literal squares at all for that idea to still apply.
That's the layer I built into the generator. A cell is a slot that can carry a zone — shop, action, puzzle — independent of what fills it structurally, and independent of whatever prop rule applies inside it.
Cell — a slot that carries a zone rule (shop / action / puzzle), independent of the structural rule that fills it and independent of any prop rule applied inside it.
| Cell | Zone rule | Prop rule | Structural rule |
|---|---|---|---|
| A | SHOP — a lull zone, room to breathe and pick up ammo or health | — | — |
| B | ACTION | PUZZLE | — |
| C | ACTION | LAMPPOST | HISM wall |
The lamppost in cell C isn't decoration. It's a landmark — the same way a real streetlamp standing alone in the middle of a real street quietly tells you this is a place, without a line of dialogue or a UI marker. Two cells can carry the identical zone label (B and C are both ACTION here) and still read completely differently, because the prop rule and the structural rule under that label aren't tied to it.
Everything the system places falls into one of two buckets.
Same underlying registration mechanism, different job.
Doors and keys are one instance of this pattern. Each door carries an index, and the system guarantees a matching key exists somewhere the player can actually reach before that door has to be crossed. That's not validated after the fact and patched to fit — the guarantee is built into how the layout gets generated in the first place.
It's the same shape Resident Evil's map design has used since 1996, sometimes analyzed as "recursive unlocking": find a key, it opens a room, that room holds the next key, and the building opens up like a spiral. The difference is that in RE, that structure is hand-authored, room by room, by a level designer. Here, the same shape is a formal rule, checked automatically every time the layout regenerates.
Door and key is also just the current skin on it. The same guarantee holds for a hammer that breaks through a wall, a lever, a button that lowers a bridge over a moat — whatever the obstacle and whatever resolves it, the underlying rule doesn't change: never place the resolution somewhere the player can't reach first.
A room is a separate abstraction — an n-dimensional, definable area. It might be a hall between corridors, or a whole biome. What makes it a room instead of leftover space is that it carries its own unique scatter of objects, distinct from whatever rules govern the corridor around it.
Four things hold the system together: modularity, abstraction, scalability, and the ability to keep adding new abstractions without rebuilding what already exists. A wall, a chest, a door, a room — every one of them is registered the same way, which is what makes it possible to add a fifth or sixth category later without touching the first four.
In practice, the workflow is three steps:
Everything is fast to reconfigure, and supports variation on top of that — through a shader or a Blueprint, whichever fits the object. On the Unreal side, registration happens through one of two paths: a container managing HISM, for anything that repeats structurally, or an individual Blueprint Actor, for anything that needs its own behavior. That split is what keeps a wall variant and a functioning door from having to be the same kind of thing under the hood — the mechanism itself is covered in a separate post: Houdini to Unreal — Typed Placement Routing for HISM and Blueprint Actors. I also wrote a Houdini Digital Asset add-on to work with this registration live, which cut iteration time down considerably and got rid of the lag that used to come with editing it directly.
Above structural placement sits the gameplay layer — rooms, doors, keys, enemies — and it follows the same registration principle end to end, because that principle is the actual foundation of the tool, not a detail specific to walls. Above that sit props: benches, lampposts, whatever fills a space without changing how it plays.
This isn't a diagram — it's running. I'm building a game on top of this exact system: a maze-like city set in a late-1980s USSR aesthetic, with its own visual language and cast of characters. Gameplay scripting isn't finished, but the placement system already runs with real objects standing in for each abstraction — enemies, doors, rooms.
The placement system runs today with real stand-in objects for every abstraction — enemies, doors, rooms — not a diagram, not aspirational. Gameplay scripting on top of it is still in progress.
This post is the shape of the thing. The next ones go into what makes it actually work: how a cell's content splits between shared instancing and individual Actors, how stacked rules get tuned without fighting each other — including one conflict I haven't solved yet — and the reachability check that makes the door/key guarantee real instead of aspirational.
Open, unsolved: what happens when two stacked rules in the same cell disagree — an action zone wanting high density against a puzzle rule wanting low density, for instance — has no resolution policy yet.
The maze is a convenient, testable surface for a system that isn't really about mazes: cells that carry design intent, not just geometry; a grid borrowed from how real maps already work, with a layer of pacing borrowed from how shipped games already zone their levels; and one reachability guarantee reused under different names. Built by one person, for one person to keep shipping content without hand-placing every instance of it.