logo
PinballGate

pinball

PinballGate

1
Unlock

Install with the CLI:

bjs download PinballGate

PinballGate

A one-way gate.

What it does

A real one-way gate is a flap on a hinge. Push it from one side and it swings up out of the way. Push from the other and it hits a stop.

A physics engine has no solid that only stops things one way. But a solid can come and go. So the gate is solid while the ball is past it, and gone while the ball is before it. The ball goes through, and cannot come back.

PinballGate builds the flap as a child entity, <id>_flap, along from to to. Each frame it works out which side of the gate the ball is on:

  • More than a ball radius past the gate: the flap gets a static body, and it says pinballGate.passed.
  • More than a ball radius before it: the body goes.
  • In between, the ball is in the gate, and nothing changes. A body that appears there would be inside the ball, and the engine would shove the ball out sideways.

While the ball goes through, the flap swings up so you can see it. The side is only checked while the ball is level with the gate, so a ball elsewhere on the table never flips it.

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

    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
{
  "Wall_Lane": {
    "components": {
      "PinballWall": {
        "points": [[6.2, 39], [6.2, -22]],
        "height": 1.2
      }
    }
  },
  "Lane_Gate": {
    "components": {
      "PinballGate": {
        "from": [6.4, -22],
        "to": [7.8, -22],
        "passDir": [0, -1]
      }
    }
  }
}

This is the top of a shooter lane. The ball goes up the lane (-Z) onto the playfield and can never roll back down into it.

Props

  • from, to ([x, z], defaults [-1, 0] and [1, 0]). The two ends of the gate.
  • passDir ([x, z], default [0, -1]). The way through. It only needs to point to the right side of the gate.
  • height (number, default 0.9). Flap height.
  • thickness (number, default 0.1).
  • ballRadius (number, default 0.42). How far past the gate the ball must be before it shuts.
  • color ([r, g, b], default [0.9, 0.9, 0.95]).

Events

Emits:

  • pinballGate.passed, { entityId, ballId }. The ball went through the allowed way.

Dependencies

MeshPrimitive and Physics for the flap. The ball needs the ball tag. It watches the first ball only.

The side test

  • The side test is in PinballGate.core.ts: gateSide gives the signed distance, and gateSolid decides.
  • A ball that starts past the gate shuts it on the first frame and counts as a pass.

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