logo

Babylon.js Market

MachineGun

shooter

MachineGun

1
Unlock

Install with the CLI:

bjs download MachineGun

MachineGun

A nose-mounted forward gun for a 6-DOF shooter — flip isFiring and it streams pooled tracer rounds off the ship's nose, each one inheriting the ship's own velocity.

What it does

MachineGunComponent is pure data: the tuning (fireRate, bulletSpeed, bulletDamage, range, spread) plus two live-state numbers, isFiring and cooldown. The player and the AI share the exact same component — the only difference is who writes isFiring. MachineGunSystem (priority 77, query MachineGun + Transform6DOF + Velocity6DOF, excluding ships tagged dead) turns that flag into shots. Each frame it counts the gun's cooldown down by dt; if the gun isFiring and cooldown has hit zero, it fires. Firing reads the ship's forward vector from its orientation quaternion, nudges it by a random ±spread tilt on the local x/y and renormalizes, then spawns a round 2.5u ahead of the nose whose velocity is shipVelocity + forward * bulletSpeed — so the bullet keeps up with a moving ship instead of being left behind. It then sets cooldown = 1 / fireRate and emits MachineGunEvents.FIRED. range is engagement distance for AI targeting; the System itself never reads it.

Bullets are a pre-allocated World pool, not per-shot createEntity. On init the System registers a 256-slot bullet pool; each slot is a Bullet + Transform6DOF + Velocity6DOF + Renderable(tracer) + Lifetime entity tagged bullet, built once and recycled. Firing calls world.acquire('bullet', …) to re-seed a slot's pose, velocity, owner, and damage; a 9-shots/sec stream never churns the GC. Each round carries a 15s Lifetime (≈ 7200u of range at speed 480), and LifetimeSystem releases it back to the pool when the clock expires. If every slot is in flight, acquire returns null and the shot is skipped — cooldown stays at zero, so the gun retries next frame.

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

    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", "ship"],
    "components": {
      "Transform6DOF": { "x": 0, "y": 0, "z": 0 },
      "Velocity6DOF": { "vz": 60 },
      "MachineGun": {
        "fireRate": 9,
        "bulletSpeed": 480,
        "bulletDamage": 1,
        "spread": 0.012,
        "isFiring": true
      },
      "Renderable": { "shape": "sphere", "sx": 2, "sy": 2, "sz": 2, "r": 0.7, "g": 0.7, "b": 0.75 }
    }
  }
}

The gun-bearing entity needs Transform6DOF + Velocity6DOF (the query) and a Renderable to draw the ship itself. Bullet, Renderable, and Lifetime are carried by the pooled bullet slots the System builds — you don't put them on the ship. isFiring: true holds the trigger down; in a real game player input or an AI system toggles it.

Props

  • fireRate (number, default 9) — shots per second. On each shot the System sets cooldown = 1 / fireRate, so this caps the stream rate.
  • bulletSpeed (number, default 480) — muzzle speed (u/s) added on top of the ship's velocity along the firing direction, not an absolute bullet speed.
  • bulletDamage (number, default 1) — per-round damage, copied onto the spawned SpaceShooterBullet round.
  • range (number, default 800) — AI engagement range only. MachineGunSystem never reads it; it exists for whatever AI decides when to set isFiring.
  • spread (number, default 0.012) — random angular dispersion in radians. Each shot tilts the forward vector by ±spread on local x and y before renormalizing; 0 is pinpoint.
  • isFiring (boolean, default false) — the trigger. While true and cooldown is 0, the System spawns a round on each eligible frame.
  • cooldown (number, default 0) — seconds until the next shot is allowed. Runtime state: decremented each frame, reset to 1 / fireRate on fire. Leave at 0.

Events

  • Emits wingman.gun.fired (MachineGunEvents.FIRED) on every round fired — payload { ownerId, bulletId }, the firing entity's id and the acquired bullet entity's id. This is the observer channel for HUD counters, muzzle-flash, and audio; the bullet's flight and damage are owned by the Bullet chain, not this event.
  • Listens for nothing. isFiring is read straight off the component each frame — drive the gun by writing that field (player input, AI targeting), never by emitting on the bus.

Dependencies

  • Transform6DOF — required by the query; its position places the muzzle and its quaternion q gives the forward vector the round is fired along.
  • Velocity6DOF — required by the query; the ship's linear velocity is inherited by every bullet (bulletVel = shipVel + forward * bulletSpeed).
  • Bullet — the projectile component each pool slot carries; acquire re-seeds its ownerEntityId + damage per shot. Bullet.ts integrates the round's flight and collisions.
  • Renderable — draws each pooled round as the bright emissive tracer sphere (orange, glow: true) built once per slot.
  • Lifetime — the 15s recycle clock on each round; LifetimeSystem releases the bullet back to the pool when remaining hits zero, closing the acquire/release loop.

Notes

  • isFiring is the only live knob you toggle. Everything else is tuning. Player and AI ships run the identical System — hold or release the trigger by writing this one boolean.
  • Dead ships don't shoot. The query excludes the dead tag, so tagging a ship dead silences its gun without removing the component.
  • Muzzle speed stacks on ship velocity. bulletSpeed is a relative add, so a fast ship's rounds travel faster in world space than a parked one's. Tune with the ship's cruise speed in mind.
  • Pool exhaustion drops shots silently. Past 256 concurrent bullets acquire returns null and the frame's shot is skipped — no FIRED, cooldown untouched, retried next frame. Raise the bullet Pool blueprint's size if a dense AI swarm starves the player.
  • cooldown is runtime state, but it serializes. It round-trips through serialize() with the tuning; seed it at 0 in scene JSON so the gun is ready to fire on load.

More like this

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