markeasting / game

OpenGL game engine
4 stars 0 forks source link

Track bezier #19

Open markeasting opened 1 year ago

markeasting commented 1 year ago

https://gamedev.stackexchange.com/questions/25280/in-very-simple-3d-racing-games-how-are-collisions-handled

In each of those games, we actually had polygons creating an invisible "wall" along the sides of the track, and did traditional collision testing against those walls. This meant that we could have some additional collidable hazards at the side of the road, inside of the invisible walls, and also let us vary the road width faster than you normally can with a spline approach.

But with that said, we also did what you list in the how I think collision would work section in order to guard against collision tunnelling/glitches, and this system was also heavily used for racing AI logic. We also used it for determining which cars were in the lead, so that we could display a "1st/2nd/3rd/etc" indicator on the HUD. Sometimes this data was also used for respawning a car after a major crash.

One piece you missed in how I think collision would work is that when you're working with splines like this, you'll normally give the splines ribs. Ribs are bits of data expressing how far the track extends sideways in each direction from the spline. So for a spline that's 100 meters long, you might have 50 ribs, giving the track width every two meters along its length. This allows your track to change widths along its extent. In the games I've worked on, these ribs have also distinguished between "track surface" and "drivable area". So you'd have one set of road widths telling how far from the middle of the spline you have nice tarmac, and then another width saying how far the sand/grass/whatever goes outside of that. That let us have players able to drive a reasonable distance off the track, but still have AI know where the real road surface was.

Many games I've worked on have also stored other data in the ribs; one game baked track lighting information into the ribs, for easily calculating whether or not an area was in shadow (which was then used for car rendering, deciding whether or not to draw a lens flare, and other effects). Another baked in information about which cinematic camera placements could see that part of the spline, so we didn't have to do line-of-sight calculations during replays. Still another included information about how many lanes were on the spline, which direction they went, and the horizontal displacement where each lane was located. This allowed us to include traffic cars which could correctly drive within road lanes. Ribs are fantastic for storing any sort of varying data you might need about your road surface.

Ribs are typically stored in an array that's associated with the spline. For speed reasons, a normal implementation will have evenly spaced ribs, so once you know an object's distance along the spline, you can calculate the nearest rib index in the array by dividing the distance along the spline by the distance between ribs. Otherwise you're stuck doing binary searches through your rib array to find the correct road width data.

Your culling description gives a good basic description of how the spline approach can be used, but it's actually a bit more complicated than you suggest -- if you use splines for culling in this way, long hairpin turns often won't draw the opposite side of the turn, since when measured by the distance along the track, the opposite side of the turn can be very far away, even if it's only a few meters away when measured as the crow flies. Additionally, the distances at which world geometry can be seen is typically different than that at which track mesh can be seen, so those don't really fit into this system either. My experience is that in most cases, it's better not to rely on track-following logic for determining whether a model should be drawn; it's much more reliable and causes fewer glitches to use standard camera frustum testing for that.