Skip to main content
GameDev.net gamedev.net

PRO Tired of ads? Read GameDev.net ad-free and help keep the community independent with GameDev Pro — $3/month.

PITVYPR Dev Blog #8: Level Generation for a scrappy, collosseum fantasy

PITVYPR Dev Blog #8: Level Generation for a scrappy, collosseum fantasy

BerneyTD
BerneyTD
PITVYPR Development · · 9 min read
939 1

Hello!

I've been tackling some concepts that I hadn't expected to be looking into for the Demo during the past week, and I think that he techniques and insights I've learned over the time period are worth recording here for some anyone to reference in the future.

Of course, I'm talking about Procedural Generation, the famously beloved and almost mandatory golden child of the Roguelike genre. How can you have replayability without random generation??? (Well, you definitely can, but we won't be discussing that here much today). Proc Gen is how most Roguelikes, including PITVYPR, create a near-endless variety of maps for players to explore in each run.

Motivations

Highly varied maps are, in my opinion, a secondary concern for the replayability of PITVYPR. Having a big cast of enemies and items that interact with the surrounding space differently will provide a large enough learning curve for players initially, that also needing to worry about specific environmental effects and concerns could become overwhelming. However, its importance has become a lot higher than it was before:

Recently I've been “researching” other Traditional Roguelikes in my non-development free time, and I've been immensely impressed with the claustrophobic and dense atmosphere of maps in games like Brogue and The Ground Gives Way. Playing the current version of PITVYPR - with its large, wide rooms and long coridoors feels quite lacking in comparison.

The current static PITVYPR Demo map - White is walls, Black is floor, Red is doors (Yellow Locked) and Magenta is items. You start in the bottom left and can explore from there. I tried to make a few differnet room shapes to test how gameplay works under different circumstances.

The feeling of being surrounded, lost, or overwhelmed is quite hard to come by in the current PITVYPR map, and there are a few reasons for this, I believe:

  1. Large Spaces
  2. Low Enemy Pressure
  3. Low Tile Variety
  4. Low Shape Variety
  5. Overpowered Player Items
  6. It is a static map, so there is no variety in scenery. (This is the most important!!!!)

Some of these are definitely resolved via non proc-gen methods (player items are gifted around every corner in the current Demo map, something that will definitely change for the sake of balance) and will be adjusted independent of how levels are generated. However, they all factor into the decisions we make when choosing our actual Procedures for generation.

Criteria

It's all well and good knowing the issues with the current level setup, but we can't act on achieving a coherent vision if we don't have a clear set of goals for our proc-gen to achieve. To do this we will, again, appeal to the desired “Fantasy” of PITVYPR. It has changed several times throughout development (this game was meant to be hyper resource heavy and crafting focused - but those have taken a far back-seat currently until a game can be built up around dropped items, instead of crafted ones) but I do still have a semi-clear vision of the general experience I want to emulate with a playthrough of PITVYPR. Typically, I like to do this with writing: setting out a kind of ideal scenario/set of scenarios that paint a picture of what would be really cool, fun, scary, etc, to happen.

So, what is our fantasy? Well, considering the context of the Alpha Demo as stricly within the confined colosseum pit, we can channel most everything into a single level:

Walls of flimsy metal, broken stone and slabs of concrete. Flooring that is of the same materials, but is layered upon matted, dense cables that pulse and hum softly with electricity… or perhaps something else.

Creations of metal and flesh, pulled together from the rotting remains of living creatures that have been mangled inside of the collosseum over the many years of Pit Fights. They lumber and move with jolts of lightning crackling under the skin, and chase down the living with a dire will.

The player opens the door only to face a Minotaur. It spots them immediately, and beings its slow walk towards them. Closing the door would be useless, as it will break it down in just one turn. Behind them, in the room just 5 tiles away, The Martyr (Level Boss) is going to be un-stunned in a few turns, and his charge-beam will be ready within that time, too. The player is out of ammunition - there might be some in the chest south, but he can't be sure. The minotaur has a charge attack that could push the player across the chasm, into The Martyr's range, allowing them to finish him off with the Plasma Slash and get access to the exit. But, they would need to get lucky, survive the minotaur charge, and then survive running from the minotaur until they reach the exit.

Some scene setting and a little paragraph exploring a potential “moment” of gameplay, where a player might need to make a difficult choice. Obviously its quite dressed-up, but I think we should aim for the stars when chasing fantasies.

With these in mind, we have some goals:

  1. The layout of the arena should be Structured, but not Organized. Pulled-Together and messy rooms, with lots of minor variation within the cells.
  2. The size of Rooms shouldn't allow for simple decision-making, and require thought to traverse effectively.
  3. Rooms should be expansive enough to allow the player to make a range of choices in any given instance.
  4. Rooms should be close enough together to affect and inform systems and decisions made in subsequent rooms.

These goals make some of the decisions for us: room maximum and minimum sizes, excluding certain generation methods, and the potential methods we might use to add detail to each room as it is made.

Those methods we choose to include and exclude from our map design our explained below:

Potential Methods

In this we are very spoiled for choice. There are dozens of proc-gen methods for us to test and choose from. But, it is nice to go in with a certain plan in mind.

To keep things simple, we will be picking one basic method of generation for the map, and then use finishing touches to tweak it into an appropriate shape each time.

Some of the methods to consider are:

Tunneling: Create a number of random, disparate rooms and then tunnel between pairs of them in simple L shapes.

BSP: Continuously Split a rectangle into smaller rectangles until they are all of adequate size, then make rooms in those rectangles and connect them with a Tunneling algorithm.

Cellular Automata: Create Cells with a set of rules (like the game of life) and tun them until satisfied. Apply some smoothing techniques to make the shapes more cohesive. Great for Caves.

Drunken Walk: Create a number of agents that walk randomly, carving out rooms of irregular shapes. Connect unconnected locations with tunnels. Also great for caves.

There are many more methods we can discuss and explore, but these are some of the more notable examples I considered for the game.

I even used some of these with differing results, some examples are shown below:

Before I had file-based level loading, I used a set seed on a simple Tunneling Generator to test monsters in the earliest version of the game. This was good for a simple space to move around in, but had some major problems. Mainly, the disconnectivity of its rooms. Meaning, rooms often had large corridoors between them, effectively disconnecting them in terms of the decisions made by the player. In gameplay, levels were so far away that it is easier to deal with the immediate threat as presented, without making use of the previous, known space in an earlier room. If we want maps to feel more cohesive, lengthy coridoors should be more of an exception than a rule in the way Rooms can be connected.

When I started messing around with proc-gen this week, I tried using a BSP division system to carve out some maps. While the rooms were often closer together, their layour was for too uniform to give a disorganized and Pulled-Together feeling.

BSP Results in a decent variety of room shapes, but a mixture of uniformity and snail-like level generation made it a poor fit for PITVYPR level generation.

I knew that I wanted the foundation of the Arena map to be room-based, so use of CA or Drunken Walk wouldn't be a good base for the level generation. Another approach was needed.

First Method: “Smashed Rooms”

So, this almost certainly is not the name for this method of generation, but it is what I called it for my class file, so it's what I'm going with.

A very illuminating interview from RPS with Brian Walker, the creator of Brogue, discusses his method of level generation. An interesting foundation was discussed here, which I decided to adopt for the basis of my proc-gen.

Create a room near the middle of the map. Then, create another room, and move it until it fits neatly against the previous room. Then, make another room and move it until it fits neatly against one of the other two rooms. Repeat until satisfied.

My method for this uses a few adjustments to create some slight structure in the generation.

So, first of all, the first room generated uses a shifted value range for minimum and maximum size, creating a larger hub room for the focus. This is also set as the starting room, currently, for simplicity.

Additionally, we create another large room with the same range every 6 rooms, creating a new hub that is elsewhere on the map. This room becomes the new focus. We do this 10 times, over 60 room generation attempts, for a 60x60 map. (Current numbers are easily adjusted for different effects, but these are the ones I use currently).

Then we add Doors connecting each room to the first room it collided with, and also add these to a Node/Tree structure for future reference.

On door creation, we discover that some rooms aren't 1-space away from their collision partner. For now, we mark these rooms and their children (child rooms are rooms that collided with the selected room.) as dead rooms, these are removed from the map to ensure connectivity across the entire level.

This results in a simple but solid base for additional adjustments. Some examples are shown below:

Some examples of the simple Smashed Room level generation. Knowing the rules, you can pick out the “target” rooms and how smaller rooms are pulled towards them, forming branches.

The method of pulling generated rooms towards larger target rooms would've, I hoped, contribute to the “Pulled Together” feeling of the Arena.

However, I decided that this method, both in implementation and theory, was unfit for PITVYPR's levels. There's a lot of post-processing to be done on each room to ensure things like pathfinding, and adding variation in room shape is difficult to achieve. Additionally, this method often leads to very frustratingly long “branches” which, when tested in-game, do not feel good to traverse. While this won't be used in the Demo's level generation, I still think Smashed Rooms could have a purpose in another type of level. Perhaps for areas with more structured rooms?

In the upcoming blog post, we will talk about the method I have been using mostly after smashed rooms. I have done a LOT of work on the generation since I began writing this one, and so there will be a lot to cover (perhaps over the course of multiple articles).

Thank you so much for reading!

Liam (BerneyTD)

Discussion

Loading comments...