Project Haunted House
The Game
A procedurally-generated, multiplayer horror game where you and your friends must search through a manor for clues about the haunting, the monster lurking in the darkness, and your means to escape.
The Mansion
Different layout every game.
Pre-designed rooms allow a sense of recognition inside this maze, yet still change and behave differently.
The Monster
You are not alone…
Different monster means a different mechanic to deal with. Change your play-style, adapt, and escape.
The Objective
Even the objective changes. Search the house for clues, use your tools, and identify your means to escape. Solving the mystery does not guarantee you will escape…
Progress





Development
The PCG
Most procedural algorithms out there did not fulfill the environment I was trying to create.
Voronoi/Delauney Triangulation
Place a bunch of rooms and move/align them using the algorithm to connect.
- Great for dungeons where you have large rooms connecting together. One implementation I have seen even used long stretches of hallways to connect rooms that normally would not.
- Not a good solution for a house/manor/mansion type landscape where there are hallways with ‘islands’ of rooms connecting.
Wall Recursion
One solution to generating house-like floor plans was to take a space and divide it up with walls with the remaining space becoming the ‘rooms’.
- Simple concept to generating a floor plan with random rooms and doors connecting to each other
- It loses all control to create handcrafted rooms as you would have to dynamically fill the room. Becomes difficult to make the rooms unique to each other. (BIG complaint about PCG content: Losing Design for Generated Content).
Cellular Automata/Perlin/Fractal/etc
Various other algorithms used in the past for PCG content.
- Great for caves or mazes as the randomness makes the environment look as if they came from nature.
- Another not great solution for house-like structures. Also loses control over designing the rooms similar to Wall Recursion.
So, I set out to create my own procedural algorithm that would open the possibility to use handcrafted, well-designed rooms within a generated space.
What I came up with is similar to the Wall Recursion, except instead of using the wall to divide of a space, I use the hallway. Hallways are drawn recursively to divide up the floor. The system then uses my Custom Data Structure (RoomLibraryComponent) to find the largest available room to fit in the space created in between hallways. The systems continuously placing rooms, rotating as necessary, and making sure doors line up. The best way to think about it is they computer is playing a game of Tetris to build you a haunted mansion. The systems knows which rooms are accessible (meaning the player can actually reach there) and can clean up rooms that are not reachable.
RoomLibraryComponent
The RoomLibraryComponent manages all the Rooms in C++. The component itself is similar to a linked-list of queues.
A single node has a queue of all the rooms of the same dimensions. When a room is taken off to be placed in the world, if it fails or is marked to be spawned multiple times, it can be placed in the exact same queue except at the end. This creates a cycle, so all rooms have the possibility of being spawned.
The linked-list scope has the ability to sort the queues based on the dimensions of the rooms it contains. This allows the generator to get the ‘Next Largest Room’ based on the available space to place the room.
Rooms
The Rooms are the designed individually to be placed in the map. Due to being made for the role of Game Designer (a role I am still filling), the Rooms are built using Blueprint.
Since the Rooms are in Blueprint and the RoomLibraryComponent/Generator are in C++, the base Room class needs to become the bridge between the two languages.
By using functions marked as BlueprintCallable or BlueprintImplementableEvent, I can either call a function in Blueprint that was defined in C++, or I can call a function in C++ that was defined in Blueprint. This opened the door to allow a two-way communication between the languages such as C++ getting a Vector Location of an object placed by the designer in Blueprint, or having the designer use a function built by the system engineer (C++) to be a used in a visual setting (Blueprint).
This way the Room could be designed to be unique, contain handcrafted textures, and have objects placed in specific locations all while having the back-end wiring that the system already understands and could be used immediately without creating the code for each individual Room.
Multiplayer
Unreal Engine 4 is designed around the Server-Client model for multiplayer. What this means is that everything programmed needs built around having the Server spawn objects, decide where they are, who has what items, etc… then broadcast that information to the clients. This brought a different kind of challenge.
For example, I needed to dive into the engine code to figure out the GameMode versus GameState. The GameMode object exists only on the server (another way to think about it is the “Host Player”). The GameMode typically makes the decisions about the game about to be played such as settings, objectives, etc. However, the GameMode cannot be reached from Client computers. The GameState is created by the Server and then replicated to all the clients which means the server and client can both view it. Changes made to the GameState will change the GameState for all copies (which is the idea behind state). In my case, I needed the Server to decide what the seed value is for the Game, so that the Objective would be decided, the environment can be built, what items needed to be spawned, and what monster will be roaming the halls. The clients also need to know that value so it can “simulate” the environment. With the GameMode and GameState concept in mind, I had the GameMode create the seed value, then when it creates the GameState (due to the lifecycle code of the engine), I could pass the seed value to the State which could then be given to all the clients. The clients could always get access to the Game that was built around the seed at any point: On Game Start, On Reconnect, Save Features, etc. This method is something I will most likely repeat for my future game ideas.
I have found that a large part of designing for multiplayer in general (as well as with procedural generation) that one needs to understand who has access to information (server/client) and who needs to know about it. If that is understood, multiplayer is pretty simple.