Procedural Dungeon Generation: A Roguelike Game

Favourite 80 favourites
Tutorial written by stemkoskiOriginally published on 6th, August 2014

Overview

In this tutorial, we will learn how to create a Roguelike-style game. This style involves "procedural generation", which means that the level design (rooms, hallways, player/item/enemy placement) is randomly determined every time the game starts, but the layout is generated intelligently (in particular, every room can be accessed by a hallway leading to it).

Final Result of this Tutorial


Here we will be implementing the algorithm presented at http://gamedevelopment.tutsplus.com/tutorials/create-a-procedurally-generated-dungeon-cave-system--gamedev-10099 using Construct 2. The main steps in this process are:

0. Fill up the level with small squares with the Solid behavior.

1. Create a total of N rooms with random sizes at random locations.

2. Create a horizontal hallway and a vertical hallway between pairs of rooms, linking room 1 to room 2, room 2 to room 3, ... , room N to room 1 (we have chosen to connect the last room to the first so that there are no "dead-end" rooms in our level).

3. "Dig out the rooms." In other words, remove all the Solid objects that overlap room/hallway sprites.

4. (Optional, but recommended for efficiency): Remove any of the remaining blocks that do not border a room/hallway.

5. Place the player in a room, place an item in each room, and place enemies in some of the hallways.

Stone and Room Placement

First, in this example we set up the project with a layout size of 1600-by-1600 (and when we are finished, a window size of 400-by-400, but for testing purposes a window size of 1600-by-1600 to match the layout size so that we can visualize the entire level). We use nested For loops to generate a grid of objects named "Stone", which are 32-by-32 and have the Solid behavior. We also create a TiledBackground object called "StoneBorder", using the same image as the Stone object and also having the Solid behavior, and we create four 1600-by-32 versions of this object and position/rotate them to use as boundary walls for our level.

Then we create a certain number of rooms, randomly setting their size and position as shown in the event sheet below. It is possible that some rooms will overlap, but that's okay -- the end result will be interestingly-shaped rooms that contain more than one item.

For each Room, we spawn an associated object called a RoomCenter, which will be useful (for filtering purposes) when we need to create the Halls. We also set an instance variable called "ID" for Rooms and RoomCenters; the hallway objects we create will connect a Room with ID=X to a RoomCenter with ID=X+1. In order for the final Room to connect to Room 1, we use a little trick: change the ID of RoomCenter 1 to be one more than the total number of rooms.

Events for creating Stones and Rooms


At this point, one possible level that could be generated looks like this:

Level Layout, Part 1


Hallway Placement

Next, we connect the Room with ID=X to the Room(Center) with ID=X+1 using two hallways: one horizontal and one vertical.

Horizontal hallways need to have width equal to the horizontal distance separating the rooms, calculated using abs(Room.X - RoomCenter.X), and the height can be any constant, somewhat larger than the player/enemy sprite dimensions. These hallways will be placed to align vertically with the Room (set Hall.Y to be equal to Room.Y); since the sprite anchor is in the center by default, the X coordinate needs to be the midpoint between the two rooms, calculated using (Room.X + RoomCenter.X) / 2.

Vertical hallways are sized/placed similarly, except that horizontal/vertical calculations are switched. The width is a constant and the height is abs(Room.Y - RoomCenter.Y); the X coordinate should be equal to RoomCenter.Y and the Y coordinate should be equal to (Room.Y + RoomCenter.Y) / 2.

Events for creating Hallways


At this point, one possible level that could be generated looks like this:

Level Layout, Part 2


Border Placement and Stone Removal

At this point, we could just destroy all the stones that overlap either a Room or a Hall, and we would have a functional level. However, this leaves lots of extra Stone sprites that require additional rendering time, and that can never be collided with, so they don't influence the gameplay. Therefore, we take the extra step of creating Border objects: set each Room and each Hall to spawn a Border object whose size is set some fixed amount larger than the Room/Hall that spawns it.

Then when removing Stone objects, we want to destroy any object that overlaps a Room -or- overlaps a Hall -or- is not overlapping any or the Border objects. This latter condition is a bit tricky to implement. It is tempting to set up the event:

Condition: Stone X (invert) is Overlapping Border --- Action: Destroy Stone

However, it is almost guaranteed that every stone can find one border that it is not overlapping, thus the condition will be true for all the stones and they all become destroyed. So, instead we set up two events: give the Stones a boolean instance variable called StoneBorder, initialized to false. After the Borders have been created, if a Stone is overlapping a Border, set the value of StoneBorder to true. Finally, destroy all Stones whose StoneBorder variable is set to false.

Events for creating Borders and removing Stones


At this point, one possible level that could be generated (prior to Stone removal) looks like this:

Level Layout, Part 3


Player/Item/Enemy Placement

At this point, you could place an item in the center of each Room for the player to collect, place the player in Room 1 (offset a bit from the center of the room), and randomly choose a few Halls to place enemies in (placing enemies in Halls rather than Rooms reduces the chance that they will be too close to the place at the start of the game. To make the enemies interactive, you could add the Line Of Sight behavior, and have them chase (move towards) the player when the player is in sight (I usually change the range of this behavior to half of the window size, so they only begin to chase you when you can see them).

Events for placing the player, items, and enemies


Finishing Touches

At this point, you will want to make sure that your window size is smaller than your layout size (since the game wouldn't be nearly as fun if you could see everything at once). Also, you should make all your Room/RoomCenter/Hall/Border objects invisible; they have served their purpose. It is also a good idea to include a GUI layer (with Parallax set to 0,0) containing a Text object that states how many objects you have collected and how many remain. These features have been implemented in the Capx file attached to this tutorial. Other fun additions to consider include giving the player a weapon to swing or fire at enemies, or rooms that contain traps that you can lead enemies into to destroy them. Lots of possibilities exist, your imagination is the limit!

Unlock your full gamedev potential

Upgrade to the Personal Edition of Construct 2, it has way more features and won't holding back from making money and using your full creativity like the free edition does. It's a one off payment and all Construct 2 editor updates are free for life!

View deals

Plus, it's got a lot of additional features that will help you save time and make more impressive games!

Congratulations on finishing this tutorial!

Did you learn a lot from it? Share it now with your friends!

Share and Copy this Tutorial

You are free to copy, distribute, transmit and adapt this work with correct attribution. Click for more info.

Comments

1
27Jollyloony 1,843 rep

How do you make the ghosts randomly move in different locations.
Like pathfinding.

Thursday, May 12, 2016 at 1:24:02 AM
0
UncleG 503 rep

I am very new to C2 but know a bit. Do I need an array for this type of game and is array available on free edition?

Thursday, May 12, 2016 at 1:49:33 PM
0
silkc2 3,116 rep

A great starting template; thanks!

I think I'd add the option to remove corridors as well (so long as a room has at least one remaining after), for some dead ends.

Wednesday, May 18, 2016 at 9:52:17 PM
0
TD Bauer 4,677 rep

Very nice... I am working on a Roguelike game at the moment. This gives me a few ideas to think about.

Thanks!

Tuesday, December 13, 2016 at 3:24:43 PM
0
UberdroidGames 8,784 rep

Nice. I did something similar, but instead of making "border objects" I just made the room and hall objects temporally bigger to do the collision check and cull the non overlapping tiles.

Also it is necessary to have a tick happen between creating or destroying any objects and detecting collisions on them. So if you create your stones then try to cull them based on collision it won't work unless there's a "wait 0" somewhere in there.

Great work!

Thursday, April 13, 2017 at 9:18:55 PM
0
artheads 2,859 rep

Hi, is there any way to do the same but using tilemaps instead of sprites?

Monday, August 14, 2017 at 6:53:19 AM

Leave a comment

Everyone is welcome to leave their thoughts! Register a new account or login.