Sunday, April 11, 2021

Procedural Buildings: Object Interaction

I'm continuing to work on my game where the player steals valuable items from houses and office buildings while avoiding zombies. My goal is to make almost every object inside the building both something the player can steal, and something the player can interact with either before or after stealing it. I also want to make this as silly as possible rather than a horror game. I started with the basic set of player object interactions required for gameplay, and some other easy ones, which were implemented earlier:

  • Picking up objects
  • Opening room, closet, and bathroom stall doors (which may be locked)
  • Opening and closing dresser, desk, and nightstand drawers
  • Turning on and off ceiling room lights and lamps
  • Throwing and kicking basketballs and soccer balls
  • Drinking beverages (wine, beer, Coke, water, and newly added poison)
  • Riding elevators up and down
  • Spinning office chairs

This time I've added many more object interactions to increase the fun level. Why stop at stealing when you can trash someone's house in the process? Add graffiti, make a mess, and run up their utility bills. The zombies won't care, but when those people return to their houses after the invasion, won't they be in for a surprise!

  • Basketballs and soccer balls can now be thrown at zombies to make them temporarily retreat 
  • Spray paint cans that can be used to add graffiti to walls, ceilings, floors, and other flat surfaces
  • Markers that will draw on walls, ceilings, floors, whiteboards, pictures, rugs, mirrors, etc.
  • Turn sinks on and off (to waste water)
  • Flush toilets (to waste water)
  • Pull toilet paper off the roll (future work: wrap toilet paper around objects)
  • Tilt pictures hanging on the wall
  • Various other objects make sounds and/or animate
  • Player can use elevator buttons (fully functioning elevators are in the planning stage)

None of these really affect gameplay, except for the first bullet point where hitting a zombie above the legs with a ball will cause it to retreat for a few seconds. This gives the player some time to get away. I might add other throwable items, such as books, at a later time. None of these interactions affect the value of items the player has or the player's survivability.

Spray paint and markers are my favorite so far. My daughter Kate also loves to make her artwork in the office buildings. Too bad I haven't implemented a save system for this yet! The player must first find a can of spray paint or a marker on a storage room shelf or in a desk drawer. These will be a color randomly chosen from a set of common colors for those items. Picking up this object will allow the player to carry it around and use it with the action key.

Each key press (or each frame if the key is held down) will generate a circular spot of color as long as it's not too close to the previous spot (optimization). Spray paint and markers have a max range and will only write on certain types of large, flat, static objects. This includes walls, ceilings, floors, windows, mirrors, bathroom stalls, cubicles, stairs, elevators, rugs, pictures, and whiteboards. Each spot is an alpha blended point sprite quad drawn with a circular blur texture. Multiple points placed near each other will merge into solid lines of color that look like real brush strokes. These are oriented in the direction of the surface and shifted a bit in front so that they're drawn on top as decals. The draw order is preserved for blending, so the player can paint over existing spots with new colors. Spray paint that's sprayed from far away creates a larger but lighter circle, while close up sprays produce smaller and stronger color spots.

These quads are stored in a dynamic buffer and sent to the GPU each frame. I could probably break them up into blocks and store them on the GPU in VBOs as an optimization. For now it seems to be fast enough that this isn't needed, unless I really expect players to spend tens of minutes painting the walls. The graffiti will remain in the building until the player starts drawing in some other building. I only allow graffiti in one building at once for now, as an optimization. Paint and marker can be seen through the windows and open doors of a building. Here is an example of my artwork:

Inspecting my artwork in this office building hallway. All it took was three cans of spray paint and a dry erase marker.

Markers will write on whiteboards, but they write just fine on the walls as well. If you take your time it's possible to create very accurate and detailed drawings. The capacity of a spray paint can or marker is large enough to create quite a bit of color. At this point there's no way to erase them, so you need to be careful not to make a mistake.

I left a surprise for someone to find when they get back to the office after this zombie invasion.

Toilet paper rolls can also be taken off the holder and used. (The holder can be stolen as well.) I haven't quite figured out how to use toilet paper, other than pulling it off the roll down to the floor. I was thinking that I would have the action key wrap toilet paper around objects. Or maybe the roll can be thrown and will trail toilet paper behind it. I guess we'll see what I end up doing.

Here's some graffiti to point out that the roll of toilet paper can be taken for some extra fun.

[Yes, the toilet paper roll is attached to the block window. The house near the player starting position is the only house I've seen that's like this. This looks bad for normal transparent windows, so I remove the toilet paper in those cases. But a roll on a glass block window isn't as bad. Maybe it's glued on? So I allow it.]

The player can turn bathroom/kitchen sinks and tubs on and off. The sound of running water is played for a few seconds when these are turned on. I've considered letting the sound play in a loop while the water is running, but I'm not sure how practical it is when the player can turn on every sink in a building. Maybe it should only play when the player is in the bathroom? I don't know, it still seems like the sound would be annoying. I've also added an animated stream of water coming from bathroom sinks when they're on. Here's a screenshot, though you can't really see the animation in the water.

I turned on the water in this sink. You can see the reflection of the previous screenshot in the mirror.

It's silly to tilt the pictures in someone's house after you've stolen all of their valuables and drawn all over their walls. However, this was fun and easy to add.

Does that picture look crooked to you? I swear it was like that when I got here!

The next item on my list is working elevators. I currently have elevators that will move up and down based on the direction the player is looking. I want to add real elevators with call buttons on each floor and a button for each floor inside the elevator. These will control the vertical position of the elevator and the opening and closing of its doors. I've added the call buttons, and they do light up when pressed, but they have no effect on elevators at this time. This is intended for use by the player and not building AI people. Implementing all of this could be time consuming, but sounds like another fun and interesting mini-project.

I really should record another video of all these building interactions. I wish I could figure out how to make audio recording work so that I can show off all the sound effects I've added. Maybe I'll put a video up on YouTube once I get elevator buttons working.

Sunday, March 7, 2021

Procedural Buildings: AI and Player Interaction

I'm in the process of working on my game where the player steals items from houses and office buildings that are infested by zombies. The goal is to have both the player and the zombies interact with objects inside the buildings as part of gameplay. This includes entities such as room lights, doors, pickup objects, items on the floor, drawers, etc. Most of the items placed in rooms have been static up until now, but I want to make almost every object be interactive. That means the player must be able to pick up, drop, move, open, close, etc. the various room items. AI People inside buildings will also interact more with objects, even if that only means better object collisions and physics.

Here are some of the interactions I'm working on. Sorry, I don't have too many pictures for these. This is mostly a list of concepts and ideas I'm working on implementing for gameplay. I'll probably post some YouTube videos of early gameplay a bit later. At this point I have the high level gameplay system written, it's just a matter of filling in all the details.

Lights

Every room and hallway has one or more light placed somewhere on the ceiling. In addition, some bedrooms have lamps placed on nightstands and dressers. I recently added small lights to the ceilings of some of the walk-in type closets that the player can enter. Each of these lights can be toggled on and off by the player. This feature has been around for a few months now. It allows players to get a better view of a room by turning on the lights, while at the same time having many of the room lights off by default to improve framerate. (Lights are expensive for large buildings containing a thousand rooms.)

I've added the ability for AI people in buildings to turns lights on and off as well. AIs will turn the light on as they enter a room, and turn it back off again when they exit if no one else is in the room. This only applies to people in buildings near the player to avoid unnecessary toggling of distant room lights.

I also added logic to query whether or not the player is in a lit room that takes into account shadow casters. I plan to use this at some point to allow the player to hide from enemies in the darkness. The system is mostly implemented, but I haven't enabled it yet.

Doors

The player can open and close interior room and closet doors with a key press. Exterior building doors automatically open and close to admit the player. I've added logic for the AI to open doors, which is controlled by a config file parameter. If the AI can't open doors, the path finding algorithm will attempt to navigate around closed doors if possible. I had to rewrite the door drawing code to allow doors to have different open vs. closed states on each floor of the building, rather than having a single door that extends down between all floors. The rewrite allows doors to be drawn sparsely based on player location and view direction, which was required to handle millions of doors across all buildings in the scene.

I added the concept of locked doors a few days ago. The user defines the probability of doors being closed and/or locked in the config file. Locked doors can only be opened after the player finds a key, which are hidden around the building in drawers. The same key can be used for every door of that building, but won't work on doors in another building. AI people have similar flags for whether or not they have keys and can open locked doors. If the player has a key but the AI doesn't, it's possible to lock the AI in a room. The key + locked doors system will be important for later gameplay.

Pickup Items

I added a player inventory and made almost every placed item something that can be picked up. There's an inventory weight limit, so some of the larger items such as the couch and refrigerator are too heavy to pick up and carry. But the player can pick up most smaller furniture, fixtures, appliances, items on tables, etc. This includes fun things like toilets, sinks, stoves, nightstands, rugs, pictures, potted plants, TVs, chairs, ceiling lights, exit signs, men's/women's restroom signs, etc. Every item has a weight and a monetary value, and some items have special effects. The player's goal is to steal the maximum value of items and make it out of the building alive with their loot. Heavy items will slow the player down while they're being carried.

Some objects must be picked up in multiple parts. For example:

  • Beds: take pillows, then sheets, then mattress; bed frame is too heavy to pick up
  • Potted plants: take plant, then soil, then pot
  • Pictures: take picture, then frame
  • Tables: must take everything off the table before the table itself can be picked up
  • Wine Racks: must individually remove every bottle of wine before taking the wine rack

Bottles function as power-ups/special effects, depending on what type of drink they are. Empty bottles have no effect. Coke and water heal the player. Beer and wine make the player drunk, which enables some neat scree space effects like drunken wave, double vision, and blurry vision. [I would like to thank my daughter Kate for input on these effects!] This will all be important for later gameplay. I plan to add other drinks such as poisons that hurt the player in some way (damage, blindness, slow speed, etc.)

I had some difficulty handling objects on shelves and in closets because they're not actual C++ objects stored with the building. They're only drawn as part of the shelves and closets when they're visible to the player, so they're all temporaries created in the drawing code. It took quite a bit of work to allow the player to pick these objects up, and to have the system remember what was taken if the player walked away and came back later. I didn't want to allow the player to pick up a valuable item from the same shelf or closet more than once, even if they walked to the other side of the map and back. What I did was to "expand" these storage containers into real objects when the player attempted to use the "pick up" key on them so that they can be properly handled. This required quite a bit of code reorganization. It also has some runtime cost because expanding an object requires rebuilding the vertex data for the entire room detail geometry of that building.

Look at all these great items in here for me to steal. Unfortunately, I don't have the carrying capacity to take them all, and this guy isn't happy to see me here.

Drawers

I need a way to distract players from zombies so that they're caught off guard. What better way than encouraging players to spend time searching through desk, dresser, and nightstand drawers for keys and other valuables. Each drawer can be individually clicked on to open and close, and can contain a single item. The player can decide to pick up the item to add to their inventory. Some drawers are empty, some contain boxes, and on rare occasions a drawer contains a key. I plan to eventually add other items to drawers such as bottles, books, papers, pens, pencils, cell phones, wallets, and money. At some point I may also allow the player to open kitchen cabinet doors.

The room key can be found in a dresser, desk, or nightstand drawer. Other drawers are empty or contain boxes.

Dynamic Items

Okay, the player can now pick items up. What about dropping them? Simple spheres are the easiest type of shape for implementing collision detection and physics. I started by adding soccer balls and basketballs to some bedrooms. These can be picked up, carried around by the player, and dropped. I later decided that it was more fun for the player to throw balls rather than dropping them. The player can also kick them while on the floor, or in mid-air, if they get close enough. AI people walking around the building will also kick any balls they happen to run into.

I've implemented proper collision detection and physics for balls, including gravity, momentum, static and kinetic friction, and elasticity. Most of this was adapted from my other/existing gameplay framework, though it's all new code. Balls will roll around on the floor (including proper texture rotation), bounce off of room objects, bounce down the stairs, and even roll under beds and possibly get stuck. They cast and receive dynamic shadows from room lights.

Balls contribute to gameplay by emitting sound which alerts zombies. They can either be helpful when thrown as a distraction/diversion, or harmful when the player accidentally kicks one. I've scattered them around in the middle of floors to maximize the chance of the player accidentally running into a ball.

Soccer balls and basketballs can be picked up, kicked, and thrown by the player. The basketball has rolled under the bed.

Sounds

I've added lots of new sounds for building gameplay mode. Everything discussed here has sound: switching on and off lights (clicks), opening and closing doors and drawers, picking up objects, throwing and kicking balls, bouncing of balls, drinking from bottles, and even walking on the floor. I've included damage sound effects for the player and various zombie moans for people in buildings.

Sound is important for gameplay. Zombies can't see through walls, but they can hear noise made by the player. Louder or repetitive sounds can be heard from further distances and are more effective at attracting zombies from other parts of the building. It's important for the player to try to be as quiet as possible while searching the house for valuables.

Another strategy is to make a lot of noise in one part of a house to attract zombies, then sneak to the other end of the house to do your searching. Or you can place some balls near a doorway and hope a zombie runs into them, making noise and attracting other zombies (away from you). If you're feeling bold you can close some doors so that zombies must open them to enter rooms. This can either be used to distract other zombies, or alert you to a nearby zombie entering the room that you're in while you're searching. If you have a key you can even lock a zombie out of the room, assuming it doesn't have a key.

Sunday, February 14, 2021

Procedural City: Adding Building Basements

I'm starting to work on adding actual user gameplay for 3DWorld's procedural city environment. I made people inside buildings somewhat behave like zombies, and I added features for the player to steal objects from buildings. It's all pretty silly at this point, but that's fine for experimenting. However, I do want to make this at least a tiny bit scary.

The problem is, the interiors of these buildings are too bright for a horror game because they have so many windows. What I need are basements that I can make darker, especially when the lights are off. I'm starting with adding basements to houses as an extra level below the ground floor that may be a subset of the first floor footprint. This means they can be smaller in area than the house itself. The basement is connected to the first floor by a set of stairs with a door. I used my random number generator to add basements to 50% of houses to add some variety and reduce the runtime and memory penalty of having more building geometry.

Basements are drawn a bit differently from the rest of the house. Exterior walls are underground, so they don't need to be drawn at all as they're never visible by the player. Interior walls are always white rather than the brighter colors used on the main floors of the house. I also plan to change the ceiling and floor textures to something that looks more like a basement, possibly bare concrete in some cases. I haven't done this yet. I'll have to experiment with that later. Lighting also works differently, as discussed below.

Basement laundry room with washer, dryer, sink, table, and bookcase, with a bathroom in the back.

While this seems like something that should have been easy to add, it took quite a while to work through the various problems and get basements properly integrated. Some of these problems were expected, while others were a surprise to me.

First, there was the task of adding a new level to a house below the ground floor without breaking how the ground floor was handled. There were dozens of special cases in the code to treat rooms with zmin (the elevation of the floor of the room) equal to the building bounding cube zmin as rooms on the ground floor. This logic was used to add exterior doors, assign the living and dining rooms, control object placement, affect occlusion culling, etc. When I added a level below the ground floor, I had to update the building bounding cube to make it draw correctly, and that caused the placement system to add doors in the wrong locations and other strange failures. It took several hours to find and fix all of these cases. In fact, I found and fixed another one of these (fence placement) that I came across in the process of capturing screenshots for this post.

The next problem was handling terrain. This is drawn as part of an independent system that's separate from building drawing. Basements are below the terrain, and I had to add stairs to connect them. But the terrain was drawn over the entrance to the stairs because it was no longer hidden under the floor of the house. How exactly am I supposed to make the terrain system omit drawing areas around the basement entrance stairs? I already have a system to cut holes in the terrain for tunnel entrances by writing all zeros to the splat map texture, which will cause the fragment shader to discard those pixels. It's fine to iterate over a few tunnels for each terrain tile, but I think it would be too slow to iterate over thousands of houses to find the terrain texels that are over basement stairs.

Yes, the basement really is underground. You can see the basement stairs and some objects below the level of the grass.

I decided to use the same hack I've used in other areas of the code. Since the terrain mesh is drawn after buildings, I can write to the depth buffer between building and terrain drawing to prevent the terrain's fragment shader from writing to that area of the frame buffer. This is accomplished by drawing two transparent quads, one directly below the stairs cutout in the floor, and the other directly above. That seemed to work well enough. There's still a minor issue where the player can see the terrain when these two quads clip through the near plane of the view frustum, but I'm ignoring that for now.

Next, I realized that the AI I wrote for people in buildings couldn't figure out how to use the basement stairs. It was an existing bug in the path finding logic that was triggered when the AI attempted to plot a path through the stairs where the top and bottom levels had different floorplans. That took me many hours to debug and fix, partly because there were multiple related bugs in the code. It also didn't help that this code was difficult to test.

Stairs leading to the basement, with a door at the bottom. Maybe I need to add walls around these stairs or move them closer to the windows at some point.

I added a door to the bottom of the basement stairs to help isolate the two parts of the house. This is a new type of door because it separates stairs from a room, rather than separating two rooms (interior doors) or separating the interior of the building from the exterior (exterior doors). In addition, these doors were closed by default. The player must use the 'q' key to open doors so that they can pass through. Once again, I had to go back and update the building AI logic.

I added a config file option to control whether or not AIs could open doors. If they can't open the basement door, then this must break the connectivity between the first floor and the basement. If the player opens or closes the basement door, the navigation graph must be rebuilt to reflect the change in building connectivity. If the player closes the door after the AI has plotted its path, it will have to stop and wait at the door, or turn around and choose a new destination. On the other hand, if the AI can open doors, then the navigation graph doesn't need to know about them. The person can simply open the door when they're about to collide with it.

Basement storage room filled with boxes, crates, and small shelves with more items.

One issue I ran into is that the building generator was not always able to connect the basement to the first floor with stairs. Sometimes the basement was very small, or had many small rooms, and there wasn't enough space to place stairs. Stairs can only be added if they don't intersect a wall or obstruct a doorway on the floors above and below. This stairs placement failure happened in about 221 out of ~5000 houses with basements. That's not too often, but I really want this to be perfect. My first attempted fix was to allow basement stairs to be steeper (and therefore more compact) than normal house and building stairs. This definitely helped, reducing the number of failures to only 48. The final fix was to apply the "big hammer" to the problem: If the generator can't connect the basement with stairs, throw away the entire interior and generate a new one. This was very effective, and 42 of the 48 cases were fixed by the third attempt. One last pesky case required 12 attempts to finally connect the basement, but now there are none left unconnected. This additional work had a negligible impact on building generation time because it only required regenerating a hundred or so buildings out of over ten thousand.

I was expecting to have to change the player collision detection logic to somehow disable terrain collision detection when the player was in the basement. But the existing code was already skipping terrain collision detection when the player was in a building, so there was nothing I had to change.

Once I had these various issues resolved I worked on room assignment and object placement. Basements are mostly full of storage rooms, boxes, tables, bathrooms, and laundry rooms. Some have couches and TVs. There are no windows, plants, bedrooms, or other objects that don't belong in basements. A few of the rooms are left empty and/or unassigned until I can come up with new purposes for basement rooms.

I finally had basements correctly placed, connected with stairs, with working AI navigation. What was I doing this for again? Oh, right, I wanted some dark areas in a house to make the experience a bit scarier. Yes, I needed to make basements darker. It was relatively easy to adjust the ambient and indirect lighting levels to nearly zero so that the only light was direct illumination from room lights. If the player or AI turned the lights off, this left basements very dark. Even with the lights on they were filled with shadows.

The basement is very dark when all the room lights are off. There's still a tiny amount of ambient light.

I did have to fix the light transition though. At this point there was an abrupt switch between the two lighting models exactly as the player entered or exited the basement stairs. I experimented with various changes such as setting ambient light levels based on elevation. I settled on a time-based transition where light levels would slowly change over a period of several seconds after the player crossed the basement dividing line in elevation (the level of the floor in the ground floor, or the basement ceiling). This was sort of like the way a person's eyes adjust to changes in light level, but reversed. The transition was gradual enough that it wasn't too obvious what was going on.

That completes my work on house basements. Now I believe I'm ready to get into the core of gameplay mode. This involves writing the "zombie AI", which basically follows the player based on sight, lights, and noise. Then there's all of the item pickup (stealing) gameplay logic. Some of this is already implemented. I'll write more details about these topics in my next blog post.

Tuesday, January 19, 2021

Procedural Buildings: Gameplay Ideas

High Level Goals

I've been thinking about the end goal of 3DWorld's procedural city and buildings system. I feel like this is still many years away, and I have lots of crazy ideas that will take hundreds or thousands of hours to implement. I really plan to make procedural cities and buildings only a small part of the overall procedural universe.

The long term goal is to have a set of galaxies with solar systems, stars, planets, and moons. The player can land on each planet and moon, and these can contain various biomes with vegetation, cities, etc. depending on their parameters (radius, temperature, atmosphere, amount of water). Each city will have buildings, road networks, vehicles, people, etc. I want to have land vehicles, air vehicles, and water vehicles. People (or some type of sentient organism/being) will be in the vehicles, in the buildings, on the sidewalks, and walking on the terrain.

I actually have most of these individual pieces by now. I have the universe/galaxy/solar system/star/planet/moon code in my universe mode with its own ship battle gameplay. I have limited cities with a grid of roads, where each city is connected to its neighbors by longer roads. I have cars, trucks, and helicopters. I have people on city sidewalks and inside buildings. I also have buildings with full interiors in both the city blocks and surrounding suburbs. These are a mix of houses, small commercial office buildings, and larger office towers. I have procedural terrain with a generated heightmap, erosion, water, grass, trees, and plants. What I'm missing are all the pieces to tie these different parts together, plus many bits of missing functionality. For example, I have yet to add roads connecting secondary buildings and people in cars. I also don't have a continuous transition from space to heightmap terrain.

Gameplay Ideas

Anyway, that's all a long way off. Right now I plan to add some type of incremental gameplay with these procedural buildings. I want to keep it relatively simple and try to use as much existing code as possible without having to implement many new large systems. That means it has to be done with the existing cities, buildings, cars, and people, plus whatever incremental additions are needed. I prefer to have more of an open world game, so that means it should use the secondary buildings rather than being constrained to the smaller gridded city blocks. That also means I can take advantage of the higher detail and better window placement/drawing of the smaller buildings.

I'm not too interested in the difficulties of multiplayer gameplay, and I have no idea who I would play against. Therefore, it should be a single player game. The existing people I have in buildings can be the enemies, since they already have a basic AI that can navigate the building and find the player. I was leaning towards making those people zombies so that I can get away with their current state of poor AI and poor animation. It's much more forgivable to have a zombie (rather than a normal person) running into walls, getting stuck on furniture, taking the long way around, and only moving their legs. I prefer not to write a complex animation system. Maybe if I can put their arms out in front rather than having their arms in the T-pose that would look okay.

My goal is to have some sort of open world game where the player is encouraged to explore the buildings with some form of excitement. Maybe I can have it be some type of survival game where the player collects food from refrigerators and bottles from the tables/desks/counters to drink. I can add some secondary goals such as collecting items from desk and dresser drawers, or maybe rescuing people. Item collection could be in the form of robbing the house of valuables, something like breaking-and-entering mixed with zombies. I can't say I've ever played or seen a game like that before. The player must complete these tasks while zombies are trying to catch and infect them. I can also add other damage types such as fall damage from the roof, stairwells, and elevator shafts.

Zombies track the player based on a combination of sight and sound. They'll wander around the building randomly until they either see or hear the player, then they'll converge on the player's location. One touch from a zombie and it's game over. Or maybe there's some chance of pushing the zombie away, with a cool down timer. The idea is to keep the player moving and force them to pay attention and avoid getting surrounded or trapped in a dead end room. The more time spent in any one building, the more dangerous it gets. It's up to the player to know when to give up searching and make a run for the exit. I'm not sure if I'll add dangers out in the open between buildings.

I haven't yet decided if I want the game to be funny, scary, or a bit of both. I'll just have to experiment to see what I like best. I don't plan on adding combat with zombies or any sort of violence. This is a survival + stealth + run-and-hide game, not a combat game or shooter. The goal is to outsmart the zombie hoard rather than defeat it.

Gameplay Details

Here are some details for how gameplay may work. This is the first draft and is subject to change. The player can control building lights, close and lock doors, use elevators, and create distractions.

Lights: Room lights start randomly on or off. The player can only search a room for items when the lights are on. However, zombies can see the player from a greater distance if the lights are on, or maybe if the player is lit by at least one light (not fully in shadow). Zombies will also be attracted to the area if they see a light switch on or off. The player can also have a flashlight that can be use for a limited amount of time and has less of an effect on zombies. The player can only inspect items in their flashlight beam, but zombies will react with rage when the flashlight beam hits them.

Doors: Most interior building doors start open. The player can close doors at any time. Closed doors block line-of-sight for zombies and make it easier for a player to sneak around. Closed doors muffle sounds behind them. They also stop zombies for a few seconds while they figure out how to open doors, making this an effective way to slow them down. However, opening and closing doors will make noise that draws the attention of nearby zombies. So it's a choice the player has to make. I would also like to add the ability for the player to lock doors. This takes some time, maybe a few seconds. It will them slow zombies down as they take time to break through. Once a door is broken it can no longer be locked. But be careful - it takes a few seconds to unlock a locked door, so you better not accidentally lock the door to your escape route!

Closets: Closets have doors that can be opened and closed as well and act like tiny dark rooms. The player can enter the closet and hide there. Zombies will rarely look in closets, unless they see a player enter one. But be careful, there's no way to see what's outside. Maybe a zombie is standing right outside the closet door!

Noises: Zombies can hear player movements from a distance that depends on the sound level. Opening and closing doors makes a lot of noise and can be heard from far away. Opening and closing drawers makes somewhat less noise. Picking up items make a bit of noise, and even walking makes a small amount of noise that can be heard by a zombie in the same room. Standing still is the only way to be completely quiet.

Distractions: The player can pick up items they find such as the existing books and bottles I have scattered around on tables, desks, and counters. These go into one or more inventory slot(s). Then they can be thrown into a room to make a noise that distracts nearby zombies. Need a quick exit? Throw a bottle to the end of the hallway opposite the exit door.

Elevators: The player can use an elevator if it's stopped on the correct floor and they avoid falling down the elevator shaft. Zombies can't use elevators. This is a good escape route for office buildings. I'm not sure if I want to allow zombies to fall down the elevator shaft after the player.

I think those items could make for some pretty fun and interesting gameplay.

Keep in mind that zombies are relatively slow compared to the player, maybe half the player's speed. They're easy to outrun (and lose) if you know where you're going and don't get stuck. They're still dangerous in a variety of situations:

  • When you're not paying attention while searching and a zombie sneaks up behind you
  • When a zombie corners you in a dead end room with no exit to escape through
  • When multiple zombies approach from different sides and you have nowhere to run or hide
  • When you turn the corner or open a door and run into a zombie

Code Additions

The good news is that I have almost all of the code setup to do this. Building exteriors and interiors should be complete enough for gameplay. The player almost never gets stuck, and 95% of buildings should be fully accessible (all rooms can be reached). The AI I have for people may already be sufficient for zombies. I have the random walking and logic to have them converge to a particular location or room. That allows them to navigate to the location of a sound and follow the player. I already have items such as bottles, books, and office supplies that can be collected or thrown as distractions.

I just recently added sounds for light switches, door opening and closing, and walking. I can add sounds for zombies as well. The door and light switch sounds can easily be hooked into the AI alert logic to have them converge on the source of the sound.

I already have a key command that the player can use to switch on and off both ceiling lights and lamps in rooms. This affects the ambient light level in the room. I have light ray casting setup so that it should be easy to tell if the player is lit vs. in shadow. AI people can also turn lights on when entering a room, and similarly turn the lights off if they're the last person to leave the room. I don't expect zombies to turn lights on or off though - they just don't care.

Exterior doors automatically open and close when the player enters and leaves the building. I just recently added the ability for players to open and close interior doors. There's a new open_probability parameter that will control what percentage of doors are initially open, and I plan to keep it near 1.0 (100%). When closed, the door acts as a collider to block both the player and other AI people inside the building. I should be able to do a visibility ray cast that handles doors as well. The AI path finding/navigation system understands closed doors and has a flag that can either tell the person to go around or open the door and walk through.

The player can also already open closet doors and enter closets. I haven't worked out the logic for what happens if they close the closet door and lock themselves inside yet. I'll have to make the closet dark inside so that the player must use their flashlight to look around.

It might be interesting to add basements to houses. These work like normal floors, except they have different room type assignments with different objects and no windows. The lack of windows means that they're extra dark, to the point where the player can't see where they're going without a room light or flashlight. One difficulty of basements is that they're below terrain level, so I'm not sure how I can make that work in the current buildings system.

Finally, I already have tiled terrain mode hooked into the existing ground mode gameplay system with a health bar, fall damage, death tracking, etc. I have onscreen text and bar displays for all of this. And I have collision detection with people implemented. Those are some of the core components needed for gameplay.

Summary

That's almost all I need for this proposed game. I just need to implement the item collection system, hunger, thirst, stamina, etc. meters, the high-level gameplay logic, inventory system, zombie activation logic, etc. It shouldn't take too long to complete all of this as these are all incremental tasks. Of course I'll probably get distracted by trying to fix dozens of other things in the process. Hopefully this won't put me too far out of the way for reaching my longer term goals.

Sorry, there were no screenshots or videos in this post. I'm just doing some thinking. At least it's not very technical. I'll continue with plenty of screenshots next time.


Thursday, January 7, 2021

Helicopters

Finally, a post that's not about procedural building interiors! I added helicopters to 3DWorld's procedural cities environment. It started out simple, and I just kept adding to it like I did with cars. For some reason, I can't stop myself from adding more features. This one was 90% fun and only 10% frustration though as I was able to get most of the features to work pretty quickly.

At first I added helipads to the roofs of some of the larger/taller city buildings. But what good are helipads without helicopters? I had to add them next. I started with one 3D model, but that wasn't enough, I had to make it support multiple models and ended up with two of them. Stationary helicopters aren't so interesting, let me make them fly. They can't just fly anywhere though, they need to choose an unused destination helipad on some other building in some other city. Right, they also need to slowly take off and land, and rotate into the correct orientation in the process. Okay, so now helicopters fly around, that's pretty cool. But wait, they're colliding with buildings! Let me add collision avoidance for buildings and terrain by increasing their altitude to avoid these objects.

That's better, but they need to cast shadows on the buildings and ground. Soft shadows. They need to self-shadow themselves as well. Okay, it was a lot of work to make shadow updates efficient. Oh, wait, the blades need to rotate too. (Or is propeller the correct term here?) The shadows need to show the rotating blades. There, that's more like it. That took, what, only two weeks to add and a few hundred lines of code?

Here's a screenshot of my first helicopter model sitting on the helipad on a building roof.

Helicopter resting on a helipad, casting a soft shadow.

Fortunately, helicopters were much easier to add than cars. Cars took me months of working an hour a day at night to finally complete them. There are several factors that account for the difference. First, I already had all of the 3D model loading and drawing systems for cars that I could reuse. Second, there are only 30 helicopters compared to ~4,000 moving cars, which means I don't have to worry much about the performance of drawing them and running their AI. Third, helicopter fly in 3D, so it's a lot easier to make them avoid colliding with stuff. But most of all, they don't have to use traffic lights or worry about traffic congestion or running people over. I was able to avoid the madness of handling traffic jams.

Helicopter flying above the terrain and buildings.

 

Helicopters follow a three segment path where they rise vertically while rotating into position, travel in a straight constant elevation line until they reach the destination, then descend to the helipad below. Once they land they will wait 30-60s before taking off again for a new destination helipad. I should probably make them accelerate and decelerate eventually, but I'm not sure how noticeable that would be. Their max altitude is computed as the max of two values: a fixed distance above the higher elevation helipad (source or destination), and a clearance height above the highest elevation obstacle. I chose to use the terrain and buildings as obstacles to avoid. I'm ignoring trees and smaller objects for now, and just assuming my min vertical clearance value is large enough to avoid colliding with tall trees.

I'm computing max terrain height by walking along the path to the destination and checking each heightmap value along the way. Building max height is computed by ray casting through the buildings and calculating the highest point of each building that the ray intersects. Note that any ray passing completely under a building is guaranteed to hit the terrain, so I don't have to handle this case. This is pretty close, but doesn't account for a helicopter clipping the side of a building. I would have to use a projected cylinder or multiple rays to handle that case. The simplest fix for buildings was to expand building bounding cubes by something a bit larger than the radius of the helicopter. I haven't come up with a good way to handle clipping something like the edge of a cliff in the terrain. I've never seen this happen in practice, so I'm ignoring that situation for now.

One feature I didn't add (yet) is collision avoidance between two helicopters. I added simple bounding sphere-sphere collision detection code, but it seems like waiting until a collision has occurred before reacting is far too late. Maybe it's okay to allow two cars to slightly collide and then move them apart a bit so that they "just touch". It doesn't work that way with helicopters. For one, they can't swerve or slam on the breaks to quickly change direction like cars can. Then there's the danger factor of near collisions. Maybe it's okay to stop your car a few inches before hitting the car in front, and no one will even notice. That's believable in a traffic simulation. However, with helicopters, coming within inches of colliding with some other helicopter mid-air is not cool. You're not going to simply scratch your bumper or dent your license plate if you're a bit off. I can't simply move the two helicopters slightly so that they're not intersecting for that frame and expect it to look believable.

Okay then, how does it work? Collisions must be predicted and avoided ahead of time. In real life it's probably on a time scale of tens of seconds to a few minutes depending on speed. Maybe I can get away with a few seconds of motion prediction in my case. Still, helicopters move pretty quickly compared to cars, and they can cover a lot of distance in a few seconds. They would have to look ahead and predict the paths of potentially colliding helicopters out to a relatively large radius in order to correct their velocity in time. That would result in some form of cylinder-cylinder intersection algorithm, which isn't the easiest to get right.

There's also the question of which helicopter moves first so that they don't actively move into each other's path. Do you ever find yourself trying to avoid walking into someone in the hallway, so you move to the left? Then that person moves to their right, and you're still on a collision course? So you move right instead, and they move left? Yeah, that's what I'm talking about. That game of chicken would be no fun to play in real life helicopters. I have no idea how pilots actually handle that sort of thing.

Anyway, I'm sure I can solve this, but it's going to be somewhat complex. My solution is unlikely to work the first time. I'll have to try something, test it, fix it, and iterate until it works properly and is visually acceptable. Unfortunately, it seems to be nearly impossible to actually get two helicopters to collide. There are only 30 of them in something like 20 square miles of cities, and they have to collide in all three dimensions. Simply crossing each other's paths in the XY plane isn't good enough, they need to be at similar altitudes as well. Okay, maybe that's really a good thing. If helicopters are incredibly unlikely to collide, then maybe it will never actually happen and I can skip implementing collision avoidance completely? It's worth a try. It's one of those tasks where I probably won't add it until I witness a collision months from now.

Here's a screenshot showing two helicopters using two different models that are very close to each other. You can tell by the shadows they project on the road below that they're maybe 50 feet apart and headed somewhat towards each other. However, they're at different enough altitudes that they don't actually collide. The bottom of the helicopter on the right is about 20 feet above the top of the one on the left. This is the closest I've come to witnessing a mid-air collision. Sure, you wouldn't want to have your helicopters fly that close to each other in real life unless it's a stunt show, but that's not close enough to properly test collision avoidance. Also, good luck trying to capture this situation again. I don't remember how long I had to fly around before I found it.

Two helicopters nearly colliding mid-air. You can see their shadows cast on the road below.

The most time consuming step, by far, was making dynamic shadows from moving helicopters efficient. Everything else in 3DWorld's tiled terrain mode is static, which means shadow maps can be generated once and reused across frames. Yes, cars and people are dynamic, but I have no hope of ever having them all cast shadows while getting a reasonable framerate. I haven't even tried.

The big problem here is that this system was never intended to handle shadow updates every frame. The user won't even notice an extra 20ms generating shadows at startup, but adding 20ms to every frame would kill the framerate. There's a lot of stuff to draw into the shadow map: terrain, buildings, trees, parked cars, etc. - I mean, a real lot of objects. On top of that, there are many optimizations that are used for the main draw pass but haven't been implemented or enabled for the shadow pass:

  • Billboard drawing of objects such as trees are disabled because they make terrible looking shadows, so we draw the full model in the shadow pass.
  • Most objects don't have level-of-detail implemented or enabled for the shadow pass.
  • Occlusion culling has been disabled for shadows and wouldn't work well anyway when the sun is overhead because buildings don't tend to occlude too much of the city from that direction.
  • The shadow map is a higher resolution than the screen, which means more work for the rasterizer.

My first attempt at regenerating shadow maps every frame resulted in unplayable frame rates in the 20s or 30s. Then I realized I only had to regenerate shadow maps for regions around the helicopters. This brought the framerate up to around half of what it was without shadow updates, on average. A typical flyover would drop from 110 FPS to 55 FPS with shadows. I later realized that I could also skip updates when the shadow of the helicopter wasn't visible in the player's view frustum. I can get the shadow location by ray casting into the scene using the helicopter's center point and sun direction. The shadow will land on whatever point the ray intersects the terrain or a building. This helped in many situations, but the worst case when the player was looking down at the shadows was still 55 FPS.

I was able to slowly improve this by making various optimizations to specific types of objects that were drawn:

  • Tree low detail (LOD) drawing was broken (incorrectly disabled) for the shadow pass. This was easy to fix.
  • I was drawing every building in the shadow pass. Changing this code to iterate over building tiles and only drawing visible tiles (copy the code from the normal draw pass) helped a lot.
  • There was a performance bug where I was accidentally drawing traffic lights multiple times. The code was supposed to draw the set within each visible tile, but was drawing them all for each visible tile, for both the main and shadow pass.
  • I wasn't bothering to use view frustum culling of scenery objects such as plants and rocks. Enabling that helped a bit.

These changes together reduced the shadow pass frame time by nearly half, giving me ~71 FPS rather than ~55 FPS. This will help in other cases where the light source (sun or moon) moves as well, such as when the player changes the time-of-day or the dynamic day/night cycle is enabled by the user. The visual quality was nearly identical, and I improved non-shadow frame time for the main pass slightly in the process. That's the low hanging fruit, and good enough for now. I added a hotkey to enable and disable dynamic helicopter shadows to allow the user to select between quality and performance.

Here's a YouTube video of me flying around following helicopters as they cross the cities.


You may notice that the blades only rotate on one of the helicopter models. I had to split the model into two parts with the blades rotated differently from the model. This was accomplished by continuously increasing the rotation angle of the propeller while the helicopter was flying. I wanted to use smaller civilian helicopters, but most of the free 3D models I found online were of larger military helicopters. I'm not sure this type can land on typical rooftop helipads. The model I liked the most was in the 3DS file format. It's relatively easy to manually edit a text-based OBJ file model to identify and extract the propeller blades. It's not so easy to do that for a binary 3DS model where everything uses the same material. So for now only one of the two helicopters has rotating blades

I'm pretty happy with the state of helicopters for now. I may get back to them later and work on more shadow optimizations, add more models, improve collision avoidance, or add new types of destinations. I guess we'll have to wait and see.

Friday, January 1, 2021

Procedural Building Interiors: One More Pass?

I'm trying to finish up all the remaining additions I plan to make to procedural building interiors before I move on to other city items. This includes a few new objects and room types.

Interior Objects 

I've added quite a few new object types to building interiors: microwaves, computers, stacks of papers, boxes, horizontal and vertical window blinds, clothes washers, clothes dryers, paint cans, and rooftop solar panels. (Technically, solar panels aren't interior objects, but they're close enough.)

First, I added microwave ovens to some kitchen counters as gray cubes with a microwave texture on the front. This was another one of my daughter's suggestions. A random counter and position are selected, and the microwave is placed at that location if it doesn't intersect any other objects on the counter. I suppose that completes the kitchen, for now. Here's an example screenshot.

This kitchen has a microwave oven in the corner. That completes the basic kitchen setup: table, stove, refrigerator, sink, and microwave.

Up to this point, some desks had computer monitors and keyboards on them, but there was no computer. Fortunately, it's easy to create a simple computer tower by pasting a computer front panel image onto the front of a gray-black cube. Yes, that looks pretty close to the case of the computer I'm using to write 3DWorld and this blog post. I can place these computers under the desk off to one side so that they don't block the chair and can't intersect with anything else placed on top of the desk. Now the office computer desks look more complete.

A computer tower has been placed under desks with monitors, to go with the keyboards.

Some desks have computers, some have books, and others are empty. Empty desks don't look too interesting. Let's add a messy pile of papers on top of some of them. I decided on a mix of mostly white/cream paper with a few yellow pages mixed in. They can be rotated at odd angles and may overlap each other. Objects such as books and bottles can be placed over them. So far I haven't attempted to add any text to these papers; that seems like a major effort that can be done later when I work up the courage to attempt it.

Some desks have a collection of papers scattered on them. The papers are currently colored but blank.

As a final addition to house interiors, I added a chance of placing some boxes on the floor of some room types. Living rooms, dining rooms, bedrooms, and offices can have one or two boxes, while storage rooms can have up to eight boxes. Now I can populate home offices with computers, books, stacks of papers, bottles, and boxes. Here's a representative screenshot of a low-tech (computer-less) home office.

Example home office with a book, papers, bookcase, boxes on the floor, and ... bottle of beer.

Blinds

The next big topic to address was window coverings, specifically for bedroom windows. I originally wanted to add cloth curtains, but I couldn't quite figure out how to add the material with its smooth wavy curves in an efficient way. There are dozens of windows in each house, and I can't add thousands of polygons for every one. So instead I decided to add straight segmented horizontal blinds similar to the ones I have on some of the windows of my house. It was easy to find a free blinds texture with normal map online. Then I had to apply that to a single cube for each blind, which is very cheap to add to every bedroom window.

I used my random number generator to make some rooms have open blinds and others have closed blinds. Then I added a bit of extra randomness per-window so that they were at slightly different heights, which looked more natural. Most blinds are left open to match the previous look of the rooms. Note that open vs. closed blinds don't actually affect the amount of indirect sun/moon/sky light that contributes to the ambient lighting of the room. Here's an example of a room with blinds that are mostly closed.

A bedroom with horizontal blinds on the windows, mostly closed.

Here is a different bedroom with open blinds. You can see that the texture is squished into the top part of the window such that the total number of slats is the same whether the blinds are open or closed. This is to simulate the rotation of the slats as the cords are used to open and close them. I might have to go back and try to find a better solution to the texture aliasing problem when the slats are too close together. I really like the pattern the normal map makes with the lamp light source in this next image.

The lamp makes an interesting pattern of light on the open horizontal blinds of this bedroom.

I can also swap the direction and create vertical blinds for some of the bedrooms. These are a bit different because they start at either side of the window and close in to the center, rather than hanging/extending from the top. Here is how mostly-closed vertical blinds look in the same room as the previous screenshot. The camera is further away from the lamp, near the back wall.

Vertical blinds in a different bedroom, one partially opened and two closed.

That's pretty good for now. I might still add curtains at some point, or maybe add more textures and variety to blinds. I still need to add cloth material to the tops and sides of canopy beds as well.

 

Storage Rooms

Now we move on to office buildings, in particular storage rooms. Since these rooms are only added to the ground floor of larger buildings, there tend to only be a few of them per office building compared to the hundreds of office rooms. Therefore I can add more storage room items than I can get away with for offices. I wanted to make sure I had enough item variety to make these rooms look interesting.

I showed storage room crates, shelves, and bottles in the previous post. Since then I've added cardboard boxes, computers, keyboards, and paint cans. These are all typical items you would find on office building storage room shelves. Computers and keyboards were easy to add because I already had them placed on/under office desks. Cardboard boxes are simple cubes, while paint cans are single cylinders. These shapes are easy to work with, but the process of finding appropriate free textures online was a lot more work. This is especially true for boxes, where I had to manually separate out the top, bottom, and side textures from a texture atlas in an image editor. Here is the end result: a room full of shelves stocked with lots of different items.

This storage room has boxes, two different crate types, and a chair on the floor. It also has boxes, crates, bottles, computers, keyboards, and paint cans packed onto shelves against the walls.

Note that none of the items on shelves are actually stored in memory on the CPU side, and don't have collision detection enabled. The player and "building people" collide with the shelf bounding cubes themselves and can't get onto the shelves to collide with individual items. Also, storage rooms don't have windows, so I don't have to worry about the player viewing their interiors from outside the building. The shelf contents are generated on-the-fly when the player enters the building, converted to raw vertex data, and sent to the GPU. I can add quite a few items because only a few storage rooms are in GPU memory at any given time.

 

Solar Panels

The next addition takes us outside the building and onto the roof. I've added rooftop solar panels to some of the houses. The most complex part of this task was deciding which roof section was the best place to add the panel. The hard requirements are that the panel can't be shadowed by another part of the house and can't intersect any other part of the roof, including the chimney. I excluded smaller roof sections such as garage/shed roofs and porch roofs as well as any triangular roof polygons. This left the larger rectangular areas as valid placements. I ordered them by height so that the upper roof sections (which can't be shadowed by another part of the house) have a higher priority. Then I iterated over them, shrunk them by a fixed amount, and checked for intersections with another part of the house. After numerous iterations, I finally got it working. Here is the result.

Solar panels have been placed on the roofs of some houses in the optimal locations.

I did run into some problems with z-fighting (flickering) when viewing solar panels of distant houses because they were too thin, which placed them too close to the roof. I made them thicker until the problem went away. But now the player can sometimes see the inside surface of the panel when standing below the house and looking up at the roof. I guess the best solution is to draw the sides as well as the top surface quad, making the panels properly 3D. I suppose that looks better overall.

Rooftop solar panels look more 3D when I add their sides.

That's all of the interior objects I had on my list, other than the washer and dryer mentioned in the section below. I think that's pretty good for building interiors. I want to go back and work on cities next, possibly fire hydrants, street signs, etc.


Room Types

I have most of the common house rooms assigned now: bedrooms, bathrooms, kitchens, living rooms, dining rooms, offices/studies, and hallways. I even have a few special room types such as libraries, entryways, and rooms containing only stairs that I call "stairs". And of course there are garages and sheds. The only two common rooms that I'm missing are laundry rooms and storage rooms, so I've added these. All of the currently unassigned rooms in houses are assigned to one of these. The first unassigned ground floor room is made into the laundry room, and any remaining rooms are assigned to storage. Since most houses don't have unassigned rooms at this point, the majority have no dedicated laundry or storage rooms. If I want to change that I have to go back and re-assign some rooms, most likely smaller sized bedrooms.

House storage rooms are filled with boxes and possibly some of the other items found in office building storage rooms. They're usually very small; otherwise a bed, table, or desk would have been placed in them and they would have been made into some other room type by this point.

Laundry rooms have a washer and dryer, assuming both will fit. If only one can fit, then it's the washer. The placer prefers to place both against the same wall if possible. I wasn't able to find any free matching washer and dryer 3D models online, so I had to use a mismatched pair. Here's an example of a laundry room.

A laundry room with (mismatched) clothes washer and dryer.

Next up is helicopters. This post is already pretty long, and helicopters aren't related to building interiors, so I'll save that topic for next time.