logo
ShipDamageFX

fx · space-combat-sim

ShipDamageFX

2
Unlock

Install with the CLI:

bjs download ShipDamageFX

ShipDamageFX

Ships that come apart into their own hull, in pieces, tumbling.

What it does

Hits and part losses have always been events; nothing drew them. ShipDamageFX: {} on the world entity gives a dogfight the two marks that make it read.

EventEffect
spaceshooter.part.destroyedthat part's chunks detach and tumble as wreckage
bullet.hita scorched puncture at the impact point on the named part
spaceshooter.player.blownapartthe player's primitives are cloned into wreckage

Wreckage inherits the dying ship's velocity, plus a random separation kick, a slight upward bias so pieces clear the hull, and per-piece tumble. Fling debris off at its own speed instead and it separates unnaturally, like a decal peeling away; a piece that keeps its orientation reads as a prop rather than as debris.

Shed pieces come off hot and cool as they fly. This is mostly about being seen. A chunk of hull is a small unlit dark object against black, and at any distance it is a couple of pixels almost exactly the colour of the background — the wreckage is right there and nobody can find it. Hot metal is bright, it is a colour nothing else in space is, and a bloom pass turns two pixels into something the eye follows. Each piece gets its own material clone (share one and a shed wing lights up the ship it came off, and every other ship using that art), is enrolled in the scene's selective glow pass, cools on a squared curve so it dumps most of its heat in the first moment, and fades out over the last fifth of its life rather than blinking away.

How big the pieces are is decided at the cut, not at the kill. An imported hull is dealt out into the part boxes the game already tests rounds against, and then each of those is cut again — debrisChunks times. One piece per part is the right size for a wing shot off a ship that flies on; it is the wrong size for a kill, where five slabs as long as the ship read as the model falling over rather than as the ship being destroyed. The price is a mesh per chunk on every live hull, so this is the knob to turn down if ships are cheap and numerous.

Two routing decisions are worth knowing:

  • A hit reported on part 'debris' goes to the nearest piece of wreckage, not to a hull. Debris has no entity mesh, and marking the ship it came off would put the crater where nothing was hit.
  • Asteroids and oil blobs are skipped. A rock is not a hull — shooting one throws off chips, and a scorched puncture in stone looks wrong.

Setup

The engine work ships with the component. Register it once at bootstrap, before the scene loads:

TypeScript
import { registerShipDamageFXExtension } from '@babylonjsmarket/…/ShipDamageFX/adapter/register';

const adapter = new BabylonAdapter();
registerShipDamageFXExtension(adapter);
const game = new ArcadeGame(adapter, { … });

Without it, damage leaves no marks and the System warns once.

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.

Fastest path

bjs download scene ShipDamageFX

Pulls this exact scene into your project and runs it — no copy-paste. Add --all to grab the components and assets it needs too.

Or 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 ShipDamageFX
    bjs download ShipDamageFX

    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
{
  "World": {
    "tags": ["world"],
    "components": {
      "ShipDamageFX": { "debrisChunks": 3, "spread": 12, "tumble": 8 }
    }
  }
}

Props

  • debrisChunks (number, default 3) — pieces per part. 1 gives whole-part wreckage.
  • spread (number, default 12) — peak random separation kick for shed pieces, units/sec.
  • lift (number, default 1.5) — upward bias, units/sec.
  • tumble (number, default 8) — peak spin, rad/sec.
  • debrisLifetimeMin / debrisLifetimeMax (number, defaults 5 / 7) — seconds before a piece is disposed.
  • holeDiameter (number, default 0.22) — base crater size; each varies up to half again.
  • maxHolesPerPart (number, default 12) — craters kept per part before the oldest is recycled.
  • seed (number, default 20260729) — fixes wreck trajectories.
  • holes (boolean, default false) — draw craters at all. See the note below before turning it on.
  • shedding (boolean, default true) — shed parts at all.
  • segmentParts (boolean, default true) — cut an imported hull into its parts at load.
  • segmentMaxTriangles (number, default 200000) — refuse to cut a hull heavier than this.
  • partTint (boolean, default true) — pulse damaged and destroyed parts on the hull.
  • debrisGlow (boolean, default true) — light shed pieces up as freshly-torn metal.
  • debrisGlowIntensity (number, default 2.6) — peak emissive at the tear. Above 1 so a bloom pass catches it.
  • debrisCoolSeconds (number, default 3.5) — seconds from white-hot to cold.

Events

  • listens for spaceshooter.part.destroyed, bullet.hit, spaceshooter.player.blownapart.
  • emits nothing.

Dependencies

Parts (so a hit's part name resolves to mesh suffixes) and Velocity6DOF (so wreckage inherits motion). Neither is required for the component to run — a ship with no part breakdown gets craters anywhere on its hull.

Notes

  • Craters are off by default, and that is a range decision. A decal is a real puncture in a hull you can walk up to, and from a dogfight it is sub-pixel: every round pays for a projection, a mesh and a draw call to render something nobody can see. Turn holes on for a cockpit view, a kill-cam or a close-up render, where the thing it draws is big enough to be worth the projection.
  • One shed piece per part is ECS-driven; the rest are spectacle. A wreck entity is a single collision proxy, so binding a part's whole burst of chunks to it poses them all at the same point — the wing arrives as one chunk with copies of itself hidden inside. So the caller says how many pieces it can actually drive, and the plugin tumbles the others itself. Claiming more than that is how a killed ship's wreckage ends up marked ECS-driven with no ECS behind it: motionless in the air, and never cleared up.
  • The player is cloned, not stripped. The player survives to respawn, so its own primitives must stay where they are; taking them is how a player ends up permanently invisible after their first death. This is also why the kill path deliberately skips the part-detach loop for the player and emits blownapart instead.
  • On a GLB ship the primitives are hidden — the model is what you see, and the primitives are the collision proxy. A shed piece has to be revealed or the wing comes off invisibly.
  • Detaching preserves world transform, so a part breaks off in place rather than snapping to the world origin.
  • Craters are capped per part, or a long firefight mints them without limit.
  • Wreckage is advanced from the ECS update, not the render loop, so the debris clock freezes with a paused world. Debris drifting on under a pause menu is a bug — which is why this System is pauseable, unlike the HUD and the explosion FX.
  • Trajectories are seeded and offset per event, so two kills in a run differ while the run itself reproduces exactly.

More like this

Asteroid
Bullet
OilBlob
Parts
SpaceShooterBullet
SpaceShooterHealth
Velocity6DOF
AsteroidField
CameraState6DOF
ChaseCamera6DOF
ChaseCameraTarget
DirectionalLight
EngineFlame
EnvironmentTexture
Flash
FlightIntent
FreighterCar
FreighterChain
FreighterHead
GameConfig
HemisphericLight
LensFlare
Mesh
MeshPrimitive
NebulaSky
PlayerFlightInput
Renderable
RoadTreadmill
Shadow
ShipFlight
Skybox
SmokeScreen
SpaceDust
SpaceShooterFX
SpaceShooterHUD
SpaceShooterScore
ToneMapping
Transform6DOF
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