b3d-turret

An auto-tracking gun — it slews its barrel to lead a moving target and elevate for gravity drop (how much of each is governed by smart; drop uses ballisticAim from ballistics.ts), turns within a traverseRate budget (steerToward from guidance.ts), and fires warhead shells (spawnProjectile) once the target is in range and it can bear. The barrel glows its armedColor the instant it has a firing solution — so you can watch it acquire, lead, and open up.

Demo

A drone orbits the turret; the turret tracks it, leads the crossing motion, and fires when aligned (barrel glows red when it can bear). Shots arc in and blast the drone, which respawns at a fresh altitude each time. It's fully automatic — no controls; just watch it acquire, engage, and correct. The smart (0..1) slider is a skill curve: at 0 it aims straight at where the target is now — so it whiffs the crossing motion and, at the default low muzzle speed, falls short. Leading ramps in first — full by 0.5 — then drop compensation finishes ramping to 1, elevating the barrel for gravity. So low-smart turrets lead but shoot flat; high-smart ones lead and arc their shots onto target. (0.5–1 is reserved to fold in target acceleration/turn-rate prediction later.) Drop the traverse rate to watch it struggle to keep up even when smart.

import { b3d, b3dTurret, b3dDestroyable, b3dLight, b3dSkybox, b3dGround, label3d, slider3d } from 'tosijs-3d'
import { orbitCam } from 'demo-utils'
import { tosi } from 'tosijs'

const { s } = tosi({ s: { traverseRate: 2.5, range: 30, fireRate: 2, muzzleSpeed: 24, smart: 0 } })
const turret = b3dTurret({ x: 0, y: 0, z: 0, traverseRate: s.traverseRate, range: s.range, fireRate: s.fireRate, muzzleSpeed: s.muzzleSpeed, smart: s.smart })

const scene = b3d(
  {
    scenePanelOpen: true,
    scenePanel: () => [
      label3d({ text: 'Turret', bold: true }),
      slider3d({ label: 'traverse rate', value: s.traverseRate, min: 0.3, max: 6, step: 0.1 }),
      slider3d({ label: 'range', value: s.range, min: 8, max: 40, step: 1 }),
      slider3d({ label: 'fire rate', value: s.fireRate, min: 0.5, max: 8, step: 0.5 }),
      slider3d({ label: 'muzzle speed', value: s.muzzleSpeed, min: 15, max: 60, step: 1 }),
      slider3d({ label: 'smart (drop comp)', value: s.smart, min: 0, max: 1, step: 0.05 }),
    ],
    sceneCreated(el, BABYLON) {
      orbitCam(el, { alpha: -Math.PI / 2.2, beta: Math.PI / 3.2, radius: 34, target: [0, 3, 0] })
      let a = 0, target = null, baseY = 4
      const spawn = () => {
        baseY = 3 + Math.random() * 8 // ~3–11m, within the turret's reach
        const t = b3dDestroyable({ meshName: 'drone', x: 12, y: baseY, z: 0, size: 1.3, capacity: 24, color: '#3388dd', explode: 'on' })
        el.appendChild(t)
        return t
      }
      target = spawn()
      el.addEventListener('destroyed', () => {
        const dead = target; target = null
        if (dead) dead.remove()
        setTimeout(() => { target = spawn() }, 500)
      })
      el.scene.onBeforeRenderObservable.add(() => {
        turret.traverseRate = s.traverseRate.value
        turret.range = s.range.value
        turret.fireRate = s.fireRate.value
        turret.muzzleSpeed = s.muzzleSpeed.value
        turret.smart = s.smart.value
        a += el.scene.getEngine().getDeltaTime() / 1000
        if (!target || target.dead || !target.mesh) return
        target.x = Math.cos(a * 0.6) * 12
        target.z = Math.sin(a * 0.6) * 12
        target.y = baseY + Math.sin(a * 1.3) * 1.2
        turret.track(target.mesh)
      })
    },
  },
  b3dLight({ y: 1, intensity: 0.85 }),
  b3dSkybox({ timeOfDay: 10 }),
  b3dGround({ width: 60, height: 60, color: '#5a6b52' }),
  turret,
)
preview.append(scene)
tosi-b3d { width: 100%; height: 100%; }

Attributes

Attribute Default Description
muzzleSpeed 35 Shell launch speed (also the lead-solver's projectile speed)
fireRate 2 Max shots per second
range 30 Won't fire beyond this distance
traverseRate 2.5 Max barrel slew rate (rad/sec)
smart 0 Skill curve 0..1: lead ramps to full by 0.5, drop compensation to full by 1 (0 = aim at the target's current spot). 0.5–1 reserved for acceleration/turn-rate prediction later
aimTolerance 6 Fires only when the barrel is within this many degrees of the solution
gravity / drag / mass -9.81 / 0.01 / 1 Shell ballistics (see b3d-launcher)
damage / fullRadius / blastRadius / los 20 / 1 / 2.5 / 'on' Warhead payload (see b3d-warhead)
idleColor '#4a5560' Barrel colour with no firing solution
armedColor '#e04030' Barrel colour when it can bear (has a solution, in range)
x,y,z 0 Turret base position