logo

Babylon.js Market

Missile

shooter

Missile

1
Unlock

Install with the CLI:

bjs download Missile

Missile

A fire-and-forget lock-on missile for a 6-DOF dogfighter: it homes on the entity you locked at launch with a capped turn rate, and if it overshoots a hard-jinking target the shot is spent.

What it does

MissileComponent is pure data: the targetEntityId it chases (the player's lock at the moment of launch), the cruise speed its velocity steers toward, and minDistance — the closest approach seen so far, which the "flew past = wasted" rule keys off. Two systems drive it.

MissileGuidanceSystem (priority 75) is capped-turn-rate homing. Each frame it looks up the target's Transform6DOF, computes the desired velocity (target − missile) · speed / dist, and blends the current velocity toward it by min(1, turnRate · dt) — a fixed turnRate of 4.5 — then points the mesh down its velocity vector with setEulerYXZ. Because the turn is capped, a hard-jinking target can be overshot; the missile does not snap onto it, and that overshoot is what makes a miss cost you the shot.

MissileImpactSystem (priority 50, so it runs after guidance each frame) measures the gap to the same target. Inside explosionRadius (6u) it detonates: it routes the kill through the shared destroyShip path — the same death a gun kill uses (explosion, part detach, shootable debris) — emits wingman.missile.exploded with the impact point, and releases the missile. Otherwise it tracks the closing distance; once the missile is clearly receding (current distance > minDistance × 1.4) and never reached the radius, the shot is wasted — it emits wingman.missile.wasted and releases. If the locked target is already gone (killed by another missile), the missile keeps flying until its Lifetime expires. onInitialize registers the shared debris pool so a missile kill can shed debris even when no gun system set it up first. Missiles are framework-pooled, so removeEntity releases the slot back to the missile pool instead of destroying it.

Use it in a scene

Missiles are normally spawned from a pool by MissileLauncher, not hand-placed — but the shape of one in flight, locked onto a target, is:

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

    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
{
  "Enemy": {
    "tags": ["enemy"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": 200 },
      "Renderable": { "shape": "kilrathi" }
    }
  },
  "Missile1": {
    "tags": ["missile"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": 0 },
      "Velocity6DOF": { "vz": 220 },
      "Missile": { "targetEntityId": "Enemy", "speed": 220 },
      "Renderable": { "shape": "missile", "sx": 0.55, "sy": 0.55, "sz": 2.4 }
    }
  }
}

targetEntityId is the scene key of the entity to chase; world.getEntity("Enemy") resolves it. Give the missile a starting Velocity6DOF down its nose — guidance steers that velocity, it doesn't create it.

Props

  • targetEntityId (string, default "") — the entity id the missile homes on, set at launch. Guidance and impact both look it up each frame; an empty or stale id makes both systems skip the missile.
  • speed (number, default 220) — cruise speed (u/s) guidance steers the velocity toward. A slower missile is easier to out-jink, which feeds the wasted rule. Tunable live via Missile.panel.tsx.
  • minDistance (number, default Infinity) — closest approach to the target seen so far. Runtime state, not a tuning knob: it starts at Infinity so the first frame always records a minimum, then only the wasted rule (dist > minDistance × 1.4) reads it.

Events

  • Emits wingman.missile.exploded (MissileEvents.EXPLODED) on a detonation inside the explosion radius — { missileId, targetId, x, y, z }. This is the detonation-FX hook (the impact point is the burst origin); the kill itself is applied inline via destroyShip, not through this event.
  • Emits wingman.missile.wasted (MissileEvents.WASTED) when a missile flies past closest approach without ever detonating — { missileId }. Ammo is spent; the observer channel for a "missed" cue.
  • Listens for nothing. Both systems are driven off their entity queries — guidance over [Missile, Transform6DOF, Velocity6DOF], impact over [Missile, Transform6DOF] — never off the bus.

Dependencies

  • Transform6DOF — the missile's pose; both systems read it, and the target's Transform6DOF is what guidance steers toward and impact measures distance to. A target with no Transform6DOF is treated as gone.
  • Velocity6DOF — the missile's motion. MissileGuidanceSystem requires it and writes vx/vy/vz each frame; the integration into position lives in Movement6DOF. A missile missing it never appears in the guidance query and never steers.
  • Parts + SpaceShooterHealth — detonation calls destroyShip (owned by SpaceShooterHealth), which detaches the target's Parts, sheds debris, and removes/respawns the ship; SpaceShooterHealth also owns the debris pool MissileImpactSystem registers on init. A target needs both to actually die on impact.

Notes

  • Guidance and impact query differently. Guidance requires [Missile, Transform6DOF, Velocity6DOF]; impact only [Missile, Transform6DOF]. A missile without Velocity6DOF still gets distance-tested but never steers — it flies wherever its initial velocity points.
  • turnRate (4.5) and explosionRadius (6u) are fixed system constants, not scene props. They live on the systems, not the component, so they never serialize and can't be set per-entity in scene JSON — change the feel by editing the systems (or the live speed via the panel).
  • removeEntity releases the pool slot, it doesn't destroy the entity. Missiles are a pre-allocated missile World pool declared as a Missiles Pool blueprint in the scene; guidance, impact, and lifetime all release the slot back for reuse.
  • The wasted rule needs a real overshoot. A shot only counts as wasted once its minDistance exceeded explosionRadius and the current distance climbed past minDistance × 1.4. A missile still closing in, or one that briefly touched the radius, is never wasted.
  • A target killed mid-flight doesn't strand the missile. With the locked entity gone, both systems skip it, so it coasts on its last velocity until its LifetimeComponent expires and releases the slot.

More like this

Parts
SpaceShooterHealth
Transform6DOF
Velocity6DOF
AiPilot
Animation
ArcCamera
Asteroid
Bullet
CameraFollow
DirectionalLight
Enemy
EnemySpawner
EnvironmentTexture
FreighterCar
FreighterChain
FreighterHead
GameConfig
Health
HemisphericLight
Jump
KeyboardInput
KeyboardMover
Lifetime
LineOfSight
MachineGun
Mesh
MeshPrimitive
MissileLauncher
MissionDirector
MoneyField
Movement
Obstacle
ObstacleField
OilBlob
Physics
PlayerInput
PlayerWalkAnimator
RespawnTimer
Score
Shadow
ShipLoadout
ShooterCamera
SkeletonAnimator
SpaceShooterBullet
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