logo
EngineFlame

shooter · space-combat-sim

EngineFlame

1
Unlock

Install with the CLI:

bjs download EngineFlame

EngineFlame

Glowing fire out the back of a ship, as long as the throttle says it should be.

What it does

EngineFlameComponent is pure data: where the nozzles are in the ship's own frame, how big the fire is, what colour it burns. EngineFlameSystem mints two meshes per nozzle — a cone that tapers to a point, and a small bright ball parked in the nozzle mouth — and re-poses them every frame off Transform6DOF. The cone's length follows FlightIntent.throttle, stretches while boost is held, and wobbles on two sines that never line back up, so it reads as combustion rather than as a lit cone someone glued on.

Nozzles come one of two ways. The shorthand — spread / rise / back — mirrors one pair across the centreline, which is what a two-engine fighter wants and the only form scene JSON usually needs. Give nozzles explicitly for a one-engine interceptor or a four-engine hauler.

The flames are not parented to the hull. A ship flying a GLB is a clone posed through a per-asset nose-facing correction, and hanging children off it would mean inheriting that correction and then undoing it; instead each nozzle offset is rotated by the ship's own basis every frame, which is a handful of multiplies and lands the fire exactly where the hull's tail is.

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

    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": {},
      "Velocity6DOF": {},
      "FlightIntent": {},
      "ShipFlight": { "maxThrust": 80, "turnRate": 5 },
      "Renderable": { "shape": "spaceplane", "sx": 3, "sy": 1, "sz": 4 },
      "PlayerFlightInput": {},
      "EngineFlame": {}
    }
  }
}

Props

  • nozzles (array of [x, y, z], default: a mirrored pair from spread/rise/back) — nozzle positions in the ship's own frame. +X right, +Y up, +Z forward.
  • spread (number, default 0.6) — half the distance between the mirrored pair.
  • rise (number, default -0.1) — how far the nozzle plane sits above the centreline.
  • back (number, default -2.2) — how far behind the origin the nozzles sit. Negative, because the nose is +Z.
  • width (number, default 0.44) — plume diameter at the mouth.
  • length (number, default 3.2) — plume length at full throttle.
  • idle (number, default 0.28) — the fraction of length an idling engine still burns at. Never set it to 0: a flame that vanishes the moment you let off the stick reads as a bug, not as a throttle.
  • boostScale (number, default 1.85) — multiplier on the length while FlightIntent.boost is held.
  • r / g / b (numbers, default 0.22 / 0.85 / 2.2) — plume colour. Values above 1 are deliberate: this is an emissive surface, and letting the blue clip past white is what gives the plume a hot core with coloured edges.
  • coreR / coreG / coreB (numbers, default 0.8 / 1.4 / 2.6) — the ball in the nozzle mouth. Brighter and whiter than the plume.
  • flicker (number, default 0.12) — how far the length wanders, as a fraction. 0 is dead steady, which looks like plastic.
  • bloom (number, default 1) — multiplier on every emissive channel. The bloom knob. How far the result runs past 1 is exactly what the glow pass keys off, so this is the difference between a flame with a halo and a blue cone with hard edges. Push it to 2–3 for an afterburner; drop it below 1 for an engine that reads as idling.
  • glow (boolean, default true) — opt the flames into the selective glow post-pass, when the scene has one.

Events

Emits nothing and listens for nothing. Engine fire is read straight off FlightIntent every frame; there is nothing about the length of a plume that another component needs to be told.

Dependencies

  • Transform6DOF — required. Its position and quaternion are what carry the fire around with the ship.
  • FlightIntent — optional, and the thing that makes this interesting. Without one, every engine burns flat out.
  • Renderable — optional. When present, the flames follow its visible flag.

Tuning it live

Add EngineFlamePanel: {} to the world entity and press E in a dev build. Sliders for bloom, length, width, idle burn, boost and flicker, a glow on/off toggle for an honest A/B on what the halo is buying, and three presets. Every slider writes to every flamed ship in the scene, not just the one you are flying, so a pooled wing of enemies changes with you. Values persist through vizStore, so a setting survives the reload you will do straight after finding it.

Notes

  • A dead ship stops burning. destroyShip communicates a player death by hiding the hull's model rather than by removing the entity, so the flames watch Renderable.visible. Drop the Renderable and the engines will keep burning at the spot the ship blew up in, through the whole respawn linger.
  • A pooled ship that dies is parked, not destroyed, and keeps its flame meshes for the respawn. That is the point of the pool; rebuilding four meshes per death would be exactly the churn it exists to avoid.
  • The defaults are sized for a fighter about five units nose to tail. Scale spread / back / length together for anything much bigger, or the fire ends up inside the hull.
  • Twelve-sided cones on purpose. This is a glowing blur seen nearly end-on from a chase camera, and the facets a hundred sides would smooth away are the ones the glow eats anyway.
  • The halo needs a glow layer, and something else in the scene has to build it. SpaceDust does, on its own first update. That is also why the flames enrol in the whitelist from onUpdate and never at mint: addGlowMesh returns silently when there is no layer, so enrolling during scene load put them nowhere at all and logged nothing. With no SpaceDust (or glowIntensity: 0) bloom still brightens the cone, but nothing bleeds past its edges.

More like this

Transform6DOF
AiPilot
Asteroid
AsteroidField
Bullet
CameraState6DOF
ChaseCamera6DOF
ChaseCameraTarget
EnemySpawner
FlightIntent
FreighterCar
FreighterChain
FreighterHead
GameConfig
Health
LensFlare
Lifetime
LineOfSight
MachineGun
Missile
MissileLauncher
MissionDirector
MoneyField
NebulaSky
Obstacle
ObstacleField
OilBlob
Parts
PlayerFlightInput
PlayerWalkAnimator
Renderable
RespawnTimer
ShipDamageFX
ShipFlight
ShipLoadout
ShooterCamera
SkeletonAnimator
SpaceDust
SpaceShooterBullet
SpaceShooterFX
SpaceShooterHUD
SpaceShooterHealth
SpaceShooterScore
TwinStickEnemy
TwinStickShooter
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