@ : ProcGen AT ruggnar DOT com
Ruggnar is a platformer set in dark, gloomy places, where the player controls the dwarf Ruggnar through the dark as he searches for treasure. In this game, there is several game modes: Story mode, with handmade levels; and Random mode which uses procedural generation. In this article, we'll focus on Random mode. In it, I'll try to explain my method for generating levels as clearly as I can.
My first indie game project was an Android app called DualRUN. The player controls 2 spaceships at the same time to try and avoid the walls and corridors.
Development of my second game, Ruggnar, began on September 22, 2016. Just a few days later I made the decision to attend a monthly local indie games event in Metz, France. During my first participation, I was invited to work on a level generator and Spelunky was the game given as an example.
I really like Spelunky. I knew that the levels were generated, but I never looked into the subject before this meeting. I found an article about the method used in Spelunky, and after reading it was inspired to implement one in Ruggnar.
In DualRUN, the corridors were randomly generated, using compatibility rules: a border piece has a beginning (the bottom) and end (top), each of a certain shape; the compatibility of each side with another is specified; and the road is created by picking pieces according to how it meets compatibility requirements.
The compatibility rules are quite simple:
- - The bottom part of the added piece must be compatible with the top part of the previous piece, and
- - The width of the road must always be above a certain limit
That's it. It's simple, not optimal, but it works.
For Ruggnar, I decided to use this method again, but to upgrade it.
In DualRUN the levels are infinite, but in Ruggnar I need both a starting and end point in a closed space.
I chose to work on a grid with variable dimensions, made up of sections.
I decided to create every section by hand and give each section the same dimensions. I did this to be sure the player can properly complete a section.
Below is an example :
Every section has its own layout, with grounds, walls, decorations, hazards and coins.
It also has a starting point, a treasure as the ending point, a checkpoint, a key and a candle.
A section is characterized by its borders, with each border being a type of exit.
When the generator puts a section on the grid, it checks that the borders of the new pieces are compatible with the neighbours borders.
In the previous picture, there are grayed border cases. These are the exits, the open walls of the section.
Some example of exit :
|Type 1||Type 2||Type 3||Type 4||Type 5||Type 6|
A section is made of 4 borders, so we must pick 4 exits: 2 vertical and 2 horizontal.
The number of sections to make increase quickly, based on the number of different kinds of exit :
|Vertical exits (v)||Horizontal Exits (h)||# of Sections (s)|
The formula is : s = (v · h)²
It's important to create every section based on the exits you've chosen, or you'll face holes in your grid. on the other hand, if you only make the required sections, you'll end up with repetitive levels. That's why it's a better move to make several versions for each exit configuration.
In Ruggnar, I have another way to generate variety. In the previous part, I spoke about what we have in a section. Among all the sections in my final grid, I chose a starting and an ending point.
Hazards have a probability of occurrence, from 0 to 100%. The number of coins, candles, keys and chekpoints is based on the size of the final grid, so the same level section will quite be different from one game to another!
The game zone is a grid where each case is a section. When the process is over, the level is created with the sections of the grid. For the prupose of this article, I'll work on a 5x5 grid.
To start generation, I put some blocked sections on the grid. The grid won't be a simple square, but as some parts will be impassable, the structure of the level will be less straight, less predictable.
We can see 2 blocked sections in black. The walls will be filled in with no way to reach the blocked section.
For each unblocked section, the generator searches for compatible sections (among all the sections available) with direct neighbouring walls (North, South, East, West) :
- - Each wall must be compatible with its adjacent walls
- - The border of the grid will have closed walls. The area must be a closed one.
Here we can see that 1 more section was blocked on during generation. Notice that the walls are identical side by side.
This is a real level, based on the grid.
The sections have been filled with the tiles and decorations!
If the process were to be stopped here, and the content filled, we'll take a risk to lock the player in an area he can't complete (no exit reachable, or not enough keys). so the process has to include a flood fill algorithm to find every closed area.
Each color shows a distinct closed area. Not counting blocking sections, we have 11 red, 9 blue, and 2 green.
For the player to have the biggest playground, I keep only the biggest area. I'll clean up the grid and keep only the red area.
My process is almost complete! Now I generate the decorations and hazards to finish off the level:
With so many possible arrangements, Ruggnar has thousands of unique levels!
With Random mode, you have access to a lot of levels to play. Every game will be different than the previous, and the challenge will be renewed. This article will be updated later as Random mode in Ruggnar evolves.