Opulence

Procedural Level Generation

My favourite part about Opulence is the procedural level generation. This was achieved through a lot of trial and error, but I’m really happy with how the final result turned out, especially since, on average, the entire level generation process takes only about 3-4 seconds.

I started by sampling Perlin noise to decide whether a tile would be spawned on a given coordinate in the level space. To reduce the randomness, and encourage more natural sections of path, there’s a small threshold that has to be met between consecutive coordinates - if it’s too small a gap, it uses the same value as the previous coordinate.

After that initial generation is done, two “clean up” passes are made. The first one goes through and fills in any empty tile that has at least 5 neighbouring/adjacent tiles. This “fills in” the level, and removes some weird-looking parts where there’d be just a single bit of empty space surrounded by lots of tiles. The second pass goes through and removes any tiles with less than 3 neighbours. This part is to increase the open space, and remove any single tiles that are jutting up or out of an otherwise straight surface.

The next step utilizes A* pathfinding to assess whether the level can actually be completed. For this to work, the pathfinding algorithm needed to factor in how the player character can move. Given the randomness of the procedural level generation, there’s a very high likelihood that there will not be a viable path from start to finish. To counter this, I gave the pathfinding algorithm an option to “explode” a 3x3 grid of tiles whenever it fails to find a path. It always explodes the closest position it got to the exit, and once it’s done this it immediately tries to find a path again. After 50 explosions/failed attempts, I assume it cannot ever find a path, presumably because the only valid way to reach the exit is outside of the player character’s capabilities, so the level generation starts again from the beginning. Once it does find a path, the loading screen gets turned off and the player can start!

The final step involved reworking the pathfinding algorithm to act within the same constraints a player would have (eg, can only jump across a 3 square gap). This led to the pathfinder failing to find the exit almost every single time, so I had to go back to the drawing board to think of a solution. I ended up giving the pathfinder a bit more freedom than a player would have, but then adding blocks adjacent to the optimal path. I then added a secondary path from the opposite corners, and added blocks to this as well. The final result looked a lot more like a platformer, as there were a lot of extra small platforms to traverse.

It was definitely a concern that the added blocks would be obvious to the players and they’d just follow them, defeating the entire purpose of procedurally generating the level, but it thankfully is not obvious whilst playing, unless you specifically know what you’re looking for (or, you’ve read this explanation!).

You can see the early pathfinder and the final level generation in action over on the right (or below if you’re viewing on your phone), and scroll down a bit further to see full playthroughs and the boss fight, as well as a link to play Opulence.

I hope you have as much fun playing it as I did programming it!

Early version of pathfinder in action, prior to taking character movement into account

Final version of the level generation in action, drastically slowed down for visual clarity

Full Gameplay Walkthrough

First Boss Fight Walkthrough

Opulence - Menu UI