Monday, January 24, 2022

Rats in Buildings

I'm working on adding rats to the procedural building interiors of 3DWorld. Why rats rather than some more common animals such as dogs or cats? I have several reasons. First, I have pet rats myself, two boys and four girls. I bought the parents at a pet store and they had 17 babies, four of which I kept as pets. I have some knowledge of their behavior that will help with modeling them, while most other people wouldn't notice if I get something wrong with their movements. Rats fit well with zombie themed games like the one I'm creating. Rats are easier to animate than dogs and cats because they're small, fast, and low to the ground. Their leg movements aren't as large or visible, which means I can get away with doing a simple procedural animation. Finally, rats have lots of interesting places to hide in my buildings: under beds, under tables, in closets, etc.

I found a free 3D rat model on Turbosquid and edited the materials to make it look a bit better with smooth normals and reduced specular lighting. I then added config options to add rats to random rooms on the ground floors and basements of buildings. A random location is chosen until one is found that's not intersecting a room object. This was the easy part!

A rat runs across the floor toward me, but I'm a ghost so it doesn't see me.

The more difficult steps were defining their behaviors/movements, path finding, and collision detection. Rats have a different style of movement compared to the cars and people I've previously added to 3DWorld's cities. They tend to run in a straight line, with frequent stops and direction changes, often under objects and along walls. I attempted to imitate this movement style by choosing paths consisting of variable length straight segments combined with occasional small direction changes. I also added in a weak "wall following behavior" from robotics, and logic to prefer destinations that are sources of cover such as under furniture and in closets. Each path segment has a randomly chosen speed with gradual full body turns between segments.

Rats collide with pretty much everything on the floor including walls, doors, stairs, furniture, plumbing fixtures, placed objects, dynamic objects (such as soccer balls and basketballs), people, the player, and other rats. The collision query is a modified line segment intersection test where I expand the room objects by the cross sectional radius of the rat. In other words, the max of its half width and half height. Static objects are queried prior to choosing a new path segment to verify that segment is traversable. If not, I generate a new candidate segment and try again. Dynamic objects are queried each frame the rat is moving to handle the case where an object moves from the time the rat started on the current segment. This makes rats able to dynamically dodge some types of objects and adapt to the player moving furniture.

The behavior I implemented allows rats to randomly explore the rooms on the current floor of their building. This isn't particularly interesting behavior by itself. Adding in fear, avoidance, and hiding behaviors was challenging, but greatly increases the fun in following rats around. My rat AIs are afraid of sounds and the sight of people including the player. The degree of fear is determined by the volume and distance of the sound, and the distance and visibility of the player. I considered adding in fear of sudden light changes as well, but it seemed easier to make the click of the light switch cause fear and lump that in the category of "sounds." They're much less fearful when the sound or person is in a different room, and not at all afraid of people they can't see due to occluders such as walls. Rats accumulate fear from these events, and their fear levels decrease slowly over time. They track the location/direction of the last sound or person they observed and prefer to avoid that area.

There are two fear behaviors: hiding and avoidance. The hiding behavior is triggered when a protected spot under an object is visible in a line of sight test from their current position. In the case of multiple possible hiding spots they choose the best one based on a factor of distance from their current position, distance to whatever scared them, and amount of protection. The protection of a hiding spot is determined by how small a space it is and how well it covers the rat. Small spaces are best, as are spaces that fully cover all of the rat's body. In addition, there's a preference to avoid locations that are occupied by another rat to help prevent excessive rat-rat collisions when they compete for the same space.

Once hidden, the rat will face the player or sound and wait. If the player moves the object the rat is hiding under, it will attempt to run under the object in the new location. Or maybe it will choose a different nearby object to hide under. If the player steals the object then the rat will find another hiding spot, if one is available. Once the player has left or the sound has faded, the rat will come out of hiding and continue to explore the building.

Two rats are scared of me and hiding under the table while looking at me. Sometimes they hide under the chairs.

When there are no available hiding spots, a rat will attempt to move away from the source of fear at a faster than usual speed. This is referred to as "running away." I found it tricky to handle the case where the player forces the rat into the dead end corner of the room because there's no way for the rat to run away. At first the rat would either freeze halfway in the wall, or spin in circles. I fixed this by changing the behavior so that the rat attempts to run past the player along one of the walls. This makes chasing rats extra fun because you can somewhat force them to go in a particular direction, at least until they find something to hide under. I had to add a key to disable rat fear so that I could more easily debug their movement without having to chase them around.

The rat motion controller works by directly setting the velocity of the rat, and setting a target direction. This is different from both people and cars. In particular, the car motion controller produces acceleration while the player and people AI controller sets position. I chose this method for rats because it seems to work well given their agile movements, sudden turns, and straight line direction. It does however mean that I can't reuse much of my existing code and had to write most of the rat logic from scratch. Of course, the fact that rats move around on the floor and don't have to climb stairs makes them significantly simpler to model than people. (In reality my pet rats do in fact climb up and down the stairs in my house.)

I still have to work on rat animations. My existing animation system for people doesn't work because it assumes two legs, while rats have four. Biped and quadruped animations are very different! I can reuse the existing animation framework, but the leg motions need to be written as a separate block of code. I'll try to post one or more YouTube videos when I have rat leg animations completed.

Update: Okay, it only took an hour or two to write the first version of leg animations. I used a slow motion reference video to help with getting the motion between the various legs in sync. It appears as though rats move diagonally opposite legs and then alternate with the other opposing pair. Feet lift up between the backward and forward stroke. They also bounce a bit with their backs going up and down as they walk, but I'm not attempting to model that part right now. That leg motion looks like sine waves to me, so that's how I implemented it. I decided to go with a pair of forward/back and up/down translates rather than a rotation like I use with people because the code is simpler and has less constants to tune. After writing some vertex shader math here is what I have:


Note that the rat animation and movement is playing back at around half speed to make it easier to see. I start off by walking into the room and turning on the light, which scares the rat so that it hides under the table. Then I back away and let it come out of hiding. At this point I switched the camera to flight/noclip mode to avoid scaring the rats so that I could get closer to them without triggering their fear behavior. I lost sight of the first rat, and after looking around some rooms I found them both in the kitchen. I scared them a bit more by closing the door, which made them both hide under the table. Collision detection between two rats is partially working. Bedrooms have more interesting hiding spots, but it's somewhat time consuming to chase them across the hallway into the bedroom area.

The animation is working but could be improved. In particular, it would look better if the upper part of the legs moved. Unfortunately, the thigh is merged into the body for this model and doesn't really rotate without distorting the body, so I gave up on that approach. Maybe I'll experiment with rotations later to see if they look any better. Of course it's actually more difficult to tell if the animations are correct when the rats move at normal speed and they're running away from you.

Okay, so now I have rats in 3DWorld's procedural buildings. What purpose do they serve in gameplay? I haven't quite figured that out yet, though I have a few ideas. Maybe they're worth money if they can be caught? Maybe they attack the player (if they're in a large enough group) and do damage? Maybe they squeak and alert zombies? Maybe they can be picked up and thrown at zombies? I don't know how much sense that last idea makes, but it certainly sounds like fun! I do like the challenge of having to chase a rat around and move or block its hiding spots. Of course it's nowhere near as difficult as chasing a real rat around a real house.

Saturday, January 8, 2022

Building Interior Fails

I recently attempted to add interiors to non cube shaped buildings. This includes cylindrical buildings with round sides, buildings with more than four sides, and buildings where stacked floors are rotated from the floors below. I was curious to see what the interiors of these buildings would look like. Note that all of the walls, doors, ceilings, floors, and rooms are still axis aligned cubes. They just stick out from the curved building exterior walls in interesting ways, giving some pretty amusing results.

Maybe it was a stupid idea to try adding rectangular rooms to round/cylindrical buildings? But this was in the name of science!

Some of the furniture, appliances, and plumbing fixtures are placed outside or partially outside the building exterior walls as well. This mostly happens at the corners of buildings. The lighting is wrong because there are no windows to "let in the sunlight", and I'm not sure where (if anywhere) the room ceiling lights are placed. I'm actually surprised I didn't get an errors from the building interior generation code. I suppose it doesn't care where the walls are and only uses the bounding cube of each part of the building.

Now that's what I call a corner office! Or maybe a windowless (and floor-less) office? I guess it works as long as you don't mind sitting halfway inside the wall of the building.

Even bathrooms can suffer from this problem.

Our bathroom doesn't need a ceiling fan because it gets so much fresh air from outside. You can even have a conversation with the person across the street while sitting on the toilet.

Stairs that would normally be against the inside of the exterior building wall are now on the outside of the wall. This made me consider adding fire escapes to the outside of some of the larger buildings. The biggest problem I can think of is that the lighting system won't work properly for these fire escapes for the reason mentioned above. It could take considerable work to fix that.

Yeah, those stairs are *supposed* to look like that. It's a .. fire escape! That's right. At least there are railings on some of the sides.

I attempted to fix some of these problems by removing objects placed outside the building. That fixes the biggest problems, but there are still issues. The internal floorplan doesn't make any sense, has empty areas, and doesn't fully connect the rooms. I've given up on this for now but left it on my TODO list, which is now on Trello.