logo

Babylon.js Market

MissileLauncher

shooter

MissileLauncher

Listens toKeyboardInput
1
Unlock

Install with the CLI:

bjs download MissileLauncher

MissileLauncher

The player's lock-on missile rail for a 6-DOF dogfighter — a sticky forward-cone lock that tracks the nearest enemy, and a Space-to-fire law that spits pooled homing missiles down the barrel.

What it does

MissileLauncherComponent is pure weapon state for one ship: ammo (a count, or Infinity for an unlimited rail — stored as the "infinity" string across a JSON round-trip), fireCooldown (seconds until the next launch is allowed), and lockedTargetId (the enemy currently in the crosshairs, plus lockedDistance for the HUD). Two systems drive it. TargetingSystem (priority 85) owns the lock: each frame it keeps the held target while it's still alive, within 600 units, inside the ±30° forward cone, and has clear line-of-sight — no asteroid-tagged rock between ship and target, which lets the HUD keep tracking an enemy behind cover without letting you lock through rock. Only when the held lock fails does it rescan every enemy-tagged entity for the nearest valid candidate. MissileLauncherSystem (priority 78) is the fire law: on the Space press edge (edge-triggered, so holding the key fires once), with a lock, ammo, and an elapsed 0.35s cooldown, it acquires a pooled missile 2 units off the nose, hands it the ship's velocity plus an 80u/s launch boost and the locked target id, decrements finite ammo (an infinite supply stays Infinity), arms the 0.35s cooldown, and emits wingman.missile.fired. Missiles come from a pre-allocated 32-slot missile World pool the System registers on init — acquired and released back, never createEntity'd per shot.

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

    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": { "vz": 30 },
      "MissileLauncher": { "ammo": 20 }
    }
  },
  "Enemy1": {
    "tags": ["enemy"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": 200 },
      "Renderable": { "shape": "ship", "sx": 6, "sy": 6, "sz": 10 }
    }
  }
}

The ship sits at the origin facing +Z; the enemy 200 units dead ahead falls inside the cone, so the lock latches and Space launches. You never declare the missiles — the System pre-builds the pool.

Props

  • ammo (number | "infinity", default Infinity) — missiles remaining. A finite number decrements one per launch and blocks fire once it hits 0; Infinity never decrements. JSON can't hold Infinity, so serialize() writes the string "infinity" and the constructor rebuilds it — a finite scene value stays a plain number.
  • fireCooldown (number, default 0) — seconds until the next launch is allowed. MissileLauncherSystem counts it down each frame and resets it to 0.35 on every shot; a launch is refused while it's above 0. Usually left at 0 in the scene.
  • lockedTargetId (string | null, default null) — entity id of the enemy currently inside the forward cone, written by TargetingSystem. Read it for the HUD reticle; the fire law refuses to launch while it's null. Runtime state — no reason to set it in a scene.
  • lockedDistance (number, default 0) — distance in units to the locked target, refreshed each frame for the HUD readout; 0 when nothing is locked.

Events

  • Emits wingman.missile.fired (MissileLauncherEvents.FIRED) on every launch — { missileId, targetId, ammoRemaining, x, y, z }. HUD ammo counters, launch SFX, and muzzle FX subscribe here; the missile's own guidance / impact / wasted-shot events live on the Missile component, not this one.
  • Listens for keyboard.keydown / keyboard.keyup (KeyboardInputEvents.KEYDOWN / KEYUP) — MissileLauncherSystem tracks held keys off the bus so it can edge-detect the Space press (a held key fires exactly once). That's the only bus input: the lock is driven off entity queries (the enemy tag + Transform6DOF), never off events.

Dependencies

  • Transform6DOF — the ship's pose. Its quaternion gives the forward vector for the ±30° cone test and the muzzle offset; its position anchors the 600u range check and the spawn point. Every pooled missile carries one too.
  • Velocity6DOF — the ship's velocity, inherited by each launched missile plus an 80u/s forward boost so a shot never stalls behind a fast ship. Each missile slot has its own for the guidance law to steer.
  • Missile — the pooled projectile the launcher fires; it holds the target id, and its homing / detonation / flew-past logic lives on that component. The launcher only spawns and aims it.
  • Renderable — draws each pooled missile (shape: "missile"), built once per slot when the pool is registered.
  • Lifetime — the 6s self-destruct that releases a missile back to the pool if it never reaches its target.

Notes

  • Both systems require the player tag. TargetingSystem and MissileLauncherSystem query tags: ['player'] — a ship carrying the component but missing the tag never locks and never fires. MissileLauncherSystem also excludes dead, so a downed ship stops shooting.
  • You don't spawn missiles yourself. MissileLauncherSystem registers a 32-slot missile pool on init (idempotent) and acquires/releases slots; the peak (6s lifetime ÷ 0.35s cooldown ≈ 18) fits inside 32 with headroom. Per-shot spawns are pooled, never createEntity.
  • The two priorities are load-bearing. TargetingSystem (85) writes the lock before MissileLauncherSystem (78) reads it the same frame, so an enemy that enters the cone this frame can be fired at this frame. Keep that order if you re-register the systems.
  • Line-of-sight only bites with asteroids present. The cone lock calls segmentClearOfAsteroids; with no asteroid-tagged rocks in the scene it always passes and the lock is a pure ±30° / 600u cone.
  • Infinite ammo is the default. Leave ammo unset for an unlimited rail; give it a finite number for a loadout. The tuning panel's "reload ∞" button restores Number.POSITIVE_INFINITY.

More like this

KeyboardInput
Lifetime
Missile
Renderable
Transform6DOF
Velocity6DOF
AiPilot
Animation
ArcCamera
Asteroid
Bullet
CameraFollow
DirectionalLight
Enemy
EnemySpawner
EnvironmentTexture
FreighterCar
FreighterChain
FreighterHead
GameConfig
Health
HemisphericLight
Jump
KeyboardMover
LineOfSight
MachineGun
Mesh
MeshPrimitive
MissionDirector
MoneyField
Movement
Obstacle
ObstacleField
OilBlob
Parts
Physics
PlayerInput
PlayerWalkAnimator
RespawnTimer
Score
Shadow
ShipLoadout
ShooterCamera
SkeletonAnimator
SpaceShooterBullet
SpaceShooterHealth
SpaceShooterScore
TwinStickShooter
WaveDirector
Waypoint
WaypointTrack

Was this page helpful?

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

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search