logo

Babylon.js Market

ShipFlight

movement · space-combat-sim

ShipFlight

1
Ace the Starfighter PilotBonus figureAce the Starfighter Pilot
Unlock

Install with the CLI:

bjs download ShipFlight

ShipFlight

Per-ship flight tuning plus the control law that turns pilot intent into 6-DOF motion.

What it does

ShipFlightComponent is six handling knobs — maxThrust, boostMultiplier, turnRate, rollRate, linearDrag, angularDrag — that define how one ship flies. Its FlightControlSystem (also exported as ShipFlightSystem) runs every frame over each ship carrying FlightIntent + ShipFlight + Transform6DOF + Velocity6DOF (and NOT tagged dead). It reads the normalized intent and writes velocity: forward thrust is throttle · maxThrust · (boost ? boostMultiplier : 1) pushed along the nose (the Transform6DOF quaternion's forward vector); pitch and yaw add body-frame angular acceleration scaled by turnRate, roll by rollRate (sign-flipped). Then it applies TRUE exponential drag — exp(-linearDrag·dt) on linear velocity and exp(-angularDrag·dt) on angular — framerate-independent, never the 1−drag·dt approximation. It writes only Velocity6DOF; Movement6DOFSystem integrates that into the pose, so the whole stack stays gimbal-lock-free.

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

    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,
        "boostMultiplier": 2.5,
        "turnRate": 5.0,
        "rollRate": 8.0,
        "linearDrag": 0.6,
        "angularDrag": 3.5
      },
      "Renderable": { "shape": "spaceplane", "glbSrc": "/wingman/falconship.glb" },
      "ChaseCameraTarget": {},
      "PlayerFlightInput": { "throttleRate": 1.4 }
    }
  }
}

Props

  • maxThrust (number, default 80) — peak forward thrust at full throttle, in units/sec², pushed along the ship's nose.
  • boostMultiplier (number, default 2.5) — thrust multiplier applied while FlightIntent.boost is held.
  • turnRate (number, default 1.4) — pitch & yaw angular acceleration per unit of intent, in rad/sec².
  • rollRate (number, default 5.5) — roll angular acceleration per unit of intent, in rad/sec² (applied sign-flipped).
  • linearDrag (number, default 0.6) — exponential linear-velocity decay coefficient, in 1/sec (exp(-linearDrag·dt) per frame).
  • angularDrag (number, default 3.5) — exponential angular-velocity decay coefficient, in 1/sec (exp(-angularDrag·dt) per frame).

Events

Nothing on the EventBus — FlightControlSystem neither emits nor listens (there are no ShipFlightEvents / ShipFlightInputEvents). It couples through shared component state instead: each frame it reads the sibling FlightIntent and writes the sibling Velocity6DOF, which Movement6DOFSystem then integrates into Transform6DOF. Feed it intent by giving the same entity a driver — PlayerFlightInput for the player, an AI pilot for enemies.

Dependencies

  • FlightIntent — the normalized controls (pitch, yaw, roll, throttle, boost) this law reads each frame.
  • Transform6DOF — supplies the pose quaternion whose forward vector thrust is pushed along.
  • Velocity6DOF — the body this law writes linear + angular velocity onto; Movement6DOFSystem integrates it into the pose.

Notes

  • Tuning lives here, separate from FlightIntent, on purpose: the same normalized controls drive a nimble fighter and a sluggish freighter completely differently — just by changing these six numbers per entity. That split is the component's whole reason to exist.
  • The body-frame angular signs, and the roll sign-flip in particular, are load-bearing — get them wrong and the stick inverts. Don't "tidy" the signs.
  • Drag is exp(-drag·dt), never 1−drag·dt (which goes unstable and inverts at large dt), so handling stays identical whether you run at 30 or 144 fps.
  • The System runs at priority 80, ahead of the movement integrator, so Velocity6DOF is fresh for the same frame's pose update.
  • Ships tagged dead are skipped (excludedTags: ['dead']) — a destroyed hull coasts on whatever velocity it had, it doesn't keep thrusting.
  • On its own the ship goes nowhere: with no driver FlightIntent stays zero, so drag simply brings any residual velocity to rest. Pair it with PlayerFlightInput (which needs a KeyboardInput entity in the scene) or an AI pilot.

More like this

FlightIntent
Transform6DOF
Velocity6DOF
Animation
Asteroid
CameraState6DOF
ChaseCamera6DOF
ChaseCameraTarget
FreighterCar
FreighterChain
FreighterHead
GameConfig
Jump
KeyboardMover
Movement
OilBlob
PlayerFlightInput
Renderable
SpaceDust
SpaceShooterBullet
SpaceShooterHealth
SpaceShooterScore
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