logo

Babylon.js Market

WeaponsVan

vehicular · vehicle-combat

WeaponsVan

1
Unlock

Install with the CLI:

bjs download WeaponsVan

WeaponsVan

Drive into the back of the truck, come out the front with something new bolted on.

What it does

WeaponsVanComponent is a van's stock and its timings; WeaponsVanSystem runs the swap. A car within hitRadius is swallowed — hidden, parked at the van, held for holdSeconds — and then put back down at the van's exit point with the next weapon off the stock, announced as weaponsVan.armed. The stock cycles, so a van with two weapons alternates rather than handing out the same thing every trip.

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.

Fastest path

bjs download scene WeaponsVan

Pulls this exact scene into your project and runs it — no copy-paste. Add --all to grab the components and assets it needs too.

Or 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 WeaponsVan
    bjs download WeaponsVan

    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
{
  "Van": {
    "components": {
      "MeshPrimitive": { "primitive": "box", "width": 2.4, "height": 2.6, "depth": 6, "position": [6, 1.3, 60] },
      "WeaponsVan": {
        "hitRadius": 4,
        "holdSeconds": 0.9,
        "cooldownSeconds": 2,
        "exitForward": 9,
        "weapons": ["machinegun", "missiles", "oilSlick"],
        "ammo": 30,
        "uses": 3
      }
    }
  },
  "Car": {
    "tags": ["player"],
    "components": {
      "MeshPrimitive": { "primitive": "box", "width": 1.8, "height": 1, "depth": 4, "position": [0, 0.5, 0] },
      "CarDrive": {}
    }
  }
}

The van needs no tag of its own; it looks for captureTag (default player) and never at itself.

Four phases and one clock

ready → holding → cooldown → ready …
                ↘ spent (terminal, once the last use is gone)

inRange only matters in ready. Once a car is inside, the van holds it for the full holdSeconds whatever the caller reports — the car is in the back of a truck, it doesn't get a vote. That's what makes the hold safe against a car that gets shoved out of range by a collision mid-swap.

cooldownSeconds is the part people cut and then regret. Put the exit anywhere inside hitRadius — which is normal, since a van 6 long with a capture radius of 4 has an exit two units past its own nose — and the cooldown is the only thing between one swap and an endless loop of them.

The car's mesh is written exactly twice

A captured car isn't pinned every frame. It's teleported into the van on capture, teleported to the exit on release, and left alone in between:

TypeScript
const at = exitPoint(
  this._van[0],
  this._van[2],
  vanMesh.rotation[1],
  comp.exitForward,
  comp.exitSide,
);

Its own drive law keeps running while it's out of sight, so a player holding the throttle comes out of the van already moving — and because the release teleport is the last word, wherever the car "drove" while invisible doesn't matter. The System runs at priority -10 so those two writes land after the drive laws in the same frame, rather than fighting them for one mesh handle.

Props

  • hitRadius (number, default 3.5) — centre-to-centre XZ distance at which the van swallows a car.
  • holdSeconds (number, default 0.9) — how long the car spends inside. The beat the swap reads as.
  • cooldownSeconds (number, default 2) — dead time after a release, so the van can't re-take its own exit.
  • uses (number, default 0) — swaps the van serves before it's spent. 0 is unlimited.
  • exitForward (number, default 8) — world units ahead of the van's nose the car reappears.
  • exitSide (number, default 0) — units to the van's right. Negative for its left.
  • captureTag (string, default "player") — which vehicles the van serves.
  • weapons (string[], default ["machinegun", "missiles", "oilSlick", "smokeScreen"]) — handed out in order, cycling.
  • ammo (number, default 30) — rounds handed over with each weapon, echoed on weaponsVan.armed.
  • hideWhileHeld (boolean, default true) — false leaves the car visible on the ramp, which is what you want for an open flatbed.

Events

  • emits weaponsVan.captured{ entityId, vanEntityId } — a car went in. entityId is the car.
  • emits weaponsVan.released{ entityId, vanEntityId, x, z } — it came out, and where it was put down.
  • emits weaponsVan.armed{ entityId, vanEntityId, weapon, ammo } — the swap itself. This is the one a weapon component listens for; weapon is a name off the stock, empty only when the van carries nothing.
  • emits weaponsVan.spent{ entityId }, where entityId is the van — it served its last swap. The cue to dim its lights or despawn it.
  • listens for weaponsVan.restock{ entityId?, weapons?, uses? } — refills a van and resets its cycle, optionally replacing the stock and the allowance. Omit entityId to restock every van.

Dependencies

  • MeshPrimitive — on the van and on every vehicle it serves. The van's rotation[1] is what aims the exit.

Notes

  • A restock that arrives mid-hold hands the car back rather than leaving it invisible forever, and opens the van on its cooldown instead of ready — a car standing in the van's mouth would otherwise be swallowed again on the next frame and the restock would look like it did nothing.
  • The same protection covers a van that leaves the world, and shutdown: whatever it was holding gets its mesh made visible again.
  • The restock is applied at the top of the next update, not inside the bus callback, so the mesh writes it triggers stay in the update loop where every other write in the frame lives.
  • Positions are read live through getMeshWorldPosition. Nothing writes MeshPrimitiveComponent.position back from physics, so a van that trusted it would keep serving a car that had long since driven away.
  • When two cars are in range the van takes the nearest. Parked (pooled) cars are skipped, so a recycled wreck can't claim a swap.
  • uses is the one knob the panel marks dead. It seeds the allowance when the van's state is created — once per van, and again on a restock — so dragging it won't refill a van that has already run out. Send a weaponsVan.restock instead.
  • No hotkey on the tuning panel: the driving controls own the letters and the spare digits are taken. Open it from the panel index.

More like this

MeshPrimitive
AiPilot
Asteroid
BoatMode
BoatWater
Bullet
CarDrive
ContactDamage
Enemy
Health
MachineGun
Missile
MissileLauncher
MoneyField
OilBlob
Parts
RoadSpawner
RoadTreadmill
ShipLoadout
SmokeScreen
SurfacePatch
TrafficConvoy

Was this page helpful?

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

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search