logo
RoadsideScenery

vehicle · vehicle-combat

RoadsideScenery

Listens toDayNightCycle
1
Unlock

Install with the CLI:

bjs download RoadsideScenery

RoadsideScenery

Woods, farms and towns beside a HighwayRoad, planted the same way on every drive.

What it does

For each 30 m chunk of road, RoadsideScenery.core makes a list of things standing beside it. Pines, oaks and birches clump into woods. Bushes, rocks and farm fences fill the gaps, reeds grow on river banks, and telephone poles and reflector posts line the shoulders. In towns there are houses, the odd barn and street lamps.

The road plan says where nothing may stand: the tarmac, the ditch and the river.

Use it in a scene

A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.

Build it from scratch with the bjs CLI:

  1. 1Scaffold a project
    npm create @babylonjsmarket/arcade@latest my-game

    World, renderer, and dev server — ready to run.

  2. 2Install dependencies
    cd my-game && npm install
  3. 3Add RoadsideScenery
    bjs download RoadsideScenery

    Copies its source into src/ so the scene resolves.

    First time? Run bjs login once.

  4. 4Paste the scene into src/scenes/arcade-room.ts and run
    npm run dev

    SceneLoader builds the world from the JSON; reload to rebuild.

JSON
{
  "Scenery": {
    "components": {
      "RoadsideScenery": {
        "seed": 7,
        "follow": "Player",
        "treeDensity": 0.7,
        "reach": 78
      }
    }
  }
}

It finds the HighwayRoad on its own. If the world has no road, it plants beside a default one. Call registerRoadsideSceneryExtension(adapter) at bootstrap to see it.

Seeded chunks

Each chunk gets its own random numbers, made from the two seeds and the chunk number:

TypeScript
  const seed = (p.seed | 0) ^ (road.params.seed | 0);
  const rng = makeRng(hash3(seed, index, 0x51ce));

So chunk 4012 is the same wood every time you pass it, and chunks can be built in any order. A new road seed gives new woods too. A few things come at a fixed rhythm instead of by chance:

TypeScript
/** Reflector posts along the shoulders, this far apart. */
export const POST_SPACING = 40;
/** A green highway sign this often. */
export const SIGN_SPACING = 640;
/** A yellow warning sign this far before a bridge. */
export const WARNING_BEFORE_BRIDGE = 110;

Draw calls

Each kind of thing is one low-poly mesh with its colour baked into the vertices. The plugin draws it once and repeats it for every placement (thin instances), with a small tint per copy so no two trees match. A forest of two thousand trees costs about as many draw calls as there are kinds of tree. The trees and houses cast shadows on the road when castShadows is on.

Without the plugin nothing is drawn, but the plan still exists, so cars still hit the trees.

At night

The lamp heads and house windows glow, and each street lamp lays a soft pool of light on the road. The pools are painted on, not real lights. A whole town costs nothing against the light limit the headlights need.

Solid things

Trunks, poles, lamps, signs, rocks and houses are also circles. CarCollision asks for the ones near a car, from its own code:

TypeScript
    const scenery = findScenery(world);
    // ...
    plan.obstaclesIn(b.z - reach - 6, b.z + reach + 6, this.obstacles);

That is how a car that leaves the road hits a tree. Bushes, reeds, fences and reflector posts give way.

Settings

FieldDefaultWhat it does
seed7Mixed with the road's seed to pick what grows where.
chunkLength30Length of one chunk, metres. Keep it equal to the road's chunkLength so the two rebuild together.
reach78How far from the paved edge things grow, metres.
clearance3.2Nothing closer than this to the paved edge (clears the ditch).
treeDensity0.7Tree density, 0 to 1. Above 1 fills every woodland cell.
undergrowth1Bushes and rocks, as a multiple of the usual amount.
houseDensity0.82Share of town plots that have a house, 0 to 1.
houseSpacing19Metres between house plots along the road. Never less than 12.
lampSpacing26Metres between street lamps in towns. 0 = no lamps (towns get poles instead).
poleSpacing42Metres between telephone poles out of town. 0 = none.
follow"Player"Entity whose position decides which stretch is planted.
chunksBehind2Chunks kept behind the followed car.
chunksAhead8Chunks kept ahead of it.
castShadowstrueTrees and houses cast shadows on the road.

Change any of them while the game runs (LiveTuner does) and the roadside replants around the car.

Events

EventPayloadDirection
roadsideScenery.ready{ entityId }emits once, when the first chunks are planted
dayNight.changed{ entityId, hour, darkness, phase }listens, reads darkness to light the lamps and windows

Needs

  • HighwayRoad: the shape of the road to plant beside.
  • MeshPrimitive: where the followed car is right now.

More like this

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search