Monday, March 18, 2024

Procedural Buildings: Colored Locks and Keys

I haven't created a blog post since January, so I'm behind on my monthly posts. I did record a 21 minute YouTube video on my procedural cities and buildings progress last month. That took quite a while to put together, and sort of takes the place of the usual blog post. I didn't add any other major visuals that I can show in that time frame anyway.

The video was created from a list of dozens of features I wanted to show that I kept adding to while working on the recording. One of the reasons it took so long was because I repeatedly found a problem while recording, so I discarded the video, fixed the problem, and started over. I guess I've never tried to visit so many areas in the world and make so many changes at one time before. But some of these were minor issues such as objects clipping through each other. I still wanted to avoid showing any bugs. What was supposed to be the final video was interrupted by a crash (C++ assert) halfway into the recording.

The problem was that I added a step in the recording process where I got a black key and unlocked a black padlock from a door, then flew across the world to some other location to show off. When I came back to the first house with the lock it failed because a step was triggered that added locks to doors, but the door had already been unlocked. The state of the door was saved for that building, so the padlock addition code needed to skip it. To show respect for this bug, today's post will be on the subject of colored locks and keys. I don't have any single big task that I worked on during this time, so I may as well show one of the many smaller tasks.

First, a bit of background. One of the inspirations for these procedural buildings was the game Hello Neighbor, which I played with my daughter in the 2018-2020 time frame. This game had several houses full of locked doors and various objects that could be picked up, used, and thrown. The player had to find specific items and get into the rooms while either hiding or running from the neighbor AI. I liked the general idea very much, but I was always disappointed when I reached the last room and found there was nowhere else to explore. After that I was determined to create my own world full of houses and items that the player could never in their lifetimes fully explore.

But I'm just a single person working part time on this (on weekends and nights), I don't have a team, and I don't have any good 3D modeling skills (or interests). I'm not even sure it's possible to make something like that with thousands of houses in Unreal Engine, which was the game engine used in Hello Neighbor. The only practical way to reach these goals was through procedural generation. And I don't just mean generating everything at preprocessing time or even load time. I mean generating the content incrementally, dynamically, while the player is moving in the world. Everything has to be ready to draw when it comes into view. Objects must be ready to be interacted with when the player enters the building. On top of this, any changes the player makes must be permanent, which means we have to track all of the objects rather than simply discarding them when the player leaves the area and regenerating them when the player comes back.

How many objects are we talking about here? There are over 18,000 buildings, consisting of around 12K houses and 6K office buildings. Houses have hundreds of items, and office buildings can have thousands of items. That's not all though - some of these items are shelves, or boxes, or have drawers that contain other items. In total there are probably close to 100M total objects in the world. Each is unique. The player can take a book off a shelf in one house and drop it onto a desk in an office building a mile away. The book no longer exists in the house, and will always exist in the office building. It's all done with a hierarchical tree of containers: city => block => building => building part => room => object => sub-object. If the player modifies an object, that node and the tree nodes above it must be flagged as persistent and not deleted or regenerated later.

Sorry, I got a bit distracted with object management. I was going to talk about padlocks and keys. Keys have existed for a while now. Each building can have one or more keys, and previously all keys unlocked all locked doors in that particular building. This means the player only needed to find one of the keys to access every room in the building. This may be okay for small houses with a single key, but it's too easy on the player for large buildings with many keys. I changed the keys to have one of eight colors, and added padlocks of these eight colors to doors. I got that idea from Hello Neighbor as well. Padlocks are object type 152 of 159. Yes, there are currently 159 unique types of objects in 3DWorld's buildings!

Keys can be found inside the drawers of desks, dressers, and nightstands. They're not left out in the open. No, the player needs to work to find keys. Opening drawers is risky in gameplay mode because the sound attracts zombies and spiders may jump out of them. There must be risk associated with the reward of opening a locked door ... even if there's nothing of value in the locked room.

Red key hidden in an upstairs bedroom dresser drawer.

At the moment padlocks are only added to houses because office buildings generally have hallways that can be navigated without the player needing to use doors, and stairs and elevators that connect all floors together. Padlocks are added to both sides of the door in cases where the player can approach them from either side. For doors connecting to leaf rooms (rooms with a single door), padlocks are only added to the exterior side of the door. I left some doors in the "locked without padlock" state where the original behavior applies: Any key will open these doors. This makes navigation somewhat easier for the player. Having a single color key will open a bit over half the locked doors (those without padlocks or with matching padlock colors).

Note that locked doors may not actually block the player. In some cases a room may have another door that creates an alternate path. Unlocking the door can still be useful though, as it allows for an escape route when cornered in a dead end room. Or the alternate path may be longer and take the player past a zombie or a snake on the floor.

I did want to make sure all padlocks could be unlocked and all rooms were reachable. I don't have direct control over key placement; they get added as part of the procedural item placement using a tree of random numbers. However, it's possible to iterate over all of the room objects, enumerate all of their drawers and doors, and query the items inside them. This lets the placer know which colors of keys can be found, and what rooms they're found in. The padlock colors to choose from are limited to the subset of the eight key colors that can be found in the house. If there are no keys, there are no locked doors. This isn't enough to guarantee each room is reachable though. For example, what if the only red key is inside a room where the only access door is locked with a red padlock? There's no way for the player to get inside other than clipping through a wall.

There are three cases where a room may be unreachable. For floors above the ground floor, a lock may block the only path from the room with the key to the stairs. This applies to both the stairs going up and the stairs down to the basement. For the ground floor, the lock may either block the path to the room with the key on that floor, or the path to the stairs leading to the floor with the room that has the key. As far as I'm aware, it's not possible for a locked door on any floor other than the ground floor to block the path to a room on a different floor. At least not with the houses 3DWorld can create, assuming stairs connect correctly to all floors. Stairs placed in houses will usually connect through all floors above the ground floor.

When the player uses the action key on a locked door that they have the matching key for, the padlock will fall onto the floor and the door will open. The padlock on the other side of the door just disappears. If they don't have the key, the message "Door is locked with <color> padlock" will be shown to make it more clear what color key the player needs to find to open this door. Once unlocked, the door can never be re-locked with a padlock. However, the door can be locked normally, preventing zombies from passing through it. Zombies can't unlock the door unless they kill the player and take the key. Only doors that were initially locked can be re-locked.

That reminds me of another feature I added. Building AI people and zombies will now open doors over the course of a number of frames before running into the door, and will wait until the door is fully open before passing through. When the door opens toward them, they will stand out of range so that it doesn't hit them when opening.

Here are a pair of images showing a door locked with a red padlock and the same door after it's been unlocked and opened. The key icon in the top right shows that the player is holding a key, and it contains small vertical strips in each of the eight colors that are shown when the player has that color key in their inventory. The red key has a red stripe.

That red key goes to this locked door to a room coming off the kitchen on the ground floor.

The door has been unlocked, and the padlock is left on the floor.

2 comments:

  1. "repeatedly found a problem while recording" This happens to me all the time when I watch my 3D renders. You can't really get around it when doing a gameplay recording though.

    Are the keys for the residential buildings still unique to that building? So that, for instance, the blue key won't open a blue padlock in another building?

    ReplyDelete
    Replies
    1. I know, right? You have everything working and then when you go to show it to someone, it's broken. That happens to me all the time.

      Technically, keys will unlock all padlocks with matching colors. But in normal gameplay mode, you loose your keys when you exit the building or die. So keys can't be carried to other buildings, and I don't have to worry about players with thousands of items in their inventories. In debug mode the keys will stay, which makes it easier to test this sort of thing. If I want to drop my inventory items for testing I just toggle gameplay mode on and then off.

      Delete