Skip to content

3D Spatial Queries

Olivier Biot edited this page Jun 17, 2026 · 1 revision

Part of Working in 3D.

For "what entities are near me / in front of me / on the camera frustum", melonJS exposes a 3D spatial-query surface through the physics adapter:

const candidates = world.adapter.querySphere?.(sphere) ?? [];

Sphere

new Sphere(x, y, z, radius) is the geometry primitive for sphere queries and arcade 3D collision:

import { Sphere } from "melonjs";

const probe = new Sphere(0, 0, 0, 30);
probe.contains(p);              // point-in-sphere
probe.overlaps(other);          // sphere-vs-sphere
probe.overlapsAABB(aabb);       // sphere-vs-AABB
probe.setShape(x, y, z, r);     // reposition + resize in place (no alloc)

world.adapter.querySphere(...)

Returns every renderable within the given sphere. Two call shapes — pick whichever reads better at the call site:

// Packaged form — sphere maintained alongside the entity
this.collider.pos.set(this.pos.x, this.pos.y, this.depth);
const nearby = world.adapter.querySphere?.(this.collider) ?? [];

// Loose form — one-off query
const nearby = world.adapter.querySphere?.(centerVec3, HIT_RADIUS) ?? [];

Use it as a broadphase. The result is every renderable whose centre falls inside the sphere — your code does the per-entity narrow phase (kind filter, exact bounding-sphere test, etc.). On 2D adapters (matter, planck) the optional method is absent, so the ?? [] fallback runs.

Gotcha: Sprite defaults isKinematic = true, which means the broadphase skips it. Flip to false at spawn for any entity you want findable via querySphere:

bullet.isKinematic = false;

world.adapter.raycast3d?(from, to)

3D ray cast. Returns the nearest RaycastHit3d (with renderable, point: Vector3d, normal: Vector3d, fraction) or null. Each renderable is treated as a bounding sphere using the circumradius of its 2D bounds.

const hit = world.adapter.raycast3d?.(eye.pos, target.pos);
if (hit) {
  console.log("hit", hit.renderable, "at fraction", hit.fraction);
}

Capability-gated by adapter.capabilities.raycasts3d. Builtin adapter supports it under Camera3d; matter/planck don't (raycasts3d: false).

Camera3d.queryVisible(world)

Bulk frustum cull — returns every renderable whose octant overlaps the current view frustum. Use as a broadphase pass before per-renderable narrow culling on dense 3D scenes (hundreds+ of items):

const visible = camera.queryVisible(app.world);
for (const r of visible) {
  if (camera.isVisible(r)) {
    // draw / process
  }
}

Returns [] under a 2D camera — safe to call unconditionally.

Supporting primitives

Two lower-level types back the queries above; you rarely construct them directly, but they're exported for code that needs to reason about 3D regions:

  • AABB3d — minimal 3D axis-aligned bounding box (min, max, contains, overlaps, overlapsSphere, isFinite, …). The Octree's bounding primitive; pass one to world.adapter.queryAABB for a 3D region query.
  • Frustum — the perspective view volume (fov / aspect / near / far + projection matrix) that Camera3d builds internally and queryVisible tests octants against. Exposed on the camera as camera.frustum.

Clone this wiki locally