Download

Zelda-esque Dungeon Creation (Binding of Isaac style)

Hi everyone!
For my current game, I’m looking for a way to make procedural dungeons in a way like Binding of Isaac does, which is inspired by Zelda on the NES.

In case you don’t know any of those games, let me explain.

Each dungeon is created of rooms, which can have a door on the north, south, east or west wall, connecting it to the next room. In Zelda, these dungeons were pre-designed, so nothing too fancy here. In Binding of Isaac, those rooms were pre-designed “blocks”, which could be connected to one or more rooms, wether or not a room has spawned on any of the sides.

What I’m thinking is that I want to pre-design rooms within the editor and then sit a blueprint with a collider of the size of the full room on top of it that just grabs everything inside and “merges” it into one room. Basically it adds everything inside it as component. Not sure how this works though.

Right now, my idea about this needs two Blueprints (because I like to keep things seperate and better organized for myself to better adjust things later on and make it more complex as I go).

  1. The “Architect”
    This Blueprint lays out the dungeon and draws lines in the length of a room, from one center to the next. It procedurally creates a path and specifies special rooms like start, end or bonus rooms for loot, secrets etc. After laying out the level, the Architect calls for a randomly selected Room to spawn in every position where a line has started from.

  2. The Room Blueprint
    After designing several rooms by hand, I will place one Room Blueprint over each one of them with the exact same size. This Blueprint grabs everything inside and merges it somehow, so I can place everything as one object. Now this Room gets called by the Architect, rotates randomly in 90° angles and spawns at the specified location. If any flags are turned on (secret, loot, end room etc), it will change some things about it and that’s it. Maybe I will create special secret rooms etc, so if the Architect needs one of them, it will grab specifically designed secret rooms, loot rooms etc. Not sure yet.

This is my basic idea.

Do you have any concerns with this idea? Could it work? What could cause problems?

My idea is to start by grabbing everything inside the Room Blueprint that overlaps the box collider component and somehow tag it or do something to make it merge together. When I move the room, everything inside should move with it and stay in the relative location to eachother and rotate according to the center of the room. If I rotate the entire room by 90°, every object inside should do the same and stay in it’s position. What would be a good way to do this?

Like that concept? https://forums.unrealengine.com/showthread.php?50525-Random-Map-Generation-Looking-for-suggestions-on-a-different-implementation

Or if you are using fixed size rooms, my explaination for a fixed sized mazed are further down in the same thread.

I’m afraid in the thread you mentioned the rooms get generated by the engine, but that is not what I’m looking for.

I want to pre-design the rooms in the editor and then somehow link all the designed parts together and use them as rooms that get placed by another Blueprint to create Zelda like dungeons.

Yep, because of that one thing, the whole tutorial is totally worthless. Game development is just about finding all the right lego pieces that someone else already made, and snapping them together.

Why not follow that tutorial in a new project so you have an idea of what it’s like, and then try converting it into pre-designed rooms?

Plus, maybe you’ll find out random is worth it, or even a hybrid where some parts are designed and some are random.

I’ve read the thread and while there are a lot of interesting ideas, not much described in that thread is helpful to my problem.

I don’t have any concerns about calculating room space, which is one of the main concerns in that thread. For example, I could even have a grid that places rooms in my case and it wouldn’t make a difference.

I think my method is far removed from the ones described as in that thread. That’s the reason why I made my own thread instead of hijacking that one.

Though some methods are of course useful for me, I think my first problem is that I have no idea how to “bundle” a room with pre-designed objects, meshes, blueprints etc to move and rotate it in runtime as one single object.

Yes using a fixed grid size makes genaration a lot easier.
Even if a maze algoritm is in theory made for corridors, there is no reason that each piece of hallway instead could be a complete room.

A modification to the maze algorithm might be of use, instead of having a fixed number of rooms in the grid where you try to connect all, you could simply have a max number of rooms and add them dynamically as you expand, you would need slight modification for that only. See if there “is” a room, rather then see if the room is “visisted”.

There are a few other examples of random generation of this kind, it might be a good idea to do a search for level generation for 2d game, since you seem to work on one plane.

Also you could make variaties of the rooms based on number of exits, removing the need to keep the center of each wall open. And just query a room for the number of doors and fetch rooms from the apropriet list.
T shape, four ways, corner, corridor and dead end.

I like to see where this is going. :slight_smile:

Hello! I find this of interest as I am the creator of that other thread. Later in the thread, I actually came to the same conclusion you started with - premade rooms (at least, in terms of their walls) are simply easier to work with and faster to process.

In any case, I’m trying to get an idea as to what you’re asking. So let’s say you have a “kitchen” room. It has 4 doors, North, East, South, and West. The center of the room has a table with chairs, the NorthEast corner has a water basin and the SouthWest corner has a shelf with cooking supplies.

You want to create this entire room so that it is treated as a single actor? I would create a “kitchen” blueprint and make as much as you could in the components tab. The only thing I think you wouldn’t be able to add there is other blueprints (unless I’m mistaken in which case, that’s your answer). I would add a vector point at each location you wish to spawn a new blueprint (maybe the shelf is a separate container blueprint).

Then, go into the event graph and make a “Begin” event node and “Spawn actor from class”, using one of your placed vector points as the location. Then, I believe the function is “Attach actor to actor”, attaching the spawned actor to your room.

After that, I believe any time you rotate the Kitchen actor, all the other attached actors will maintain their relative transform within the kitchen as well.

If this is not what you’re asking… sorry! Please re-phrase and I’ll try again :slight_smile: I’m at work, I’m bored, I’ve had lots of coffee.

Edit: One other thought. You can change the transform of an actor as you spawn it, removing the need to rotate it after the fact. Unless rotating the room is part of the game which admittedly sounds pretty awesome.

Hi Blue669! I discovered an interesting way of doing this just a bit ago and I thought I would share. You can dynamically load the levels in blueprint - but before you do - get the streaming level (using the ‘Get Streaming Level’ action) and then set it’s Level Transform. Now you can move your levels around randomly to generate dungeons using premade rooms. The limitation to this approach is that you can’t have multiples of the same streaming level - however you can obviously get around this as well just by making a copy of the level you want to put on the stage multiple times. Hope this helps - and good luck!

I’m finally ready to share my first version of the Architect. The Architect is a Blueprint that handles the creation of all Blocks in the level. I’ll go over the logic behind creating rooms and then - well, there’s a problem I can’t figure out. No matter how many times I go over the logic, I can’t find the problem. I really hope someone else with a better sense of logic can help me find my mistake.

Good things first: most of the time, the Architect works and delivers beautiful random “Zelda” maps:
architectworks.jpg

However, sometimes the logic bugs out and produces disconnected rooms. I have no idea why?!!?
architectbugs.jpg

The level you see is built from Blocks. Each Block is a Blueprint that contains a number of rooms as components. Every Block is predesigned by hand and the Architect gets all the Blocks fed and put into an array from which it selects what Block to place. So far so simple. In this example I have only one type of Block to better demonstrate the bug there currently is.

Now lets go over the logic behind the placement of those Blocks.

architect01.jpg
The whole placement of these Blocks comes in 3 steps. Setting a random rotation, finding a suitable location and lastly combining these two steps to produce a transform for the Block.

architect02.jpg
First step is a random rotation for the Block. Since it’s a Zelda map, these rotations are always in 90 degree angles.

architect03.jpg
If this is the first Block to create, I just want to place it wherever the Architect is placed in the map.

architect05.jpg
As said, the first Block will just be placed at the location of the Archtect. I then add the Block to my array that contains all locations of the created Blocks.

architect04.jpg
After the first Block is placed, we start looking for a new location for other Blocks. The Architect will continue looking until a new location has been found.

architect06.jpg
Here, I do 2 things. First, I determine a direction in which the new Block will spawn compared to the old one. Then, I pick a random Block that is already stored inside the array and break the X,Y and Z into variables.

architect07.jpg
Here, I check for the chosen direction. If the direction is north of the old Block, I add the size of a Block to the X. South subtracts the Blocksize from X. Same for East and West with the Y axis. Z stays untouched. Then I set this as the new Block position.

architect08.jpg
The Architect needs to check if the new location has already been used by another Block before. If it did, we do the whole Block finding logic again until the location has never been used before.

architect09.jpg
Now that the new location has been found, we add it to the array. I also added a few other arrays for debugging purposes.

architect10.jpg
The last step is putting the rotation and vector together to make the full transform for the new Block. I also reset the Block found variable, so after placing the Block, the process can start anew for the next Block.

I know this logic is pretty simple and it also produces disconnected Blocks - I can’t figure out why. If you have an idea, please let me know!

The next step: adding specials to Blocks - for example Blocks that are locked and require a key to access, a Block for the player start position to spawn, a Block for the exit and so on.

If you have any questions, let me know!

I’m still confused why the disconnected rooms appear. The logic seems completely sound to me.
Has anyone else taken a look at it?

I’ve added a debug line that is drawn from the original vector pulled from the array (I call it parent vector) to the new location of the ready-to-be-spawned Block.
Since I only go one step north, south, east or west from the original position per loop, I expected only horizontal and vertical lines. However, I saw this image:
architectdebug.jpg

The Blocks seem to jump over multiple distances, creating all sorts or diagonal lines.
Now, this would explain why some Blocks appear completely disconnected from every other Block, but there doesn’t seem any logical explanation for this in the entire BluePrint.

Just for everyone who is also interested in this dungeon generator, here is my solution for the problem (ignore the seeded random, I just did that to make debugging easier)

ccda4ef9f0e8a39458329e165cea18fb5fcf9728.jpeg
The problem was whenever I was setting each X, Y or Z for the Block position, the Blueprint was always reaching for a different “parent” position. Here, it will always take the same value for each X, Y and Z position.
Have fun with this :smiley:

Thank you so much, you really halped me out with this. :slight_smile: