Mind the Traps is designed to be a fun-filled party game that you play with your friends for a few rounds, kinda like Mario Party and Super Smash Bros. For those reasons, it needs to be action-packed, short and replayable. Originally, we had a few options, such as creating a large amount of levels and adding character classes, but given that this would be our first commercial game we decided to go with a more risk averse approach—procedural generation.
So far, it’s turning out pretty good for the basic floor plan.
If you had a chance to play the prototype, you’ve probably noticed that the game is broken down into pathways and mini-game rooms. We’ve continued with that method of level design and created a blueprint that procedurally generates pathways and rooms at the beginning before the game starts. It follows the simple logic of: for every 2 pathways (white colored blocks), a mini-game room (black block) is created. The final room (red block) is a boss room.
All of this is kept uniform by following a simple set of rules:
- All rooms and pathways follow a simple grid system, where every 1000 by 1000 UE4 units are considered one “unit” in our grid.
- All rooms and pathways origin position are the centerpoint of the first 1 by 1 unit. So that if we have a room that is size 3 by 3, the centerpoint is at 1 by 1, the bottom-left-most corner of the square. This is so that calculating the entrance and exit positions of each room and path is uniform and easy.
- Every path and room is entered from the south, meaning the players only enter each room and path from the bottom of the room/pathway.
- Every path and room is exited from the north, meaning the players only exit each room and path from the top of the room/pathway.
Each previously created pathway and mini-game room is randomly chosen from preset arrays. Having arrays gives us the flexibility of adding more pathways and rooms in the future.
Generate Entire Level at BeginPlay
For our procedural generation, everything is done in the Level Blueprint at BeginPlay so that everything should be done within the first couple moments of the level loading up. First off, you’ll notice we have the switch “Has Authority” to make sure only the server generates the level. Next, we have functions and custom events “Init Vars” to initialize variables and “GenerateRooms” actually spawn the rooms.
First, we need to make sure the variables we need in order to create the rooms are correctly initialized. This part is especially important to make sure that the same rooms and pathways aren’t used the same time twice in a single playthrough until we run out of rooms and pathways.
Here, the array “PathsV” represents the preset array of vertical pathways we have set in the level. “CurrentPathsV” is a placeholder array to hold all the vertical pathways during generation. While the paths get created, we find the index of the room chosen in “CurrentPathsV,” and remove that index from the array, reducing the size of the array, and making sure that the next pathway isn’t the same as the one removed. Eventually, if we have very few pathways created, or if the amount of rooms/pathways generated is large, the “CurrentPathsV” array will empty, and we can simply call this function “FillCurrentPathsV” again to repopulate the array and begin anew.
Event: Generate Rooms
Here is where the event “GenerateRooms” goes. In it, we first make a “SpawnFirstPath” so the players have a nice peaceful starting point. After, we have a while loop that runs until we spawn enough rooms and pathways to equal the “NumberOfRooms” variable. The “RoomCounter” variable is incremented every time SpawnRoom spawns correctly. Once it’s all done, we make one last pathway and attach a Boss Room at the end of it all.
Spawning Rooms, Paths, Boss Rooms, or First Paths
This is the function “SpawnRooms” which spawns our rooms. The other functions “SpawnPaths,” “SpawnFirstPath,” and “SpawnBossRoom” are pretty much identical with the exception of using different arrays. The “SpawnPaths” function also has an added RNG function that chooses whether to generate a vertical, left, or right pathway.
First off, before we can spawn the actor, we need to make sure that the array “CurrentRooms” is actually populated, or else we run the function “FillCurrentRooms” to do so. Next, we pick a random index and spawn that object in that index and remove the index from the “CurrentRooms” array such that we don’t spawn the same room twice. The transform that we spawn the actor at initially is arbitrary as the room will be moved anyway to it’s correct position.
Next, you’ll notice I cast to the RoomVariables class, which is simply just a parent class that all of the rooms created are children of. This parent class has the vital information we need to place the rooms correctly, namely the position of the “EntranceLocation,” and the “ExitLocation,” which are 2D vectors that tell me the location in respect to our “grid” system. In this section of the “SpawnRooms” function, you’ll see we use a function called “CalculateEntranceLocation,” which simply finds the vector at which we need to move the room to based on the entrance location. For example, if we have a 3 by 3 unit room, with an entrance location at 1 by 2, we need to calculate the world location where it is 1000 UE4 units by 2000 UE4 units away from the origin of the room and adjust the room to be placed correctly.
Finally, we need to set the “GenerationLocation” variable at the exit point of the room in a similar fashion, using the exit location of the “RoomVariables” to calculate where the next spawning point should be for the next pathway or room.
Afterwards, everything is ready so that we can continue creating rooms and pathways by running the same functions until we have a complete level.