New Beginnings at the Facility

So this post is a few days late, though not without reason. Firstly, a couple of friends of mine have been in town for the past week or so and on Sunday night we took a trip down to Kings Dominion to ride the rides and check out some of the Halloween stuff they had going on down there. It was a blast, with the new ride they have really throwing me for a loop. The second reason is due to a particular game release. If you know me, you know that I love automation games, specifically Factorio. While it’s not my highest playtime game on steam (that title is still held by Garry’s Mod even after all these years), it’s very quickly making its way up the ranks.

My top 4 played games on Steam

Now, if you’re at all familiar with Factorio, you may know that its first (and probably only) expansion just released on Monday, called Factorio: Space Age. This has lead to me spending the entire day Monday and also a fairly sizable chunk of Tuesday playing the expansion. If you’re curious (and willing to withhold judgement), my save file that I started a few hours after the release of the expansion has nearly 20 hours logged already as of the writing of this post. All that to say, I’ve been slightly distracted by this game for the past couple days, but I do have stuff to show off from last week, so lets get into it.

Last week’s post was all about my decision to create a new project file to work on Maintenance Crew in. I’m happy to report I’ve done that, and even on a new engine version (5.4.4 instead of 5.2.1). Additionally, a vast majority of the functionality of the prototype and even some new stuff is in the new project file. Before we get to that though, lets talk planning.

Since I was creating a new project file for this game I already have mostly worked out, I figured the first thing I should do before I do anything in the project is plan out the gameplay loop. I did this through a flow chart that I made on draw.io, and here it is!

Maintenance Crew gameplay loop flow chart

The goal with this was to abstract a general idea of how the game would work. It doesn’t account for every single aspect of the game design, but it’s useful for keeping track of the broad strokes. The chart starts all the way up in the top left with the “Launch Game” bubble. As you can see, this flow chart includes not only the game but the menus necessary to get into the game. From the menu, the player can access options, host a game, or join a game. Hosting a game will put the player in a new lobby and open that lobby up to other players. Joining, on the other hand, will scan for available lobbies through the Steam Online Subsystem. The players will spawn in a sort of waiting room level as players join. Once everyone has arrived, the players can then make their way to the elevator. This elevator essentially acts as a sort of seamless loading screen for the game, so the dungeon can generate while the doors are closed. From this point, we start getting into the specifics of how the generator should work.

The way I’m handling generating levels is through Level Instances. This allows me to create a new Floor level every time the players return to the elevator as well as completely unload the last floor level at the same time. Due to this, every time the players change floors, the old floor generator is deleted and a new one takes its place. This isn’t a massive problem however. Each time a new floor generator spawns, it immediately makes a call to the game mode to find out what floor its meant to be generating. This works because in Unreal Engine, the Game Mode and Game State are both persistent to the overall level and can be relied on to hold info so long as the main level doesn’t change. At this point, the generator can grab all the info about the floor its generating from a data table, including things like tile sets, number of rooms, objective types, and anything else it might need to know. After doing this, the generator can determine which objectives the players will need to complete.

As you can see, this creates a bit of a branching point. Some objectives might require a particular room to generate right outside the door. If this is true, only one of these objectives can be picked. For example, I might have an objective where the players must grab a part from a maintenance closet, which is right by the elevator, then go find a room to repair using that part.

Once the first room is generated (if necessary), the floor can generate as normal. Once half (or some other percent) of the allotted rooms have generated, the generator can pause again here to generate other necessary objective rooms, like from our example before. If there are none, or once this finishes, it can generate the remaining rooms. As you can see though, this isn’t the end of the generation. The generator can function in “sections” so that the floor can have distinct areas with different tile sets. These would generate in a particular order, indicated by their listed order in the data table. At this point, if there are more sections to generate, the floor will generate that next section until it finishes all sections in the floor data. Once finished, the generator can move on to generating any necessary items for objectives before moving on to generally populating Points of Interest with anything.

At this point, the elevator doors open and the players start to engage with the newly generated level. As you can see, this section of the flow chart is the most abstract of the bunch, only taking into account a small handful of possible player actions. These particular actions were picked because they lead to important gameplay loop events down the line. Firstly, you can see that if a player dies, they get to spectate the game. This would be in a third person view, allowing them to see what their fellow players are doing while they wait for the round to end. If all players are dead, then the game is over and they return either to the main menu or the lobby.

The other option off this decision is slightly confusing in how I presented it, so I’ll explain. The “players interact with level” node makes an assumption that the players might go do whatever they like, be it exploring, collecting items, or completing objectives. So once they return to the elevator, this is where we can start to see some meaningful gameplay loop elements again. The first of these is checking if all the objectives are complete. If they are, the players can leave. If not, they still can so long as they have emergency departure charges available. If none are available, the players cannot leave until they complete the objectives. In either of these cases, anyone in the elevator leaves and anyone outside it dies. If they leave normally, they get to move on to the next floor. If they use a charge, they stay on the same floor, though the floor is regenerated. The in game explanation of this would be re-inserting into a different section of the floor. Regardless of the method, players are then rewarded money for any objectives they completed, dead players respawn in the elevator, and then the players can spend their money at the company shop, also conveniently located in the elevator. From this point, the game loops and we generate a new floor.

With that the gameplay loop is pretty much complete. I still need to work out some specifics like how objectives are handled, how to deal with a floor having multiple sections, how the objectives interact with that at generation time, and some more gameplay level features such as inventory and sprinting, but the bones are all there. To end this blog post, I’d like to show off a few pictures and videos of the project as it currently stands.

The lobby level, as it currently stands. 99.99% a place holder, but it does the job of being spooky and mysterious.

The elevator, seen from the outside, inside the large building shown previously.

Inside the elevator.

The elevator in motion. Taken before the floor generator was implemented, but shows the level instancing in action.

The floor generator (in its most basic, prototype form) functioning in the new project file. Notice the debug information in the top left corner of the viewport. This information has been set up to be toggled in the future.

Previous
Previous

Running in Circles

Next
Next

Moving from Proof of Concept to Prototype