Wednesday, May 31, 2023

Procedural Buildings: Breakable Objects and Particle Effects

I have a long list of smaller additions and improvements to show, so I'll split them up by category. This post will show some of the breakable and broken objects I've added to buildings, and the particle system I implemented to add glowing spark effects to broken electrical objects. Some of these have been around for a while, while others were recently added.

Broken Car Windows

I added the ability for the player to break into parked cars in office building parking garages to steal items. This was done a year or so ago, but I don't remember posting a screenshot or explaining it on this blog. Each car window can individually be broken once by using the player action key on the car. Some cars happen to be unlocked and can be looted without breaking the window. I can't easily modify each instance of the car's 3D model to remove a window, but I can draw a pile of broken glass on the ground at the player's feet. As you might expect, each pile of glass is unique. This glass will remain in the building forever to help players keep track of which cars they've already broken into. This was an easy change to make. I may decide to go back later and make the glass shards dynamic physics objects, or possibly allow the player to pick them up for some gameplay purpose.

A car in a parking garage with broken glass from the window on the ground.

Broken Mirrors

Throwing an object at the center of a building mirror will break it and add a crack decal texture over the reflective surface. One of two crack textures is randomly selected for each mirror. This works with dresser mirrors, office bathroom wall mirrors, and house medicine cabinet mirrors. While the player can drop various types of inventory items, the only object types that can be thrown are basketballs and soccer balls. Therefore, these are the only objects that can be used to break mirrors and other glass surfaces.

Breaking mirrors plays a loud glass shattering sound, which of course is guaranteed to attract zombies. Breaking a mirror also unlocks the "7 Years of Bad Luck" achievement.

Mirrors can be broken by throwing balls at them. Broken mirrors are cracked but still reflective.


I've considered making windows breakable, but so far haven't figured out how to do this. Windows aren't individual objects but are instead drawn along the entire side of the building, and are visible through cutouts in the walls. I would need to split these up into individual window panes somehow, then add the crack texture to each broken one. Some of the house windows are already divided vertically or horizontally into 2-4 panes, which further complicates things. A second challenge is adding a transparent overlay to an object that's already transparent in a way that makes the alpha blending correct. Of course, writing about it here will increase the likelihood that I actually complete this feature later.

Broken TVs and Computer Monitors

The player has been able to throw balls at TV and computer monitor screens to break them for a while now. The new feature to showcase is the particle system I've added that will spawn glowing sparks around the area where the ball and screen meet, as long as the device was powered by electricity. These sparks are physics objects affected by gravity with collision detection enabled for all room objects. I was able to reuse most of the basketball and soccer ball physics and collision code for particles, with some adjustments to physical constants to make their paths more pleasing. For example, sparks use lower gravity so that they remain in the air for longer.

Each broken object spawns a new group of particles that will disappear after their lifetime expires, which is currently set to a few seconds. This way only a small number of particles will be active at any given time and won't affect performance much. Sparks are drawn with an emissive colored material stretched out along the length of their velocity vector. Their color follows the normal black body light emission profile as their temperature decreases over their lifetime: white => yellow => orange => red => black. Light emission decreases over time, and transparency increases.

Later I decided to make these particles act as small light sources to illuminate the walls, ceiling, floor, and other nearby objects. The screenshot shows light sources, while the (older) video was taken before this was added.

Still screenshot of a TV screen that's just been broken with a basketball, with sparks flying around the room and producing light.

 
 

Broken Lights

There are two types of broken lights. A while back I added flickering lights with a random chance to occur in basements and parking garages. These lights are mostly on but turn off for a few frames every few seconds.

The new type of light I added is even more broken: normally off, with an occasional flash of light and a continuous shower of sparks. This was a convenient way to reuse the particle system I developed for TVs in a setting where the sparks are constantly visible, rather than only being seen briefly for a few seconds. These types of lights are only added to house extended basements rooms, giving them a dark and creepy atmosphere. Broken lights are always initialized to the off state and must be turned on by the player to make them active. This increases the amount of surprise when the player encounters a shower of sparks rather than a lit room. So far I haven't decided if I want the building AI people to turn these lights on as well.

Still screenshot of a broken light emitting glowing sparks that fall to the floor. Maybe they should catch the rug on fire?


It's possible to have a large number of broken lights in a building due to the way occlusion culling works. Since they're conveniently only added to basement rooms, I don't have to worry about them being visible through windows. The player can only see the lights and the sparks when the clipped light volume is visible. This means I can reuse the existing light bounding cube occlusion queries to skip generating, updating, and drawing the particles when the light is blocked from view by a wall, ceiling, or floor of the building. As soon as the player turns to leave the room or walks around the corner, these lights will become inactive and won't take any resources to draw or update.

Future Work

Broken lights don't really affect gameplay, except for reducing zombie (and player) visibility due to the dark room. Maybe I should have sparks catch rugs on fire? It sure seems like that rug should be bursting into flames in the screenshot and video above. That could damage the player and also either distract or kill zombies. Okay, I'm adding that to my TODO list.

I have plenty of other ideas for breakable objects. I could add a tool such as a crowbar, hammer, or bat that can break objects. I could allow some inventory items such as cups and vases to be dropped or thrown on the floor to break. I could provide a way for the player to create holes in the walls, ceilings,  floors, or doors. There are so many possibilities. I need to take some time to think about how gameplay will work and the various ways in which breaking/destroying objects would benefit the player.

4 comments:

  1. Have you implemented normal maps yet? If so, you could make one for the mirror so the reflection isn't perfectly flat. Or, maybe you could apply a screen-space offset to the mapping of the render in different fragments to get the same effect?

    Yeah, breaking and entering through windows seems like a good feature, especially since you can see through the windows to the interior.

    For breakable objects, do you plan to do procedural gibbing? Or would you need some generic "broken parts" that would fly out when the object is destroyed? Or do you just mean adding a damage decal, like with the mirrors?

    ReplyDelete
    Replies
    1. Yes, I have normal maps. Look at the walls and the doors. Mirror reflections aren't as simple as adjusting the normal though, the reflection plane itself needs to be adjusted. This really means the reflection camera must be adjusted, and I'm not sure how to do that per-mirror fragment without rendering the reflection multiple times. I could probably cheat with by adjusting the texture coordinates for the reflection like I did with water ripples in a later post. This would be easier to integrate than screen space offsets. The difficult part is that I need to identify each face in the shader. Maybe I can manually color the various parts of the crack image different colors and read that in the shader that draws the reflection. It's an interesting idea.

      I'm not sure how I would handle breakable objects. Anything that's procedurally generated could be split at triangle boundaries and drawn as parts. I'm unsure of how to handle 3D models because the current system doesn't know anything about the volume or interior vs. exterior surfaces. Flying particles could work with some objects, and damage decals could work with large flat objects.

      Delete
    2. Yeah, a custom color-zone based on the crack image was the first solution that came to mind. You could assign a random reflection coordinate offset at runtime so every break is a bit more unique. Kinda seems like you could do some quick voronoi math to generate the whole thing parametrically, but pre-baked textures certainly are easier.

      3D voronoi boolean volumes would be my first instinct for breaking 3D models, but there are a ton of damage and fracture mechanics techniques these days.

      That reminds me, if you want breakable windows in the cars, you should be able to split out the windows using the technique I demonstrated for splitting up mechanical objects:
      https://youtu.be/c6URo6RUCyo
      Then you could animate the windows rolling up and down as well!

      Delete
    3. I haven't done anything with Voronoi yet, so that would be significant effort to develop. I have an existing crack generation and propagation system for ground/gameplay mode that I could possibly reuse here. I'm also not sure I have the patience to split the windows out of the 13 car models I'm using. One would be fine, but that sounds like a lot of effort. I think the priority is mirrors, then windows. Mirror zones should be easy to do with the Gimp paint bucket tool, and I can directly use the red and green color components as a UV offset value. In fact I can probably automate that with a flood fill algorithm, assuming the individual mirror faces are solid colors except for the cracks.

      Delete