logo
ConicPath

gameplay · physics-puzzle

ConicPath

1
Unlock

Install with the CLI:

bjs download ConicPath

ConicPath

The exact orbit of a MassDriver's next shot round one mass, drawn dashed from the equations instead of stepped.

What it does

Every frame ConicPath takes the shot the driver on its entity is about to fire: the muzzle position, the launch velocity, and where the named mass is now. From those it works out the orbit. With one mass pulling, every path is a conic: a circle, an ellipse, a parabola or a hyperbola. One formula draws all four:

r(ν) = p / (1 + e·cos ν)

Here ν is the angle round from the nearest point, e is the eccentricity, and p is the semi-latus rectum: the distance from the centre to the orbit a quarter turn round from the nearest point. The code works in p and never in the semi-major axis a. At exactly escape speed the energy is 0, and a = −μ/2ε divides by zero. p = h²/μ stays finite there, so the parabola between the ellipses and the hyperbolas needs no special case.

The dashes run forward from the muzzle along the direction of motion. They stop where the shot comes down on the mass (its radius plus the ball's bodyRadius), or after one lap, or at maxDistance for a shot that never comes back. A bound orbit that swings out past maxDistance is drawn as two arms, going out and coming back.

Two overlays show where simpler models stop working:

  • galileo adds Galileo's parabola from the same muzzle. It holds gravity at its muzzle strength and "down" at the muzzle's direction, and lands on flat ground that touches the round ground under the muzzle. Close in, it lies on the orbit. Its error shows where it comes down.
  • ticks puts a short bar across the orbit at equal time steps over one lap. Each tick comes from Kepler's equation, M = E − e·sin E, solved by Newton's method until a step is smaller than a tolerance. Near the mass the ball is fast and the ticks spread out. Far away it is slow and they bunch up.

Nothing else pulls in this picture. Beside a second mass the drawn orbit and the real flight part, which is the three-body problem made visible.

When the orbit's kind or any of its numbers change, the System emits conicpath.changed. The numbers are rounded to 4 decimals, and that is also the smallest change it announces. It carries where the shot comes down and where Galileo's parabola does, so a difference of a few pixels on screen still reads as a number in the EventBus panel.

Setup

The engine work ships with the component, in adapter/register.ts. A project made with bjs create installs it for you: src/main.ts calls every adapter/register.ts that bjs download puts in the project, before the scene loads. If you boot the game from your own code, call it once, right after building the adapter:

TypeScript
import { registerConicPathExtension } from './Components/ConicPath/adapter/register';

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

The plugin draws the lines thick and sets them in depth: the orbit sits behind an aim line drawn in the same place, and Galileo and the ticks sit in front of both. Without the plugin the System draws the same lines one pixel wide with the engine's plain line system, and warns once. The plugin is for BabylonAdapter only.

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

    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
{
  "Cannon": {
    "components": {
      "MeshPrimitive": { "primitive": "cylinder", "height": 1.6, "position": [-0.8, 0, 11] },
      "MassDriver": { "pool": "rock", "speed": 12, "direction": [1, 0, 0], "muzzleOffset": 0.8, "spread": 0 },
      "ConicPath": { "around": "Planet", "galileo": true, "ticks": 12 }
    }
  },
  "Planet": {
    "components": {
      "MeshPrimitive": { "primitive": "sphere", "diameter": 20 },
      "GravityMass": { "mass": 1100, "radius": 10, "softening": 0 }
    }
  }
}

With this planet a sideways shot from 11 units out circles at 10, swings out to 28.29 at 12, and escapes at 14.14.

Props

  • around (string, default "Planet"): id of the entity to orbit, its key in the scene. It needs a GravityMass and a MeshPrimitive.
  • bodyRadius (number, default 0.3): the launched ball's radius. Match its GravityBody, or the drawn landing and the real one differ.
  • maxDistance (number, default 100): nothing is drawn farther than this from the mass's centre.
  • segments (number, default 240): points the orbit is sampled at.
  • dashes (number, default 90): the most dashes the orbit is cut into. A short path gets fewer, none shorter than six line widths.
  • dashFill (number, default 0.45): share of each dash slot that is drawn, kept between 0.05 and 1.
  • galileo (boolean, default false): draw Galileo's flat-ground parabola too.
  • ticks (number, default 0): bars across the orbit at this many equal time steps per lap.
  • tickLength (number, default 1.4): length of a tick bar, in world units.
  • color ([r, g, b], default [1, 0.78, 0.25]): the orbit.
  • galileoColor ([r, g, b], default [0.92, 0.95, 1]): Galileo's parabola.
  • tickColor ([r, g, b], default [1, 0.5, 0.12]): the ticks.
  • width (number, default 0.2): line width in world units, read by the plugin.
  • visible (boolean, default true): draw the lines. The event fires either way.

Events

Emits:

  • conicpath.changed: { entityId, aroundId, kind, eccentricity, semiLatusRectum, energy, period, periapsis, apoapsis, landAngle, landX, galileoX }. kind is circle, ellipse, parabola, hyperbola or radial (a launch straight up or down). period and apoapsis are 0 for a shot that never comes back. landAngle is the degrees round the mass to where the shot comes down. landX and galileoX are where the orbit and Galileo's parabola come down, measured sideways from the muzzle. All three are 0 when the shot never comes down, and galileoX is 0 with galileo off. It fires on the first frame, when a number changes in its 4th decimal, and when a pooled driver comes back into play.

Listens to nothing.

Dependencies

  • MassDriver: required on the same entity, for the muzzle, direction and speed.
  • MeshPrimitive: the driver's position and the mass's.
  • GravityMass: the mass's mass (G = 1) and radius.

Notes

  • The orbit is the centre of the launch cone. With MassDriver spread above 0, real balls scatter round it.
  • Softening is ignored on purpose. A softened mass pulls less than mass/r² close in, so its real orbits turn a little every lap while the drawn ellipse stays put.
  • The game's integrator stores velocity half a step behind, so a real shot comes down a little short of the drawn landing: under 0.1° for most shots, 2.1° for one that grazes the ground.
  • A mass of 0 or less, or a launch from the mass's centre, draws nothing. A launch from inside the ground draws nothing and reports a landing at the muzzle.
  • Parking the driver hides its lines until it is handed out again; destroying it, or taking the ConicPath off, disposes them.

More like this

GravityMass
MassDriver
MeshPrimitive
AxisViewCamera
BallPossession
BallReset
BilliardBall
BilliardRack
BilliardTable
BlackHole
BoatMode
BoatWater
Bumper
CaptureZone
CardFan
ContactDamage
ContactImpulse
Enemy
Goal
GravityBody
GravityLens
HealthBar
MotionTrail
MouseHole
Obstacle
Physics
PointerDrag
PokerHUD
PokerTableCards
RespawnTimer
RoundReset
Score
Scoreboard
SoccerDirector
SpaceGolfDirector
SpaceGolfHUD
SpaceRock
SpaceShooterHUD
SpaceShooterScore
Spin
Spinner
SurfacePatch
TrajectoryPreview
Velocity6DOF
Waypoint
WhiteHole
Wormhole

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search