Shadows of Doubt DevBlog #13: Creating Procedural Interiors
Shadows of Doubt is a detective stealth game set in a fully-simulated sci-fi metropolis! There’s been a murder and it’s up to you to solve it by any means necessary, with the condition that you keep a low profile. A unique mix of procedural generation and hand-crafted design enables every room of every building to be explored. Citizens go about their lives independent of the player as you watch from the Shadows, in search of crucial information. Read previous dev blog entries here.
For the last few weeks I’ve been working on an important part of the game; the environments. One of the most ambitious parts of this project is the huge number of explorable interiors and their complexity. I’ve had to think very carefully on how to go about creating this: I need a way of generating huge numbers of apartments, offices, cafes etc that have enough variation but also don’t require a huge amount of individual design work. It’s perhaps the toughest aspect of the project, but I’ve had a long time to think about it and progress is going well.
What makes a location in a game interesting? With a narratively-focused game, I find that environmental storytelling can really make a location feel compelling. Empty beer bottles. Broken mirrors. Books lying around that you can read snippets of. Pictures on the wall. Notes on the fridge. The environment is not only part of the player’s world, but someone else’s. With my citizen generation system, I have a lot of what I need in place to drive environmental storytelling. Time will tell how compelling that can really be, and I’ll be writing a lot about this in the future. But for the moment I’ve been focusing on the interior blueprints themselves.
Buildings in Shadows of Doubt are split into floors, then divided into ‘addresses’, and within them ‘rooms’. The location hierarchy in the game from large to small looks like this:
City > District > Block > Building > Floor > Address > Room > Tile
I decided to concentrate on ‘floors’ as the point where designed elements intersect with the procedural generation. I set to work on a separate tool that lets me design floorplans, while also letting me preview what the procedural generation can do. The idea is that I design lots of individual floorplans that are sectioned off into different addresses- kind of like in SimCity where you lay out residential, commercial and industrial zones. Instead of that though I’m defining the floor space, of say an apartment, within the floor of a building. Buildings can then be generated from a bunch of these floorplans.
This is as far as my input goes and where proc gen takes over: I have a ‘generate interior’ button that uses some wizardry to generate dividing walls. In this example with an apartment, it will section off a bathroom, and if there’s room, bedrooms, a kitchen and for much larger floorspaces even a study, shower room, dining room etc.
How does this work then? The physical space in the game is represented through ’tiles’- each one of these is 1.8m x 1.8m in-game, basically turning everything into a giant grid that makes it easier for many systems such as pathfinding, culling and this generation process. Building floors are 15 x 15 tiles in size.
When generating an interior layout, my system takes stock of the floor space and where the entrance to is. It decides if the entrance is too far from a corner of the defined space, to draw a hallway that connects the entrance to a tile closer to that corner. Hallways are essential because without them you tend to generate a bunch of rooms that are only ever accessed via each other. This might be fine for something like a small apartment, but as they get larger the less sense they make. Imagine having to get to the bathroom by having to go through the living room, then the kitchen, then the study, then a bedroom etc.
Next, the game will cycle through a list of defined rooms, each with their own pre-defined rules. It cycles in order of how ‘important’ they are, eg the first entry for an apartment is a living room, followed by the bathroom (I think they’re pretty much the most important rooms!) The game will then loop through every available free space and simulate positioning the room there. It will rank how successful or unsuccessful this has been using the rules. It will then save this placement location and ranking in a list. After it has tried every available space, it will sort the list using the ranking and pick the most successful attempt, and place the room there. Repeat until either all rooms are placed or there is no space left. I currently have 3 main means of ranking how appropriate a room placement is:
1). Floor space. Important for larger rooms, for example the living room. Less important for say a bathroom.
2). Uniform shape. Basically how many corners they have. This is important for most larger rooms, maybe less so for smaller ones.
3). Windows. It’s more important for some rooms to feature windows than others. For
That’s the basic model, but I’ve had to add a lot of special conditions to this to make things work appropriately. Certain rooms can only connect to other certain rooms. For example, while everything can connect to a hallway, a kitchen can also connect to a living room directly. Some rooms can have only 1 door (eg bathrooms), while others can have more. Some rooms are allowed to feature the entrance to the address (you probably don’t want the main door to open up into the bedroom). There is also an override system that allows rooms to ‘steal’ floorspace from existing rooms in the generation process if for example one previously placed room has excess floor space and the current one has too little. After a lot of tweaking, I think I’ve found a decent balance. Of course, you can also preview what’s been generated in the editor too!
I think at some point this editor will be released with the game to allow others to created custom floor plans. It makes too much sense not to have it as a tool people can use. I’ve had such a great response that I may even consider forking off this project to create some other cool stuff with it. Next time I’ll write about decorating these generated rooms.