CarCollision
Cars that hit like cars, and a world they can crash into.
What it does
Every frame, after CarDrive has moved everyone, CarCollision finds each pair of cars that overlap and each car touching the world, and works out the shove each one gets. It moves nothing itself. Each car's fix goes back to CarDrive as one carDrive.nudge (position, velocity and a yaw kick), and the damage goes out as health.damage.
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:
- 1Scaffold a project
World, renderer, and dev server — ready to run.
- 2Install dependencies
- 3Add CarCollision
Copies its source into src/ so the scene resolves.
First time? Run bjs login once.
- 4Paste the scene into src/scenes/arcade-room.ts and run
SceneLoader builds the world from the JSON; reload to rebuild.
The car also needs CarDrive and a MeshPrimitive.
Oriented boxes
A car is about twice as long as it is wide. As a circle, two cars in side-by-side lanes would touch, and a tap on a rear corner would push the whole car sideways. As boxes:
- Separating axes find the overlap and the contact normal.
- The corner that dug in is the contact point.
- One push along the normal (the bounce) and one along the surface (the scrape, capped by friction) are solved for the speed of the two contact points, which includes how fast each car is turning.
Corner hits
The turn a hit gives is the push times its lever arm, the distance from the car's middle to the contact point:
Hit a car dead centre and the arm is zero, so it only slides. Hit the rear corner of a 4.1 m car and the arm is about 2 m, so the same push turns it. spinGain then scales that turn for arcade feel.
Damage from a knock
Against another car, that is scaled by this car's armor and the other car's ramDamage. A tree or a wall hurts 1.35 times as much, because it does not give.
damageTaken scales every hit from anything on the road: rams, gunfire, bombs and craters. AutoRunnerProjectile and RoadHazard read it through damageTo() in CarCollision.defs. A car that drives into a river is lost whatever it is set to.
Spin-outs
A hard hit, or carCollision.spin from an oil slick, sets the car spinning and takes its grip away for a moment. It slides on its old line while it turns, and its own grip comes back as the spin dies down. Set recoverDamping low and the nose keeps swinging after the slide, fighting the steering.
A grip change from anything else (a tuning panel, a refit) is kept as the car's own grip. A pooled car that leaves play mid-slide gets its grip back first, so it never comes back stuck with the slide grip.
The world
With a HighwayRoad and RoadsideScenery in the scene, roadside props are solid and a bridge's rails are walls. Grass drags the car down and jolts it. The river drowns it. fieldEdge is a hedge at the far edge of the fields, so no car drives off the generated ground into empty fog.
A kinematic car (a trailer on its hitch) pushes and is never pushed back. A car tagged ghost is ignored, like a car inside a truck or between lives.
Settings
| Field | Default | What it does |
|---|---|---|
halfWidth | 0 | Half the body's width, metres. 0 uses half the MeshPrimitive's width. |
halfLength | 0 | Half its length. 0 uses half the MeshPrimitive's depth. |
mass | 1.4 | How heavy the car is. A heavy car shoves a light one further. |
restitution | 0.3 | Bounce between two cars, 0 to 1. The two cars' values are averaged. |
friction | 0.35 | Scrape grip between two cars, 0 to 1. Also averaged. |
spinGain | 1.6 | Multiplies the yaw a hit gives this car. |
maxSpin | 7 | Top yaw rate, rad/s. |
spinDecay | 2.4 | How fast a spin dies while the car slides, per second. Higher is a shorter slide. |
slideGrip | 0.3 | Share of the car's grip left while it slides, 0 to 1. |
gripBackAt | 1.8 | Spin (rad/s) below which the tyres bite again. |
recoverDamping | 9 | How fast the leftover spin dies once the tyres bite, per second. |
damageThreshold | 4 | Knock (m/s) the car shrugs off with no damage. |
damagePerKnock | 2.2 | Damage per m/s of knock past the threshold. |
armor | 0 | Share of crash damage this car ignores, 0 to 1. |
ramDamage | 1 | Multiplies the damage this car deals when it rams. |
damageTaken | 1 | Multiplies every hit the car takes. 0.5 is half, 0 is invincible (it still drowns). |
offroad | true | The car feels the road: grass, river, bridge rails and the field edge. Off, it ignores all four. |
offroadDrag | 0.9 | How fast the grass slows the car, per second. |
scenery | true | It can hit trees, poles and houses. |
fieldEdge | 45 | Metres past the paved edge where a hedge stops the car. 0 is none. |
kinematic | false | Moved by something else. It pushes other cars, takes no push back, and takes no damage. |
Events
| Event | Payload | Direction |
|---|---|---|
carCollision.hit | { a, b, x, z, closing, dvA, dvB } | emits |
carCollision.crashed | { entityId, kind, x, z, closing } | emits |
carCollision.spinout.started | { entityId } | emits |
carCollision.spinout.ended | { entityId } | emits |
carCollision.surface.changed | { entityId, surface }, surface is road, shoulder, verge, rail or water | emits |
carCollision.drowned | { entityId, x, z } | emits |
carDrive.nudge | { entityId, dx, dz, dvx, dvz, dheading } | emits |
health.damage | { entityId, damage } | emits |
carCollision.spin | { entityId, spin, seconds } | listens |
closing is in m/s. A touch under 2 m/s sends nothing, and one car sends at most one crash every 0.35 s.
Needs
- CarDrive moves every car. This reads its heading, speed and grip, and sends fixes back through
carDrive.nudge. - MeshPrimitive gives each car's position, and its size when the half sizes are 0.
- HighwayRoad says what is under each car, where the bridge rails are, and where the river runs.
- RoadsideScenery lists the props beside the road.





















































