Pages

Wednesday, March 11, 2020

Procedural Dungeon Generation

I was inspired several years ago to create my own procedural dungeon generator after reading a reddit post on the subject (also explained here).  I came up with an algorithm that was pretty simple and generated some pretty good results.

Below is a screenshot  and animation showing how the dungeon is being created at each discrete step.  I'll talk through each step one by one.

Final result of a procedurally generated dungeon.


Algorithm

In my code, I call this the tunneling algorithm.  The idea is to create a series of rooms and connect them by "tunneling" from one room to the next.  The tunneling route is the result of an A* path finding algorithm in which each cell is given a movement cost.  Cells which are floor tiles have a small movement cost and cells which are wall tiles have a high movement cost.  This results in the path route leaning toward using open floor cells over chiseling out wall tiles to get to the destination.  Depending on the layout, however, the path finder may decide to break through a wall instead of going through an existing room/hallway.  This is how circular loops and multiple entrances to a room can be generated throughout the dungeon - which is a pretty nice feature.

Room Generation

Rooms don't just have to be rectangles.  I create rooms by combining a random (usually 1-4) number of rectangles together.  I start with a single rectangle as the root, and then connect other randomly sized rectangles to a random edge of the root.

A single rectangle connected to the root rectangle to generate a room.

It's OK if rectangles overlap.  It can give the room more variation this way.
Two rectangles overlapping each other as they are connected to the root.

This simple technique can give some nice results to create a room.
Example room with 3 rectangles.

Example room with 4 rectangles.

One by one, a room is generated near the average room position on the map and then separated from the existing rooms by pushing it away until there there is no overlap.  The gap between the new room and the existing rooms can be configurable.

Once the room is separated, a path is generated from the previous room to the new room using the A* pathing method described above.  The map then generates new cells along the route in order to connect the rooms. 

The weight of the floor and wall tiles can be adjusted to provide different results.  If the wall tiles heavily out weight the floor tiles, then the algorithm will rarely attempt to tunnel through a wall in order to connect the room.  If the difference between the wall and floor tiles are small, then the algorithm will frequently path through existing walls.

The screenshots below show how the A* algorithm is searching through the map in order to connect each room.

If the cost of moving through an existing floor is significantly cheaper than moving through a wall, the A* algorithm won't even consider pathing through a wall.  The image below show the A* route using a floor cost of 0 and a wall cost of 100.
The search space (shown in orange) as the A* algorithm finds the optimal path.

If the difference between the floor and wall movement costs are small, then the A* algorithm will consider pathing through more walls.  The image below is generated from a floor cost of 30 and a wall cost of 100.



Depending on the map layout, the start position, destination position and the the floor/wall movement costs, the tunneling can create some nice results.  The below image is an example of where tunneling through a wall was cheaper than pathing around to the existing room entrance.
Room which needs to be connected to the rest of the map.

Path tunneled through a wall in order to connect the room to the map.


That's pretty much it.  I didn't go into detail on how A* is implemented, so that may be a future write up.  This algorithm can also be expanded to use a key/lock system in which rooms are broken up into different sectors and given keys to unlock doors of the next sector.

Here are a few more animations of the algorithm at work:







Saturday, March 7, 2020

A New Game - Game Pillars, Requirements and More.


I've been working on a game concept for a while now which I initially began in order to prototype multiplayer networking techniques.  It's starting to take more shape into some sort of MOBA-dungeon-crawler type game.

As with all game ideas, this one started out very ambitious and quickly had to be scaled back to something much more manageable.  

I still don't know what the game is going to be, but I've given myself some requirements up front:
  • The game shall support a persistent world.
  • The game shall support player run servers.
  • The game shall support roughly 10 simultaneous players
  • The game shall support replayability.
  • The game shall focus on gameplay mechanics above artistic style and story.
It's always a good idea to give yourself some constraints when starting a project.  This helps keep the project in focus and not let feature creep set in.  Given a handful of simple high level requirements like this, we can start to make more specific design decisions.

In order to support a persistent world, where players can log on and off at their leisure on player run servers with a no more than 10 players at once, I've decided the client-server model fits best.

In order to create a pillar around strong game mechanics above art and story, I've decided a 2D top-down rendered game using simple geometric shapes fits well.  

In order to support game replayability, I will lean heavy on procedural generation to create most content and player mods.

Find your why.

I jumped ahead of myself.  I've just listed a bunch of high level requirements that the game needs to meet, but where did they come from and why are they important?  What are the fundamental messages for your players?  What kind of experiences and emotions are you trying to create for them?  Once you understand the answers to these questions, you can start to identify the the core pillars of the game and every design decision should somehow link to them.

Most of my requirements and subsequent design decisions feed into these core game pillars:
  • Teamwork
    • Players should be able together as a team and work cooperatively toward a common goal.  They should feel a sense of camaraderie and feel how their contribution impacts the team.  
  • Continuous Improvement
    • With every play session the player should feel they've increased in power - whether it's earning experience points, loot, or increase stats, etc.
The rest of my requirements are simply non-functional.  They are constraints either due to time/budget or lack of artistic ability.

Screenshots

Finally some initial screenshots my progress.  It's currently implemented with client-server networking model including player movement, chat, and procedural dungeon generation.  More to come!
Login Screen

In-Game Diagnostic Menus and Tools

Player Movement

Procedural Dungeon Generation