Controlled Procedurality — Cells, Rules, and Manual Override in Solo Level Design

pavelzosim:~/atlas_SYS.ONLINE / UTC+3

01 Overview

I've been building a procedural maze/dungeon system in Houdini, cooked into Unreal. It's easy to describe as "a maze generator," and that description is wrong — or at least beside the point. The maze is the current demo. The actual system is a cell-based framework for controlled procedurality: rules that generate content, that a designer can still reach in and override by hand, one cell at a time.

Large procedurally generated level built from cell-based rules, cooked through Houdini into Unreal
A generated level — the maze topology is the current test surface for the underlying rule system, not the point of it.

Spatial partitioning itself isn't the novelty here — chunking a world into cells for streaming and culling is old, deliberate engineering (Unreal's own World Partition is exactly that). What's different is what a cell carries. In a streaming system, a cell is a unit of loading. Here, a cell is a unit of design: it can hold a gameplay zone, a set of visual props, a density of enemies, and a rule for how it composes with its neighbors — all at once, all tunable.

02 The cell as a rule-bearing unit

A cell is a set of conditions, not a fixed thing. The same abstraction that defines a structural module (a wall, a floor tile) also defines a gameplay zone — one cell can be authored as a shop, another as an action/combat zone; that's a level-design decision. The same abstraction extends to purely visual rules: this cell gets a puzzle prop, that one gets a lamppost, and both are controllable independently of the zone-level decision.

Every module type — a wall variant, a Chest, a Trap, a Room — follows the same pattern: it's an abstract slot with its own embedded rules, not a hardcoded thing. A module's rules aren't limited to "where do I spawn" — a module can also define how it shapes the space immediately around it. What fills the slot is arbitrary; the maze is just the current place all of this is exercised end to end.

03 Two layers per cell: common and unique

Splitting a cell's content into what's shared and what's unique turns out to map directly onto how the Unreal side has to represent it. Structure that repeats across many cells — walls, floors, the generic module set — goes through one shared instancing layer. Content that's specific to one cell — a chest, an enemy, an obstacle placed relative to a reference point like the player's spawn — gets its own independent Actor.

Cell
├─ zone rule        → shop / action / puzzle / ...
├─ common layer      → HISM: shared structural repetition (walls, floor)
└─ unique layer      → Actor: cell-specific content (chest, enemy, obstacle)

That HISM/Actor split is a real, shipped mechanism — a typed data layer over the Houdini Engine API decides per point whether it's common (routed to a named HISM component inside a shared container) or unique (an independent Blueprint Actor). I wrote up the implementation separately: Houdini to Unreal — Typed Placement Routing for HISM and Blueprint Actors. What matters here is the conceptual half of it: the cell is what makes "shared repetition" and "unique placement" composable over the same space instead of two disconnected systems.

04 Stacking rules and controlling saturation

A cell isn't limited to one abstraction. Several can stack in the same cell — a zone rule, a prop rule, a spawn rule — and each is tunable independently: wall variation, whole-level variation, trap intensity and distance between traps, enemy count. All of that is shipped, not aspirational.

For a solo developer, this is the part that actually matters day to day. Without composable, saturation-controlled rules, the only alternatives are hand-placing every cell — which doesn't scale to one person across a full level — or a team of level designers. Tunable stacking is what makes the difference.

Open question, stated honestly: what happens when two stacked rules in one cell disagree — an action zone wanting high enemy density against a puzzle rule wanting low density? Not solved yet. The top-level system was built reconfigurable in advance for exactly this kind of extension (responsibility between rule layers can be reassigned, visible directly as nodes in the graph), but the actual conflict-resolution policy doesn't exist yet.

05 A reusable obstacle-resolver pattern

Door and Key are the concrete example: the system traces reachability from the player's start position and guarantees the maze stays solvable — key with index 1 is always reachable before door with index 1 has to be opened, for every door/key pair. That guarantee is checked, never forced; if a generated layout can't satisfy it, the system reports the conflict rather than silently moving a key to fix it.

Obstacle-resolver pattern
Door      ←→ Key
Rubble    ←→ Shovel
Passage   ←→ Boss defeated

Door/Key is one skin on a generic pattern: guarantee that whatever resolves an obstacle is reachable before the obstacle has to be crossed. The same reachability logic applies whether the obstacle is a locked door, a dig-through wall, or a boss gating a passage — only the narrative label changes.

06 What's next

Transitions between cells that behave as self-contained biomes — moving from one cell's rule-set into a neighboring one with a deliberate handoff, not just adjacency — is on the roadmap, not built. Future posts will go into the parts this one deliberately left out: the room/DFS generation underneath the topology, the reachability algorithm's own internals, and the wall-variation system in more detail.

07 Result

The maze is a convenient, testable surface for a system that isn't really about mazes: cells that carry design intent, not just geometry; structure split into what's shared and what's unique; rules that stack and stay tunable; 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.

// END OF ARTICLE // CONTROLLED_PROCEDURALITY // EOF