logo

Babylon.js Market

AsteroidField

world · space-combat-sim

AsteroidField

2
Unlock

Install with the CLI:

bjs download AsteroidField

AsteroidField

Lays out a whole hazard belt from one line of scene JSON.

What it does

AsteroidField: { count: 110 } on the world entity replaces hand-placing boulders. The System proposes points on a flattened spherical shell, rejects any that would overlap a rock already placed or intrude on a spawn, and spawns what's left as ordinary entities carrying Transform6DOF, Velocity6DOF, Renderable and the hazard component. kind: 'oil-blob' builds the oil variant from identical placement, because the two fields differ only in what the rocks are.

Four decisions carry the look:

  • Radius is power-distributed (min + u²·range). A uniform roll gives a field of interchangeable medium rocks; squaring it makes most small and a few enormous, which is what reads as a population of distinct masses.
  • The shell is flattened vertically (verticalSquash, 0.55). Without it the field reads as a ball of rocks you're inside; with it, as a belt.
  • Rocks never touch. Every placement keeps a gap of free space between surfaces, so the field is navigable and reads as separate boulders rather than one merged blob.
  • The shell grows with count by the cube root, holding spatial density constant. Ask for ten times the rocks and the field spreads out instead of packing ten times tighter until the sampler starves.

count is an attempt budget, not a promise. A slot with nowhere legal to go after maxAttempts tries is dropped, because forcing it would mean intersecting rock. At the default belt — rocks up to 360 across in a 520-thick shell — expect to lose some.

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

    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": {
      "AsteroidField": { "count": 110, "seed": 1337 }
    }
  },
  "OilField": {
    "components": {
      "AsteroidField": {
        "kind": "oil-blob",
        "count": 10,
        "seed": 2027,
        "minRadius": 35,
        "maxRadius": 60,
        "shellInner": 360
      }
    }
  }
}

Two fields means two entities — one component instance per type per entity.

Props

  • count (number, default 36) — rocks to attempt.
  • seed (number, default 1337) — fixes the field. The same seed always rebuilds the same rocks.
  • kind ('asteroid' | 'oil-blob', default 'asteroid').
  • shellInner (number, default 280) — inner radius of the shell.
  • shellRange (number, default 520) — shell thickness at densityCount rocks.
  • densityCount (number, default 36) — the count shellRange is calibrated for.
  • minRadius / maxRadius (number, defaults 40 / 360).
  • gap (number, default 35) — free space kept between two rock surfaces.
  • verticalSquash (number, default 0.55) — 1 is a ball, 0.55 a belt.
  • driftSpeed (number, default 1.6) — peak horizontal drift, units/sec.
  • spinSpeed (number, default 0.08) — peak spin, rad/sec.
  • exclusions (array, default []) — { x, y, z, r } spheres the field must avoid.
  • avoidTags (string[], default ["player"]) — tags whose entities get an exclusion zone wherever they actually are.
  • avoidRadius (number, default 300) — radius of that zone.
  • maxAttempts (number, default 50) — tries per rock before the slot is abandoned.

Events

None either way.

Dependencies

Transform6DOF, Velocity6DOF, Renderable, and Asteroid or OilBlob depending on kind.

Notes

  • Collision extents come from the same function that builds the geometry (asteroidStretch). Derive them any other way and the box you crash into drifts from the shape you can see — bullets pass through visible rock — and segmentClearOfAsteroids reads that same box for lock-through-cover gating, so a wrong box breaks target locks too.
  • avoidTags beats a hand-written exclusion. It puts the hole wherever the scene actually placed the player, so moving the player start can't silently bury them.
  • Placement uses a spatial hash rather than scanning every rock placed so far, so a thousand rocks lay out in milliseconds instead of going quadratic.
  • The field is built once, on the frame the component first appears. Rocks are ordinary entities, not pooled ones — a pool is for things that spawn and die during play, and these are scenery that outlives the round.
  • Layout is deterministic, so a scene reset rebuilds the same field and a screenshot pipeline can take the same picture twice.
  • acos(2u−1) for the polar angle, not a uniform roll, or the rocks bunch at the poles.

More like this

Asteroid
OilBlob
Renderable
Transform6DOF
Velocity6DOF
CameraState6DOF
ChaseCamera6DOF
ChaseCameraTarget
FlightIntent
FreighterCar
FreighterChain
FreighterHead
GameConfig
LensFlare
NebulaSky
PlayerFlightInput
ShipDamageFX
ShipFlight
SpaceDust
SpaceShooterBullet
SpaceShooterFX
SpaceShooterHUD
SpaceShooterHealth
SpaceShooterScore
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