A lot of people have asked how the procedural road generation in DRIFT RIDE 2 works, so here’s a quick breakdown of the current setup.
The route is built from 200-meter segments.
Each segment contains objects that can either deform together with the road or remain unchanged. I currently use several different base segment types, with variations in terrain shape and road layout. For example, one can be mostly flat, while another has a cliff on one side, and they can also use different numbers of traffic lanes.
Right now these base types are still fairly independent from each other, so they can’t always be mixed freely within the same run.
Additional environment objects are then placed on top. Each one can also be configured to deform with the road if needed, which is useful for larger objects, fences, guardrails, and anything else that needs to follow the terrain or road shape.
Some of the non-deforming objects go through a randomizer. It decides which ones are active on the next segment and how many of them appear, which adds variation without requiring every possible version to be built manually.
The actual road shape is controlled separately through a set of parameters. I can define ranges for horizontal turns, elevation changes, road irregularities, and how long each of those changes should last. These values are generated continuously as the route extends, which gives me a result that is random, but still predictable and controllable rather than completely chaotic.
Banking is handled separately in code and is added depending on the strength of the turn.
Each segment is also marked up for traffic. I only need to define the lane width, number of lanes, and their direction, and the traffic system can use that data on the generated route.
There is also a check that prevents the route from intersecting itself. Before the next segment is accepted, a box collider checks whether its direction would cause it to overlap with an already generated section. If an intersection is detected, that spline is rejected and a different one is generated instead.
The route keeps generating endlessly ahead of the player, while sections far enough behind are removed.
Some large background elements, such as distant mountains, aren’t generated as part of the road at all. I use an old trick from classic racing games, where distant scenery was often kept static. In my case, those background elements are attached to the skybox and kept at a fixed distance from the player, which creates the illusion that they are much farther away than they really are.
That’s roughly how it works right now.
One of the main things I’d like to improve next is the transition between different base types. Ideally, I want those transitions to be generated automatically at runtime, so segments with very different shapes could still connect smoothly. Eventually, even the base geometry itself could become procedural instead of relying on predefined meshes.
I’d also like to spend much more time on the environments. A lot of the current environment work was done fairly quickly because I’m building the whole project alone. In the future, I want to create more dedicated assets based on real references for different types of roads and locations, instead of trying to reuse a relatively small set of assets everywhere.
The attached video is from the latest version of the game.