Wednesday, October 29, 2025

Procedural City Gas Stations

I took a break from adding custom room and building types to work on cities again. I've had gas stations on my to-do list for a while now, so I decided to finally add these. I'm only adding the outdoor gas pumps and roof rather than the full building. This makes them have a smaller footprint that's easier to fit in the space between roads and buildings.

The first step was to place gas stations around the city. I'm only adding them to commercial cities with office buildings and not to the residential cities with houses. The best location is probably on the corner of the block adjacent to two intersecting roads. That way I can have cars enter from one road and leave on the other without having to worry about driveways, or cars crossing each other's paths. I moved the interior area with the pumps far enough from the edge of the roads so that pedestrians can walk by without colliding or blocking the cars.

It doesn't look correct to have two gas stations across the street from each other, so I set a minimum separation distance of four city blocks. This tends to produce around four gas stations per city in their own quadrants, which seems reasonable. That works out to about one gas station per 50 city blocks / 120 buildings / 200 cars. That's probably still too many, but I'm going with that number anyway.

Each gas station has a rectangular roof and four supporting pillars, with a gas pump next to each pillar. This seems to be the most common layout I've seen in reference images. I found two different gas pump 3D models that worked in my cities. I placed a sign with gas prices on the side by one road, and added five lights to the underside of the roof that turn on during night time. I went with a white roof with red trim, since that seemed like a pretty common combination in real gas stations. I'm not going to add any sort of logo at this time. I also went with blue pillars to get good contrast with the red on the roof and add some more color to cities. To finish things off, I added four circular manhole covers over the concrete floor to cover the fuel storage tank openings.

Here are some screenshots showing gas stations in the city environment with cars and pedestrians enabled. 

Day time gas station in a city with one car parked next to a gas pump.

Gas station using a second gas pump model with two cars and a truck stopped at it.

About half the total time was spent writing, debugging, and testing the logic for cars to use gas stations. I added a fuel_amt variable to each car that's initialized to a random value and have fuel decrease over time based on distance traveled. When fuel drops below 20%, cars will attempt to reserve a spot in the closest gas station within a limited travel radius. I had to use reserved slots to avoid the complexity of queuing lines and multiple cars attempting to turn into the gas station from different directions. If no slots are available, the car will choose a random travel destination and retry reserving a gas station a few minutes later. Nothing happens when a car runs out of fuel, which occurs quite often. After refilling at a pump, the car is good for another 10-20 minutes or so of driving around the city.

Cars in residential cities have no local gas stations to visit. I start these cars with more fuel on average and have it decrease more slowly. When fuel_amt reaches 25%, cars will find the nearest connected city that has a gas station and navigate to the global connector road leading to that city. Once there, they'll have to compete with other cars to reserve a gas station lane. Hopefully I set the fuel use rate for residential cars low enough that they don't all converge on the commercial cities while leaving the residential areas empty. This system appears to work relatively well in my limited time testing.

Each gas station has four "lanes" connected to the "input" road, two on the outsides of the pumps and two on the inside. I could probably fit two cars in each lane since there are two pumps, but that makes navigation more difficult, so I limited it to one car per lane at any given time. Cars will enter the lane, stop at one of the two pumps for 30-60s, then turn and exit. Their engines and headlights will turn off while stopped. There's a single output lane that exits the gas station onto the "output" road. This output lane must also be reserved and can only be used by one of the cars in the input lanes at a time. I reused much of the driveway and parking lot state machine code for gas stations. Each of the five lanes (4 input + 1 output) is represented as a driveway in the road network and automatically picks up the enter, exit, stop, and turn logic.

Pedestrians caused a lot of problems, as usual. Those street corners where the gas stations are placed are right next to the places where people stop and wait to cross the road. The combination of multiple people walking together and multiple cars trying to enter or exit the gas station at the same time tends to lead to deadlock where no one can move. The problem is that both the pedestrians stop for cars and the cars stop for pedestrians. The system is intentionally too cautious to make up for the fact that it misses some potential collision cases. Both cars and pedestrians will stop when their projected path *may* collide with something else within the next ~2 seconds.

The easiest fix was to simply disable the lane closest to the road as this is where the majority of people are walking or standing. This reduces the number of available lanes that cars can use from four to three. It avoids problems most of the time, but the system can still deadlock when a person gets stuck between two cars that are entering at the same time, for example in the two interior lanes. I didn't want to spend an entire week debugging this, so I gave up and made the cars ignore people once they've entered the gas station's bounding box. They still check for people before pulling in or exiting the driveways, and they still avoid other cars. But once cars start to turn onto a gas station driveway, they'll continue to turn and pull in/out even if a person crosses into their path. People will still attempt to avoid cars to some extent. They won't actively walk into a car, but they won't move out of the way either. This pedestrian logic is inaccurate because, unlike on straight city roads, they can't easily predict the way cars will turn and drive around gas stations.

With the hard part out of the way, you get to see some night time screenshots. Those lights on the underside of the roof come on automatically when it gets dark outside. These cast shadows and are very similar to streetlights, except their color is more blue than yellow. 

Night time version of the gas station from above, showing the lights on the underside of the roof.

Here I've enabled light bloom, which runs in a postprocessing shader. This makes light sources such as streetlights, window lights, and headlights appear brighter. The sign is hard to read, but it does reflect the current average gas prices here in my home state of California.

Another gas station at nigh time, with light bloom enabled. The sign with gas prices is visible but not illuminated.

Here's a short video showing cars entering and leaving a gas station while I play around with the sun position to change the time of day. You can even see a car colliding with a person. Don't worry, everything is fine. People can't get hurt in this simulation.


Bonus Content

Since this post is shorter than usual, I decided to add some bonus images showing off features I've added but not yet written about. None of these were important enough to create a separate blog post on the topic. I've included a gas station in the background of each one to fit today's theme.

First we have wind turbines. I added these many months ago. I believe you can catch some of these in my earlier videos if you look closely, but I never officially showed them off in this blog. They're placed on hilltops away from cities and tall buildings. They look close, but they're actually huge and far away. I think they're at least twice as tall as a building. There are about 20 wind turbines in my scene.

View from above a city gas station with three wind turbines visible on the hills behind the buildings.

I just recently (while writing this post) added parking structures to cities. They're probably taller than they should be, and they may have too many cars to get a good framerate. I do like the look of these buildings though. I even managed to connect them to adjacent office buildings with overhead walkways. This screenshot has three parking garages on adjacent city blocks. I'll probably add a distance limit similar to how I handled gas stations to even out the framerate.

View from above a gas stations with three tall city parking structures visible. The one in the back has a walkway connecting it to the adjacent office building.

Finally, here's a screenshot of one of the strange light sculptures I added to cities. These are formed from a cluster of sphere and torus shapes connected to a black metal cylinder frame. They turn emissive when the sun sets, similar to streetlights. They're more like area lights, so they don't cast shadows.

One of the many emissive light sculptures I have scattered around 3DWorld's procedural city, viewed at night time, with a gas station behind it.

That's it for gas stations and other city additions. I've started working on extended basements again in another attempt to make them look old and abandoned with missing ceiling tiles and hanging wires. I may write about that in the next post.

No comments:

Post a Comment