Skip to content

v19.5.0

Choose a tag to compare

@obiot obiot released this 22 May 00:03
· 36 commits to master since this release
290ba5e

What's New in melonJS 19.5.0

A physics-focused release. The headline is the new PhysicsAdapter abstraction — the same game code can now run on either the engine's built-in SAT physics (default, unchanged behavior) or on a third-party rigid-body engine via the physic Application setting. Two official adapters ship alongside the engine: @melonjs/matter-adapter (wraps matter-js) and @melonjs/planck-adapter (faithful Box2D 2.3.0 port via planck.js). Collision dispatch gains a proper lifecycle with a receiver-symmetric contract, spatial queries (raycast / queryAABB) become portable across every adapter, and a new procedural audio surface lets you fire envelope-shaped tones and noise without sample assets.

New Features

  • PhysicsAdapter interface — pluggable rigid-body abstraction. The new BuiltinAdapter wraps the existing SAT detector + Body class as the default; third-party adapters plug in by passing an instance to Application's physic setting (new Application(w, h, { physic: new MatterAdapter() })). The contract covers body lifecycle, kinematics, collision filtering, queries, and a capabilities flag set so portable code can branch on what the active engine supports.
  • @melonjs/matter-adapter — first official third-party adapter, packaged separately. Wraps matter-js's Engine and translates the PhysicsAdapter contract to matter's API. Constraints (springs/hinges/pins), sleeping bodies, continuous collision detection, raycasts, native rotational dynamics. Reach the engine via adapter.engine for matter-specific extensions.
  • @melonjs/planck-adapter — second official adapter. Faithful Box2D 2.3.0 port via planck.js. Native joints, CCD bullet flag, sleeping bodies, native raycasts, per-body gravity scale. Reach the engine via adapter.world.createJoint(...). Showcased by the new Neon Plinko example.
  • Portable spatial queriesadapter.raycast(from, to) and adapter.queryAABB(rect) are now portable across every adapter with the same return shapes (RaycastHit = { renderable, point, normal, fraction }, queryAABB → Renderable[]). raycast computes precise entry-point geometry per shape (line-segment vs polygon edges, quadratic ray vs ellipse), with the normal flipped to point back toward the ray origin per Box2D / planck convention.
  • Collision lifecycle hooks on every RenderableonCollisionStart / onCollisionActive / onCollisionEnd. Dispatched consistently by all three adapters with a receiver-symmetric contract (response.a === this, response.b === other, response.normal is the MTV of the receiver). The portable stomp idiom is response.normal.y < -0.7 — same expression on every adapter. Legacy onCollision is preserved unchanged for backward compatibility but superseded by onCollisionActive on a per-renderable basis.
  • Procedural audio primitivesme.audio.tone({ freq, attack, decay, gain, wave, filter, pitchSlide }) and me.audio.noise({ type: "white" | "pink" | "brown", duration, gain, filter, filterSweep }). Envelope-shaped oscillators and band/lowpass/highpass-filterable noise, perfect for SFX without sample assets. Public me.audio.getAudioContext() / me.audio.getMasterGain() accessors let custom WebAudio graphs mix with the engine's master volume / mute.
  • Three new showcase examplesMatter Platformer (the canonical platformer ported to matter), Pool (Matter) (top-down 8-ball pool, drag-to-aim cue with physics-driven break shots and pocket sensors), and Neon Plinko (Planck) (ball-ball collision + procedural WebAudio peg clack / pentatonic chime / row-pitch arpeggio with stereo pan).
  • BodyDefinition — declarative body description set via renderable.bodyDef and auto-registered by Container.addChild. Maps portable fields (type, shapes, collisionType, collisionMask, frictionAir, restitution, density, gravityScale, maxVelocity, isSensor) onto whichever adapter is active.

Changed

  • onCollisionActive supersedes legacy onCollision — defining onCollisionActive on a renderable suppresses the legacy every-frame onCollision dispatch on that same renderable. Per-side, so renderable A can be on the modern handler while renderable B is on the legacy one in the same pair; each gets the contract it asked for.
  • Built-in push-out semantics matter-aligned — dynamic non-sensor bodies separate by default whether or not onCollision is defined. The pre-19.5 "no onCollision defined ⇒ implicit sensor" quirk is gone; mark a body as a sensor explicitly via bodyDef.isSensor: true or body.setSensor(true) to opt out of push-out.

Bug Fixes

  • Container.removeChildNow evicts from the QuadTree broadphase — pool-recycled entities frequently take the removeChildNow(child, true) path; previously the broadphase retained a stale reference until the next full rebuild. Eviction is now eager.
  • QuadTree subtree-count + scratch reuse + collapse — the broadphase now tracks accurate sub-tree counts (was off after a collapse), reuses a single per-frame scratch array for retrieve calls, and collapses leaf-only subtrees after removeContainer. Combined with re-entrancy-safe public APIs, calling queryAABB / raycast from inside an onCollisionStart handler no longer clobbers the outer detector iteration.
  • CanvasRenderTarget.destroy source-keyed lookupdestroy() was using the target's own canvas as the cache key instead of the source; targets created via Canvas.from(source) survived their explicit destroy and leaked until GC.
  • OffscreenCanvas non-destructive texture upload — uploading an OffscreenCanvas-backed texture no longer resets its render context, so subsequent draws to the same surface keep working.
  • debug-plugin heap formatting — the heap-usage line in the debug panel formats correctly across browsers that report bytes differently.

Install

npm install melonjs@19.5.0

Optional new adapter packages:

npm install @melonjs/matter-adapter
npm install @melonjs/planck-adapter