logo

Babylon.js Market

By Lawrence

5 minutes

Every answer this stage has so far runs through the front bumper, and lesson 4 made that expensive — the ram that kills a hostile bills you for the same collision. Outrunning the problem is not the plan either: the red pool's profile tops out at 34 against your 30. A combat car needs something it can leave behind it.

Two charges, three clocks

One component, on the player, next to the drive law:

"SmokeScreen": {
  "maxCharges": 2,
  "rechargeSeconds": 8,
  "ventSeconds": 1.6,
  "dropInterval": 0.2,
  "puffLifetime": 4,
  "maxPuffs": 12,
  "dropBack": 3.5,
  "puffY": 0.5,
  "blindRadius": 5,
  "pursuerTag": "enemy",
  "deployKeys": ["Space"]
}

No new entity. deployKeys is a list of KeyboardEvent.code values, and the Input entity from lesson 2 is what publishes the keyboard.keydown those keys are matched against — without it the car has a rack of smoke and no button.

A screen is not one cloud, and the state it needs to be four numbers wide:

src/components/SmokeScreen/SmokeScreen.core.ts
/** Charges held, and the three clocks. */
export interface SmokeScreenState {
  charges: number;
  /** Seconds until the next charge returns. 0 when the rack is full. */
  rechargeTimer: number;
  /** Seconds left venting. 0 means the button is not down. */
  ventTimer: number;
  /** Seconds to the next puff. */
  dropTimer: number;
}

Press the button and the car vents for ventSeconds, dropping a puff every dropInterval — a line of overlapping clouds laid down the road, each blinding for puffLifetime before it thins out. Which is why the screen keeps working while you are turning: the puffs mark where you were.

stepSmokeScreen advances all four in one call and reports what the frame owes:

const p = { maxCharges: 2, rechargeSeconds: 8, ventSeconds: 1.6,
            dropInterval: 0.2, puffLifetime: 4, maxPuffs: 12 }

let smoke = createSmokeScreenState(p)                   // → charges 2, every clock at 0

const press = stepSmokeScreen(smoke, p, true, 1 / 60)
// → { deployed: true, drops: 1, refused: false }        one charge gone, venting for 1.6s

const leaning = stepSmokeScreen(press.state, p, true, 1 / 60)
// → { deployed: false, drops: 0, refused: false }       still 1 charge left

The second call is the interesting one. The button is down, the rack is not empty, and nothing happens — a press made mid-vent is ignored, not refused, so leaning on the key cannot drain both charges into one cloud. refused is reserved for the press that finds an empty rack, and it is what fires smokeScreen.depleted for the empty-click sound.

drops is normally 0 or 1. It goes higher only after a long frame, because dropTimer carries its remainder across frames rather than resetting: puff spacing comes out identical at 30fps and at 144, and a hitch drops the puffs it owes instead of skipping them, capped at maxPuffs so a stall cannot mint a hundred clouds.

Continue reading

Unlock the Full Course

Every lesson, the runnable examples, and the finished build — yours to keep.

$9one-time

Was this page helpful?

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

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search