logo
RoadHazard

vehicle · vehicle-combat

RoadHazard

1
Unlock

Install with the CLI:

bjs download RoadHazard

RoadHazard

Oil slicks, smoke clouds and bomb craters left lying on the road.

What it does

  • An oil slick spins a car out, once per car per slick. The side the car enters on decides which way.
  • A smoke cloud blinds an AI driver while it is inside, and for blindSeconds after.
  • A crater jolts a car and damages it on the way in.

Each hazard is a pooled entity that lives life seconds and shrinks away over the last fade. Lay one with dropHazard. A bomb that bursts on the road turns into a crater from a pool named crater, if the scene has one. Whoever laid a hazard is safe from it for its first 1.5 seconds, so the player does not spin on the oil they just dropped.

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 RoadHazard
    bjs download RoadHazard

    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
{
  "OilBlueprint": {
    "tags": ["hazard"],
    "components": {
      "Pool": { "name": "oil", "size": 14 },
      "MeshPrimitive": {
        "primitive": "cylinder",
        "diameter": 5.4,
        "height": 0.03,
        "material": {
          "diffuseColor": [0.02, 0.02, 0.025],
          "specularColor": [0.8, 0.8, 0.85]
        }
      },
      "RoadHazard": { "kind": "oil", "radius": 2.7, "life": 16 }
    }
  },
  "CraterBlueprint": {
    "tags": ["hazard"],
    "components": {
      "Pool": { "name": "crater", "size": 10 },
      "MeshPrimitive": {
        "primitive": "cylinder",
        "diameter": 4.6,
        "height": 0.03,
        "material": { "diffuseColor": [0.09, 0.07, 0.06] }
      },
      "RoadHazard": {
        "kind": "crater",
        "radius": 2.2,
        "life": 30,
        "fade": 2,
        "spin": 3,
        "damage": 12
      }
    }
  }
}

Lay one from code with the helper in RoadHazard.defs:

TypeScript
export function dropHazard(world: World, pool: string, x: number, z: number, owner: string, yaw = 0): Entity | null {
  const e = world.acquire(pool, { x, y: 0.02, z });
  if (!e) return null;
  const h = e.get(RoadHazardComponent);
  if (h) h.owner = owner;
  const mp = e.get(MeshPrimitiveComponent);
  if (mp) mp.rotation[1] = yaw;
  return e;
}

dropHazard(world, 'oil', x, z, owner) lays a slick from the oil pool at (x, z). It returns null if the pool is not up yet.

Which way oil spins

A car that clips the left edge of a slick should spin the other way from one that clips the right edge. oilSpin works that out with one dot product:

TypeScript
export function oilSpin(carX: number, carZ: number, heading: number, hx: number, hz: number, strength: number): number {
  const rx = Math.cos(heading), rz = -Math.sin(heading);
  const side = (hx - carX) * rx + (hz - carZ) * rz;
  return (side >= 0 ? 1 : -1) * strength;
}

(rx, rz) points straight across the car, so the sign of side says which half of the slick the car came in on. Every spin is the same size: spin rad/s, with no grip for slipSeconds. Each slick keeps a set of the cars it has spun, so a car that sits in the oil for 30 frames spins once, not 30 times. A crater uses the same sum at 0.4 of its spin for 0.3 seconds, which reads as a jolt.

How a hazard fades

TypeScript
  const g = grow > 0 ? Math.min(1, age / grow) : 1;
  const left = life - age;
  const f = fade > 0 ? Math.min(1, left / fade) : 1;
  return Math.max(0.001, (0.35 + 0.65 * g) * f);

A new slick starts at 35% size and spreads to full in a quarter second. Take the defaults, a 14 s life and a 1.5 s fade. At 13 seconds the slick has 1 second left, so it is at two thirds of full size. Its 2.6 m reach shrinks to 1.7 m, plus 0.6 m of slack for the car's body. A dying slick looks smaller and is a smaller trap.

Settings

FieldDefaultWhat it does
kind"oil"oil, smoke or crater
radius2.6Reach in metres at full size. A car counts as inside 0.6 m further out.
life14Seconds it lies on the road
fade1.5Seconds it shrinks over at the end
spin5.5Oil: spin given to a car, rad/s. A crater gives 0.4 of it.
slipSeconds1.1Oil: seconds the car has no grip
blindSeconds1.6Smoke: seconds an AI driver stays blind after it leaves the cloud
damage10Crater: damage on the way in, scaled by the car's damageTaken
smokeColor[0.82, 0.82, 0.8]Smoke colour. Saved with the scene, but no system reads it yet.
puffsPerSecond40Smoke puff rate. Saved with the scene, but no system reads it yet.

Events

RoadFX draws the smoke cloud off roadHazard.laid. A director reads owner on roadHazard.entered to credit a kill to whoever laid the slick.

EventPayloadDirection
roadHazard.laid{ entityId, kind, x, z, radius, life }emits, on a hazard's first frame
roadHazard.entered{ hazardId, entityId, kind, owner }emits, each time a car drives in
carCollision.spin{ entityId, spin, seconds }emits, from oil and craters
autoRunnerDriver.blind{ entityId, seconds }emits, every frame an AI driver is in smoke
health.damage{ entityId, damage }emits, when a car drives into a crater
autoRunnerProjectile.exploded{ entityId, owner, kind, x, z, radius, ground }listens: a bomb with ground true becomes a crater

Needs

  • MeshPrimitive draws the hazard and gives each car's position.
  • CarDrive marks which entities are cars, and gives their heading.
  • CarCollision takes the spin, skips ghost cars, and scales crater damage by damageTaken.
  • AutoRunnerDriver takes the blind from smoke.
  • Health takes crater damage.

More like this

AutoRunnerDirector
AutoRunnerDriver
AutoRunnerProjectile
CarCollision
CarDrive
Health
MeshPrimitive
RoadFX
AiDriver
AiPilot
Asteroid
AutoRunnerBomber
AutoRunnerHUD
AutoRunnerWeapons
BoatMode
BoatWater
Bullet
CarPaint
ContactDamage
ContactImpulse
DayNightCycle
Headlights
HighwayRoad
HighwaySpawner
MachineGun
Missile
MissileLauncher
OilBlob
Parts
RoadAudio
RoadCamera
RoadSpawner
RoadTreadmill
RoadsideScenery
ShipLoadout
SmokeScreen
SupplyTruck
SurfacePatch
TrafficConvoy
TwinStickEnemy
WeaponsVan

Was this page helpful?

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

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search