๐„ช

Dungeon Toolset

๐„ช

7 min to read

Welcome, Crawler!

The progress I've made on the Dungeon Pipeline Project up until now.

Overview

The objective of the Dungeon Pipeline Project is to display an understanding of environment creation pipelines with procedural workflows, using a dungeon as an example. I am completing this directed study over the course of the Fall 2026 semester with my industry mentor, Macey McCuller (prev. Associate Technical Artist at Bluepoint Games), and my professor, Dr. Bryan Kim.

The project is intended to go through 3 stages:

  • Stage 1: HDA creation

    • The level designer using the tool lays out planes that are then translated by the HDA into points with metadata

    • They will be able to choose a room type, which will then assign room type-specific modules to the points

  • Stage 2: Editor Utility Widget

    • The user will be able to take the HDA and pick out modules that they would like to alter with a quick and easy interface

    • Serves as a more customized editing tool to supplement the HDA

  • Stage 3: PCG Set Dressing

    • PCG will handle set dressing based on the metadata from the HDA, making each room look personalized and unique

    • This stage will transform the toolset from something clearly procedural to looking like it was hand-placed

If given extra time, the project will be accompanied by additional tools geared towards environment artists to break up the scene even further!


Visual Style

This project was heavily inspired by my love for the Dungeon Crawler Carl series! My goal is to make the project seem as though it is set in that universe. In particular, the goblin quadrant from Book 1. This includes making rooms like bomb workshops, safe rooms, and tutorial guilds.

Now obviously, this will require some asset creation. Therefore, throughout the project, I may, every once in a while, indulge myself in the creation of a Goblin Copper Chopper or a pineapple on a stick. However, the rest of the project will be created using a pre-existing asset pack.


Beginnings

To start the project off, I set up a simple system that snaps user input planes to fit 1x1 squares. I took the centroid of every outside edge to spawn walls on them, and took the centroid of each square to place floors. And then, I calculated the ray distance upwards and downwards from every wall point to see how far it is from other points. That created the base logic for spawning shorter walls, railing, and stairs. After all of this, I had a very simple setup for the rest of my HDA!

Then, I added a second input for corridors that had an override on floor combination. Any plane fed in through this input is automatically classified as a corridor, and is booleaned with its neighboring rooms.

Afterwards, it was fairly simple to add random rotations to floor tiles with the labs instance attributes node, and assign models from a pool of different versions!

(The placeholder modules are from Minions Art's Modular Dungeon Asset pack)

https://www.patreon.com/minionsart/posts/free-asset-pack-106367673


Room Parameters

Next, I made the logic for the @room_id attribute that would later shape the rest of my HDA. This allowed me to control the room type of each room. However, there was a problem! I had no way for my HDA to automatically update a multiparm whenever a room was formed. This is because each input plane may not exactly lead to one room. I wanted the user to be able to make unique room shapes instead of just putting a bunch of squares together. In addition, I wanted the space underneath other floors to be an individual room, separate from the others on its floor.

So, with help from my mentor, I wrote a python script that took a @max_rooms value and automatically updated the multiparm with an initialize button and a clear button. This allowed me to make each room editable with room type and wall height parameters (so far)!

def resizeMultiparm(kwargs):
  node = kwargs['node']

  piece_amt = node.node("../dungeonpipeline/Data/Third_Cleanup/CHECK_ROOM_AMT").geometry().attribValue("max_rooms") + 1

  node.parm("input_pieces").set(piece_amt)

  return piece_amt

def clearMultiparm(kwargs):
  node = kwargs['node']
  
  node.parm("input_pieces").set(0)
def resizeMultiparm(kwargs):
  node = kwargs['node']

  piece_amt = node.node("../dungeonpipeline/Data/Third_Cleanup/CHECK_ROOM_AMT").geometry().attribValue("max_rooms") + 1

  node.parm("input_pieces").set(piece_amt)

  return piece_amt

def clearMultiparm(kwargs):
  node = kwargs['node']
  
  node.parm("input_pieces").set(0)
def resizeMultiparm(kwargs):
  node = kwargs['node']

  piece_amt = node.node("../dungeonpipeline/Data/Third_Cleanup/CHECK_ROOM_AMT").geometry().attribValue("max_rooms") + 1

  node.parm("input_pieces").set(piece_amt)

  return piece_amt

def clearMultiparm(kwargs):
  node = kwargs['node']
  
  node.parm("input_pieces").set(0)


Visualization

After this, I realized that the humble Houdini visualizers aren't exactly visible in Unreal. And what was a level designer going to do if they had 100+ rooms and no way to tell which is which? I decided to tie custom text to the centroid of each room so that the user could see exactly which room it is, and what the current room type is.

So first, I had to create a label attribute:

s@label = sprintf("Room %d | %s", i@room_id, s@roomtype);

Then, I had to make sure that each label was individualized in the text node:

points('../attribwrangle3/', 0, 'name')

Then, I had some snazzy little labels on each of my rooms! I plan to use these to visualize each room in the first step of a button strip I'll set up, that way they can customize with fast iteration before cooking.


Next Steps

In the next week, my focus will be on getting the HDA cooperating with Unreal, and incorporating the Unreal asset pack I plan to use for the rest of the project! If you've read this far, God bless you and thanks for keeping up with my process!

Create a free website with Framer, the website builder loved by startups, designers and agencies.