logo

Babylon.js Market

PlayerFlightInput

input · space-combat-sim

PlayerFlightInput

Listens toKeyboardInput
1
Unlock

Install with the CLI:

bjs download PlayerFlightInput

PlayerFlightInput

Maps the keyboard onto a player ship's FlightIntent — the player half of a 6-DOF flight game's controls.

What it does

PlayerFlightInputSystem is the human pilot behind a 6-DOF ship. On init it subscribes to the canonical keyboard.keydown / keyboard.keyup bus events (emitted by KeyboardInput) and tracks the set of held key codes. Each frame it rebuilds normalised intent from that set and writes it onto every matched ship's sibling FlightIntentComponent: ArrowUp/Down pitch (Up drops the nose), ArrowLeft/Right yaw, A/Q and D/E roll, W/S ramp the throttle up/down at the component's throttleRate (clamped to [0, 1]), and Left/Right Shift set boost. pitch/yaw/roll are recomputed fresh from the held set each frame — release a key and that axis returns to 0 — while throttle is an accumulator that read-modify-writes and persists between frames. It runs at priority 100, ahead of the flight law that consumes the intent, and skips any ship tagged dead. The bindings and their signs are ported verbatim from Wingman; roll is deliberately left un-flipped here because the flight law applies the wz += -roll flip downstream.

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

    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
{
  "Player": {
    "tags": ["player"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": 0 },
      "Velocity6DOF": {},
      "FlightIntent": {},
      "ShipFlight": { "maxThrust": 80, "turnRate": 5.0, "rollRate": 8.0 },
      "Renderable": { "shape": "spaceplane", "glbSrc": "/wingman/falconship.glb" },
      "PlayerFlightInput": { "throttleRate": 1.4 }
    }
  }
}

(A KeyboardInput component must exist somewhere in the scene — put one on a world/global entity — so the keyboard.* events this system reads are actually emitted.)

Props

  • throttleRate (number, default 1.4) — how fast the W / S keys ramp the throttle from 0 → 1, in throttle-units per second. The only serialisable knob; the key bindings themselves are fixed constants on the System.

Events

  • emits nothing — this system only writes intent onto the sibling FlightIntentComponent, it doesn't touch the bus.
  • listens keyboard.keydown (KeyboardInputEvents.KEYDOWN) — { code }; adds the key code to the held set.
  • listens keyboard.keyup (KeyboardInputEvents.KEYUP) — { code }; removes it from the held set.

Dependencies

  • KeyboardInput — bridges DOM key events onto the keyboard.keydown / keyboard.keyup bus events this system reads. Without it the held set never fills and the ship never responds; put one on a world/global entity.
  • FlightIntent — the sibling this system writes each frame; it must sit on the same entity (it's in the query's required). Downstream, ShipFlight reads that intent plus the ship's tuning to drive Velocity6DOFTransform6DOF, and Renderable draws the model — so a fully controllable ship is Transform6DOF + Velocity6DOF + FlightIntent + ShipFlight + Renderable + PlayerFlightInput.

Notes

  • The query is required: [PlayerFlightInputComponent, FlightIntentComponent], excludedTags: ['dead'] — a ship with no FlightIntent is never matched, and tagging a ship dead freezes its input without removing the component.
  • The signs are load-bearing: ArrowUp = +1 (nose down), ArrowDown = -1; ArrowLeft = -1, ArrowRight = +1; A/Q = -1, D/E = +1. They match the Wingman map exactly; drift here and the flight law inverts the stick.
  • Roll is intentionally NOT flipped here — the flight law applies wz += -roll · rollRate, so flipping again would double-negate.
  • Priority 100 puts it ahead of the flight law (80) and the Movement6DOF integrator (60), so intent is current before it's consumed each frame.
  • throttle persists frame-to-frame (it's integrated, not sampled), so it holds its setting when W / S are released; pitch/yaw/roll do not — they zero the instant the key is up.
  • Renderer-agnostic: zero @babylonjs / three imports; the System constructor takes (eventBus) only.

More like this

FlightIntent
KeyboardInput
Asteroid
CameraState6DOF
ChaseCamera6DOF
ChaseCameraTarget
Flipper
FreighterCar
FreighterChain
FreighterHead
GameConfig
Jump
KeyboardMover
OilBlob
PinballBuilderInput
PlayerInput
PlayerInputBridge
Plunger
PokerInput
Renderable
ShipFlight
SpaceDust
SpaceShooterBullet
SpaceShooterHealth
SpaceShooterScore
Transform6DOF
Velocity6DOF
WaveDirector
Waypoint
WaypointTrack
WorldOriginAnchor

Was this page helpful?

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

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search