logo

Babylon.js Market

ObstacleField

shooter

ObstacleField

1
Unlock

Install with the CLI:

bjs download ObstacleField

ObstacleField

Scatters a seeded field of wall-and-cover barriers across a top-down arena, once, on the first frame.

What it does

ObstacleField is a singleton config plus a one-shot spawner. You drop one ObstacleField component on a marker entity (typically the scene's world entity); ObstacleFieldSystem queries for it and, on its first onUpdate, seeds a mulberry32 PRNG and runs generateObstacles to place count non-overlapping barriers. Each placement becomes its own entity carrying a box MeshPrimitive, an Obstacle (collision footprint), and a Shadow caster. Spawning is deferred to the first frame so the MeshPrimitive and Shadow systems are already up and pick the new entities via onEntityAdded — the same lifecycle the enemy spawners rely on. It only runs once; later updates are a no-op.

Barriers come in two kinds, split by fullRatio: full-height walls (fullHeight) that block movement, bullets, and line of sight, and low cover (halfHeight) that blocks movement only (shoot over, see over). The placer rejects any candidate that pokes outside the placement square, lands in the centre spawn circle (clearCenterRadius), falls inside a caller-supplied keep-out circle (avoid), or overlaps an already-placed barrier within spacing. Same seed, same layout; seed: 0 reseeds from the clock for a fresh field each run.

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

    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
{
  "Field": {
    "components": {
      "ObstacleField": {
        "count": 10,
        "seed": 4242,
        "fullRatio": 0.5,
        "placementHalf": 16,
        "clearCenterRadius": 4,
        "avoid": [[-15, 15, 3], [15, 15, 3], [-15, -15, 3], [15, -15, 3]],
        "fullColor": [0.32, 0.2, 0.42],
        "halfColor": [0.5, 0.38, 0.22]
      }
    }
  }
}

Props

  • count (number, default 8) — how many barriers to scatter; the placer stops early if it can't fit them all after count * 30 attempts.
  • seed (number, default 0) — PRNG seed for a reproducible layout; 0 reseeds from Date.now() for a fresh field every run.
  • fullRatio (number, default 0.5) — fraction of barriers that are full-height sight/bullet blockers; the rest are low cover.
  • placementHalf (number, default 16) — half-extent of the square placement region; keep it inside your arena walls.
  • clearCenterRadius (number, default 4) — radius of the clear circle around the origin, so the player doesn't spawn boxed in.
  • avoid (Array<[x, z, radius]>, default []) — keep-out circles, e.g. the corner enemy-spawner pads.
  • spacing (number, default 2.5) — minimum gap between barriers so agents can pass between them.
  • fullHeight (number, default 3) — box height for full-height walls.
  • halfHeight (number, default 1) — box height for low cover.
  • thickness (number, default 1) — the barrier's short-axis width.
  • minLength / maxLength (numbers, defaults 3 / 7) — long-axis length range; each barrier's long axis is picked at random between them.
  • fullColor ([r,g,b], default [0.32, 0.2, 0.42]) — diffuse tint for full-height walls.
  • halfColor ([r,g,b], default [0.5, 0.38, 0.22]) — diffuse tint for low cover.

Events

None. ObstacleField touches the EventBus neither to emit nor to listen — it spawns entities directly through the world.

Dependencies

MeshPrimitive, Obstacle, and Shadow — each spawned barrier is composed of all three, so their systems must be registered in the scene for the field to render, collide, and cast shadows. Obstacle has no system of its own (it's collision data read by gameplay systems), but MeshPrimitive and Shadow do, so include at least one MeshPrimitive (a ground works) and a Shadow/DirectionalLight in the scene to register them.

Notes

  • The one-shot spawned flag lives on the System, not the component, so it survives the config entity but resets if you build a fresh System.
  • Each barrier's Obstacle gets blocksSight / blocksBullets set to its full flag, so full walls block sight and bullets while low cover blocks neither — movement is always blocked regardless.
  • The spawned box sits on the ground: its position.y is height / 2, and the Obstacle half-extents match the mesh footprint (width/2, depth/2).
  • Every barrier is tagged obstacle, so world.getEntitiesByTag('obstacle') collects the whole field.
  • The config is a singleton — the System uses the first matching entity; extra ObstacleField components in the same scene are ignored.

More like this

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