Agent based Dungeon Generator
Auteur: N.E. Misset
Student number: 500779878
Blog Post: Research and Development
Education: HBO-ICT Game Development
Coach: Coline Pannier
Guild Master: Remco van Swieten
Datum: 28 October 2021
- 1. Introduction
- 1.1 A basic level
- 1.2 The Cathedral
- 1.3 The forest
- 1.4 A map generator
- 1.5 End Goal
- 2. the Generator
- 2.1 Level design
- 2.2 Agents
- 2.3 Selection and Rotation
- 2.4 Object Placement
- 2.5 End Product
- 3. Future
This blog post explains the process of making a Dungeon generator with inspiration from the Diablo 3 game and later on Roguelikes like Enter the Gungeon. Chapter one will introduce the inspirations and goals of the project. Chapter two explains the methods used to get the end product in the order that the scripts are called. It will also explain the process taken to come to the given method. Lastly Chapter 3 will explain future additions that could be added.
I wanted to make a Diablo 3 level generator. If you ever played Diablo 3 you might think: “Which Diablo 3 level”. This is because diablo 3 levels aren’t all the same. This is, beside the fact that I like Diablo 3, one of the main reasons that I chose this project, because it has a variation of levels that I think, with the knowledge we acquired from the workshops, needs different algorithms to generate. So to answer the question: The cathedral and forest level. This is because I thought they were far away from each other in the way they are structured.
1.1 A basic level
The different Diablo levels still have some similarities. For starters, they have an entrance and an exit. Furthermore, enemies are roaming around to block your path, there are objects to break, treasure to loot and power-ups in the form of pillars to boost your fighting ability.
1.2 The Cathedral
The cathedral is the more structured of the two. As you can see below, the level consists of straight corridors and corners with some bigger chambers that have some symmetrical features.
Like mentioned and seen, below the level has some breakable objects in this case the barrel on the rightside of the player character.
1.3 The forest
The forest, contrary to the cathedral, is a lot more round. Humans still have had some effect in these levels, but most of what they have built is recaptured by nature like destroyed buildings and broken iron gates. The only man made feature still standing is the road you are walking on.
1.4 The Map
I wanted a developer to be able to have some input on the level. Below is my top result when I google map generator. I used it to get some inspiration on what a developer could input and how it would look like.
I didn’t use many of these features in retrospect. This is mostly because these features aren’t applicable to my generator. I did however take some features. Firstly, the seed is an important part of a generator so that you can easily recreate previously generated maps. Secondly the UI. I was looking for something that wouldn’t get in the way but was also not overlooked. Finally, the sliders. My generator doesn’t work when some inputs are zero. These sliders fix this problem.
1.5 End goal
I want to deviate from the Diablo 3 game. I don’t want to make a clone of the Diablo 3 level, but mostly want to take inspiration from the levels. The Diablo 3 game doesn’t actually have any procedurally generated levels. In that sense I was already deviating from the game. if I make a generator I might as well make something I can use myself. Therefore, my target group will be developers. I want to make a generator that can have a different result based on the developer using it. In the sense that I would be able to make levels for 2 totally different games. To complete this goal I want to answer the following questions:
- How do I make a good level using procedural content generation?(Because why would you want a level generator that makes bad levels)
- What are the rules that all levels follow?
- What techniques should I use to make the layout of a level?
- How do I determine the start and end of the level?
- How do I place objects in the level?
I ended up just making a level generator focusing on the cathedral level, since this was enough to fill this project’s timeframe.
2. The Generator
This chapter talks about all features that I worked on and the process I went trough to get the result to the results. To start with I will discuss how I tried to make a level based on a mission graph. Then I’ll explain the techniques I eventually used and finish with the end result.
2.1 Level design
Anders Bouwer translated a mission diagram and translated it into a level.
I thought this method would ensure that my level had a good action order. That a key would spawn before a door for example and enemies would gradually become stronger. I started with trying to generate a mission graph.
This was my first version of a mission graph. I wanted to make a first version to at least be able to make a prototype and afterwards iterate on it. I faced some difficulty when trying to convert this graph to a game space. Since I didn’t want to waste too much time I decided to find an alternative, beginning with the game space instead of the mission.
I found a couple of fan sites and blog posts talking about how the cathedral levels were divided into chunks and in game these chunks were swapped to keep the level more fresh when going through the level again on a different character. To check I started the game up myself and indeed I got a different level every time.
For my PCG workshop assignment I made a binding of isaac kind of level. I found that the layout of the rooms in the example above had some similarities. My binding of the isaac dungeon was agent based. So I decided to try and expand on this concept.
In this project an agent is simply a gameobject that has a starting location and “randomly”(seed-based) chooses a direction and returns all the locations it has been. In the case of the picture below 6 agents will be sent out with a minimum of 14 steps and a maximum of 44 steps.
Level Start and finish
After all rooms are placed I search for a good place to start and end the level. The script that I wrote loops through all rooms and whenever it is more North, east, south or west of the room in the list it will replace that room. Afterwards I check the position distance between the North and south room and the distance between East and West and select the 2 with the greater distance.
Agent based level design
When I first implemented the agent they all started at the same location. During the guilds meeting I got the feedback that I should decide the dilemmas I had at the time by aligning it with my goals. The question “what is a good level” came up in discussion. When I think about it, the first thing that came up was the game maker toolkits from Mark Brown. Especially boss key episodes. In those episodes Mark talks about the layout of Zelda levels. I had a hard time finding a solution to tweak the layout in such a way that the key always came before the locked door for example. Therefore I decided on a new approach based on section.
I now spawn one agent that does his thing then uses the start and finish method from before to find the start and end of the section instead of the level. Then at the end of the section I spawn a new agent that is restricted in such a way that it doesn’t go back over the previous section.
For Example when a room with the location X = 100 and Z = 80 is the most North room it will spawn a Room at X 110 and Z 80. Then a locked door can be placed in between and a key can be placed in the previous section(I wasn’t able to implement this yet). Afterwards a new Agent will be placed with a constraint X value of 120. This way It won’t intersect with the previous sector.
After the agents return the location of the rooms every room will have an empty room object. Not all rooms are the same. For example rooms that are put on the outside need to be closed on the outside otherwise a player could run out of the level. To determine the room that needs to be placed we simply check if there is a room on every side.
After the number of entrance is checked. We need to determine which room is placed. As you can see below the script takes a list of prefabs of every entrance count. Furthermore there are 2 room types for 2 entrance rooms because a room with 2 entrances can be a corner or a corridor.
As you can see the 2 corners have 2 prefabs, everytime a corner needs to replace one of the two will be selected.
In the code below you can see how the rooms are selected. Note that the corridor is chosen whenever the opposite entrances are open.
You can be pretty creative with the prefabs as long as the entrances are inline with the center of the room. Other requirements are the room component script and the object spawn location, but more about that later.
After the rooms are placed the room will be rotated based on the location of the neighbors. It is therefore important that all the rooms are built in the right orientation otherwise they are not rotated correctly. As you can see in the images below both perfabs are built with the opening on the red axis(X) and the blue axis (Z).
After rotating the level appears as below.
2.4 Object placement
I first wanted the generator to look for places that aren’t filled by walls or pillars. I thought that would go against the goal of letting developers have a great deal of input. Therefore all object placement needs to be placed by hand. I admit this isn’t the most efficient way of placing spawn areas. One of the things I would have liked to add if I had had more time is a system where developers can make a big area where they want to spawn objects and enemies which is then divided into 1 by 1 squares by code.
One problem I had was that I initiated the seed everytime i placed objects. This meant that al objects were placed exactly at the same spot.
The object can also be input by the developer just like the rooms.
Results after object have been placed.
2.5 End product
After implementing the methods above, inputting the required data and pressing the generate button these are the results.
Lastly, here are some examples of how the variables affact the dungeon.
The Sections variable just lengthen the dungeon
Density increases the amount of objects and enemies that spawns.
Enemy is the amount of enemy in comparison to the amount of objects placed.
In this chapter I briefly explain a couple of concepts I would like to add to the generator if I continue to work on it.
In Enter the gungeon the exit of the treasure rooms is sometimes visible at the start of a section. This gives players incentive to explore that area and find the way to the treasure.
Right now all objects that are put in will be placed in every room. I would like to add a function where rooms can have different types so that some items are only placed in that area. For example a fridge will only be placed in a cook area.
Furthermore all rooms now have the same object density and enemy ratio. If this would be different per room it would cause more diversity and make the level less repetitive.