Traluno▶ Back to the layout

Roads Are Harder Than Rails

Track goes where you put it and only ever meets other track two ways. Roads have junctions, kerbs, footways, crossings and pedestrians, and every one of those is a special case.

Rails took about a week. Roads took nearly three, and the ratio still surprises me, because a road looks like the simpler object: a flat grey band with markings on it, no sleepers, no ballast, no switches.

Here is where the time went.

Track has two ways to meet. Roads have many

A piece of track connects to another piece at one of its ends, and the connection is either straight through or diverging. That is the whole vocabulary. Even a switch is just a diverge, and a crossing is two straights that happen to overlap.

A road tile can meet its neighbours on any of four sides, in any combination. That is sixteen cases, of which fifteen are roads — dead end, straight, corner, T-junction, crossroads, and the rotations of each.

Every one needs different geometry. A straight has kerbs down both sides and a centre line. A corner has an inside kerb and an outside kerb of different radii and no centre line at all through the turn. A T-junction has three kerb returns and a give-way marking on the stem. A crossroads has four returns and no markings in the middle, because the middle is where everyone is looking at each other.

None of this is hard. There is just a lot of it, and it all has to agree at the tile boundaries or the seams show.

The kerb radius problem

Kerbs are swept bands, and the inside kerb of a corner is the tightest sweep on the layout by a wide margin. Below about 1.5 units of radius a swept box overlaps itself into a starburst of raised fins, which is covered in more detail in the post on sweep radius.

A road corner's inside kerb is well below that. So it is not swept: it is laid as a single corner pad shaped to fill the inside of the turn. One piece, no steps, no fins.

The outside kerb of the same corner is comfortably above the threshold and is swept normally. So a single corner tile contains both techniques, and they have to meet invisibly where the corner pad ends and the sweep begins.

Footways are a road, and that is the useful insight

The footway — the pavement, the sidewalk, whichever word you grew up with — started as decoration. Draw a raised strip outside the kerb, put a slightly different colour on it, done.

Then pedestrians arrived and needed somewhere to walk, and the obvious answer was to give the footway a path down the middle of it, exactly like the road has a path down each lane.

That turned out to unify a lot. The path-cursor system that drives trains along rails and cars along carriageways drives people along footways too, with different speeds and a different model, and everything that follows from having a path — junction choice, queuing, stopping — came for free.

It also produced the first genuinely emergent behaviour in the game. A pedestrian reaching a corner picks a footway to continue on. Several pedestrians doing that independently produce a plausible flow of people around a block, which nobody wrote and which is entirely a consequence of the corner having two exits.

Crossings need to read what is underneath

A zebra crossing is placed on a road and has to know which way the road runs, because its stripes are perpendicular to the traffic and the whole thing is meaningless at any other angle.

So the crossing reads the tile beneath it at build time and orients itself. That is a small piece of code and it is the reason crossings cannot be static assets: an exported mesh has one orientation and no idea what it is standing on.

The same pattern appears in exactly one other place — the mountain carves tunnel bores by reading the track under it — and both are the reason the "buildings stay procedural" line is drawn where it is.

Level crossings, where a road meets a railway, are a third variant. Those appear automatically at the intersection and bring gates with them, and the gates are the single most satisfying thing on the layout to watch, which is a poor engineering justification and an excellent design one.

The lane overlap

One more geometry note, because it cost an afternoon.

A carriageway is built as several parallel lanes swept side by side. Butting them edge to edge produces thin dark radial seams that flicker as the camera moves: two coincident faces, and the renderer picking a different winner per pixel.

The lanes now overlap by about 0.3 rather than sharing an edge. No two faces are coplanar, so there is nothing to fight over, and the seams are gone.

This is a rule rather than a fix: anywhere two pieces of swept geometry meet along their length, overlap them. Tidy abutment is the wrong instinct in a renderer with no depth tie-breaker.

Would I do it differently

Probably not, but I would do it in a different order.

Roads were built as "track, but for cars", which got the sweeping and the path cursors for free and then spent three weeks discovering that everything else was different. Building the junction taxonomy first — sixteen cases, what geometry does each need, where do they have to agree — would have made the sweeping work fall out of it rather than the other way round.

The general version: when the second system looks like the first, the parts that are genuinely shared will be obvious and cheap. It is the parts that are nearly the same that will take the time, and those are worth enumerating before you start rather than discovering one at a time.

  • engine
  • roads
  • geometry

◀ All posts