logo

Babylon.js Market

FreighterChain

shooter · space-combat-sim

FreighterChain

1
Unlock

Install with the CLI:

bjs download FreighterChain

FreighterChain

A snake-style freighter convoy for 6-DOF space flight: one head flies a path, N trailing cars retrace it on a fixed time delay.

What it does

The convoy is a snake made of two pieces. FreighterHeadComponent marks the locomotive; each FreighterCarComponent is a car with an index (0 = nearest the head) and the id of the head it follows. Two systems drive it. FreighterChainSystem records the head's pose (position + quaternion + timestamp) into a circular history ring every tick — the ring lives in a WeakMap on the system, never on the component — then samples that ring for each car at age (index + 1) · carSpacingSeconds, interpolating between the two bracketing samples. Car i ends up sitting exactly where the head was i delay-steps ago, so a slow head turn unrolls into a long, smooth snake. FreighterHelmSystem is the head's autopilot: it steers toward the current waypoint-tagged gate and advances its index on entry (or runs a sinusoidal drift weave when no waypoints exist), re-projects linear velocity from the body-forward each tick so motion stays locked to the nose, and blends in a steer-away for any entity tagged asteroid in its forward cone. Heads are found by the freighter-head tag; cars by their component. It emits and listens to no EventBus events.

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

    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
{
  "Freighter": {
    "tags": ["freighter", "freighter-head"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": 0 },
      "Velocity6DOF": {},
      "FreighterHead": { "carSpacingSeconds": 0.7, "capacity": 480 },
      "Renderable": { "shape": "freighter-head", "glbSrc": "/wingman/freighter.glb" }
    }
  },
  "FreighterCar0": {
    "tags": ["freighter", "freighter-car"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": -32 },
      "FreighterCar": { "index": 0, "headEntityId": "Freighter" },
      "Renderable": { "shape": "freighter-car", "glbSrc": "/wingman/freighter.glb" }
    }
  }
}

Props

FreighterHead

  • carSpacingSeconds (number, default 0.7) — per-car age offset in seconds; car i samples the head's history at clock − (i+1)·carSpacingSeconds. The visible gap between cars is roughly this times cruise speed (~45 u/s).
  • capacity (number, default 480) — ring-buffer length in samples (read-only after construction). Must cover the tail's total delay at your frame rate — see Notes.
  • clock (number, default 0) — seconds the head has been alive; advanced by the chain system each tick and used as the phase source for the drift weave. Runtime state — leave at 0.
  • currentWaypointIndex (number, default 0) — index of the gate the helm is steering toward; the helm bumps it on each gate entry. Runtime state — leave at 0 (or set to skip ahead in the track).

FreighterCar

  • index (number, default 0) — position in the chain, 0 = closest to the head. Determines the sampling delay; surviving cars keep their index, so the chain isn't compacted on death.
  • headEntityId (string, default "") — id of the FreighterHead entity this car retraces. Empty or missing = the car is left where it is.

Events

None. The convoy is driven entirely by the two systems reading tags and pose history — FreighterChain emits and listens to no EventBus events (meta.emits and meta.listens are both empty).

Dependencies

  • Transform6DOF — the 6-DOF pose on both heads and cars; the chain writes each car's transform straight from sampled history, and Movement6DOF integrates the head's velocity.
  • Velocity6DOF — on the head only; the helm writes linear + angular velocity into it and the integrator moves the head. Cars carry no velocity — the chain positions them.
  • Waypoint — optional gates the helm steers through. Tag each waypoint and give it a Transform6DOF + Waypoint { index, radius }; the helm advances currentWaypointIndex as it clears them.

Notes

  • The head must carry the freighter-head tag — the chain system iterates heads by tag, not by a component query. A FreighterHead component without the tag records no history and its cars never move.
  • Don't put Velocity6DOF on a car. The chain overwrites the car's transform from history every tick; a velocity integrator running alongside just fights it for the frame. Cars need only FreighterCar + Transform6DOF (+ a Renderable to be seen).
  • Size capacity for the whole tail: at ~60 fps the ring holds capacity / 60 seconds of history, and the last car reads back numCars · carSpacingSeconds seconds. The default 480 (~8 s) covers a long train; if the tail bunches or clamps, the buffer is too short.
  • With no waypoint entities the head runs a gentle sinusoidal drift weave (driven by clock) — enough to show the snake without a track. Add gates to carve a deliberate arc; the trailing cars turn the reveal into a long S.
  • Asteroid avoidance is automatic and configuration-free on FreighterChain: any entity tagged asteroid that exposes a size (hx/hy/hz half-extents or a radius) and sits in the head's forward cone bends the whole convoy around it. No rock in the scene = a harmless no-op.
  • History lives in a WeakMap on FreighterChainSystem, not on the component, so save/load round-trips trivially — the buffers simply refill from live poses on reload.

More like this

Transform6DOF
Velocity6DOF
Waypoint
AIGoalSeek
AIKick
AIZone
AiPilot
Animation
ArcCamera
Asteroid
BallPossession
BallPursuit
Bullet
CameraFollow
ChaseCamera6DOF
ChaseCameraTarget
DirectionalLight
Enemy
EnemySpawner
EnvironmentTexture
FlightIntent
FreighterCar
FreighterHead
GameConfig
Health
HemisphericLight
Jump
KeyboardInput
KeyboardMover
Lifetime
LineOfSight
MachineGun
Mesh
MeshPrimitive
Missile
MissileLauncher
MissionDirector
MoneyField
Movement
Obstacle
ObstacleField
OilBlob
Parts
Physics
PlayerFlightInput
PlayerInput
PlayerWalkAnimator
PokerAI
PokerAIBill
PokerAICali
PokerAIRandy
PokerAIShark
PokerAISurge
PokerAITilt
RespawnTimer
Score
Shadow
ShipFlight
ShipLoadout
ShooterCamera
SkeletonAnimator
SpaceDust
SpaceShooterBullet
SpaceShooterHealth
SpaceShooterScore
TwinStickShooter
WaveDirector
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