logo
Slingshot

pinball

Slingshot

1
Unlock

Install with the CLI:

bjs download Slingshot

Slingshot

The rubber kicker above each flipper.

What it does

On a real table the ball presses the rubber, a switch closes, and a solenoid snaps a bar into it. The ball leaves faster than it came.

Slingshot builds the rubber as a child box, <id>_face, with a static Physics body, so the ball bounces off it like a wall. Each frame it checks every ball. When one is touching the middle of the face, it fires:

  • The ball's speed across the face becomes at least kick. Its run along the face is kept.
  • The face flashes flashColor.
  • It says slingshot.kicked and score.add.

A worked kick, on a face that points along +X with kick 16:

TypeScript
slingKick(-3, -4, 1, 0, 16) // { vx: 16, vz: -4 }, speed 16.49

The 3 into the face became 16. The 4 along it stayed.

The ends of the face are posts, not rubber. Only the middle activeSpan fires, 80% by default. After a kick the face waits cooldown seconds before it can fire again, so one touch is one kick.

The physics engine has usually bounced the ball before the system looks, so the ball may already be moving away. The kick does not care which way it was going.

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

    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
{
  "Sling_Left": {
    "components": {
      "Slingshot": {
        "from": [-6.6, 25.6],
        "to": [-4.7, 31],
        "kick": 15,
        "points": 10
      }
    }
  },
  "Sling_Left_Back": {
    "components": {
      "PinballWall": {
        "points": [[-6.6, 25.6], [-6.6, 30], [-4.7, 31]],
        "height": 1,
        "thickness": 0.3
      }
    }
  }
}

The face is one side of the triangle. Close the other two sides with a PinballWall so the ball cannot get behind it.

Props

  • from, to ([x, z], defaults [0, 0] and [0, 2]). The two ends of the rubber.
  • kick (number, default 16). The least speed the ball leaves at, straight off the face.
  • points (number, default 10). Paid to ownerEntity on each kick.
  • ownerEntity (string, default "Player"). Who gets the points.
  • cooldown (seconds, default 0.15). Wait before the same face fires again.
  • activeSpan (0 to 1, default 0.8). How much of the middle of the face fires.
  • ballRadius (number, default 0.42). For the touch test.
  • thickness (number, default 0.5), height (number, default 1). Size of the rubber.
  • color ([r, g, b], default [0.95, 0.95, 1]).
  • flashColor ([r, g, b], default [1, 0.85, 0.3]). Glow when it fires.

Events

Emits:

  • slingshot.kicked, { entityId, ballId, points, speed }. speed is how fast the ball leaves.
  • score.add, { ownerEntity, points }.

Dependencies

MeshPrimitive, Physics and Shadow for the face. Score to count the points. The ball needs the ball tag and a dynamic Physics body.

The kick maths

  • The kick is in Slingshot.core.ts: slingKick for the new velocity and onFace for the touch test.
  • Each slingshot fires at most one ball per frame.

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