Why Generate The Levels?
Mostly for re-playability purposes. With the ever-increasing dynamic difficulty, perma-death, and the daily and weekly challenges, we knew players were going to be playing though the same stages multiple times, and we wanted to give players a more interesting experience by making sure the actual levels were different.
How It Works
Because Seraph is so focused on movement, it would be really easy to generate terrible levels, by making them too small or too cluttered, or even just making them not interesting to fight in. A good level in Seraph is essentially made up of several ‘arenas’ where you fight some collection of demons before progressing to the next one. This design is based off a couple of hand-authored levels that our designer made for the prototype, once we started the game for real, I just had to write some code to make levels that were similar.
In order to get a nice balance between hand-authored awesomeness and generated re-playability, I created a system where hand authored rooms were stuck together using some logic to keep things interesting.
Anatomy Of A Room
A room is made of groups and blocks. Groups are themselves made up of blocks. Each block (and group) has a % chance of appearing in any instance of a room (each room has some data to say how many times this room can be used in a level). With this system, our designer could design rooms that ended up looking quite different depending on which blocks/groups were enabled. Groups are there so that a collection of blocks can be shown/hidden together, so you don’t end up with nonsensical combinations. There are many types of block, some represent the physical are, the floor and walls, whilst others are decoration, and some are special (lockers, enemy spawns, etc.).
A room will have a number of doors. Doors are a special kind of group that contain some blocks that make up a wall or floor, and optionally, some other blocks. When a door is used, the group is hidden (so there will end up being a gap in the wall/floor). Making the doors a group means that some decoration blocks can be added to be shown if the door isn’t used [pic]
We had over 150 of these room templates by the time we shipped, leading to a lot of variety.
Joining Rooms
The process of creating a level is fairly simple, we start off with a random room, and then repeat the following:
1 2 3 4 | for each room type that can still be used for each door on that room type if all filters say placing a room here is valid come up with a score for spawning the room there |
Most of the logic for placing the rooms is in either a filter, or a scorer. Filters stop invalid placements, for example if the doors are not the same size of facing each other, or if placing a room there would cause it to overlap with another room. Scorers help us to nudge the level generation into more interesting directions, and include such things as ‘prefer to use all the doors of a room’ and ‘try not to make a large vertical shaft’. There is also a ‘random’ scorer to help give a bit more variety.
This process is complete when either no rooms pass all the filters, or we reach the designer set limit for how large a level should be at this stage of the game.
Once this process is complete, we hide all groups for doors that are being used, and hide a random selection of the other blocks and groups.
Populating Levels
So now we have a level made up of several rooms, we need to populate it with some fun things.
Firstly, we pick a random room that has an elevator block, and make that the ‘entrance’ room, this is where the player will start. Then we pick another room with an elevator to be the exit, where the player will need to get to to complete the level. There are some constraints on this, the elevators have to be a certain number of rooms away, so it might take a few attempts to find an elevator that is sufficient. Once that is done, all over elevator blocks are disabled.
Then we pick a room with a boss spawn to be the boss (or some rooms with fountains if the level is a fountain one), again all other blocks of this type are hidden.
Next, if the level will have an ambush room, we pick which rooms will they will be in. There’s a special block that rooms can have that mark it as an ambush room, these were hand-placed to ensure that they would make an interesting arena (since the player will be locked in there for a minute fighting).
The last special type of room is what was called an event room, this is the room that is locked off with a switch that was elsewhere in the level, and generally filled with goodies, these rooms are placed procedurally, by simply finding a dead-end room (a room where only one door is used) that isn’t already used for something else (ambush, boss, or elevator). Once this is placed we look for a door switch block (which have been placed in a number of other rooms) that is a certain distance away, and join that to the door on the event room. We path-find from this switch to the door to know how long to make the timer (once you’ve pressed the switch the door is only open for a limited amount of time).
Weapon and Item lockers