FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Releases · melonjs/melonJS · GitHub

Releases: melonjs/melonJS

v20.2.0

Choose a tag to compare

Filter
github-actions released this 29 Aug 01:30
6b22c04

What's Changed

melonJS Team

  • docs: write down the changelog convention, and refresh the release docs (#1613) (6b22c04)
  • test(bench): stop drawmesh_bench wedging the shared GPU process (#1612) (1f871de)
  • perf(webgpu): memoize the per-quad segment slot lookup (#1608) (7047a7d)
  • perf(particles): compute particle bounds lazily (#1607) (441a392)
  • fix(webgpu): reset batchers when a frame is abandoned (#1609) (5e17030)
  • Particle reference spaces: measure particles from the world or any container (#1606) (b66a5cf)
  • Advanced blend modes on both GPU backends, and three renderer bugs found on the way (#1318) (#1604) (7b06a42)

Contributors

@Vareniel

  • fix(input): account for world offset in pointer events (#1605) (008980a)

Full Changelog: 20.1.1...20.2.0

v20.1.1

Choose a tag to compare

Filter
github-actions released this 25 Aug 03:32
8e8a8d3

What's New

A bug-fix release, and mostly one bug wearing seven faces. Two crashes were reported from a game in production; chasing the first turned up five siblings of the same shape, all fixed here.

The shape: an engine loop hands control to your code, then carries on using state your code was free to tear down. "Remove me when this animation finishes" and "remove it on pickup" are the most ordinary things to write in a callback, and removeChildNow() destroys immediately. Every one of these was reproduced before being fixed, and every fix is pinned by a test that fails against the previous build.

No API changes. No new features. melonjs only.

Bug Fixes

The two reported crashes

  • An animation callback that removed its own sprite crashed the frame. FrameAnimation.update() fires onended and the completion callback in the middle of its frame loop, then read the animation map the callback had just emptied — so a death animation removing its own sprite threw Cannot read properties of undefined (reading 'frames'). The {next, onComplete} chain form needed a separate fix: that wrapper re-enters the engine while still inside your callback.
  • A font whose filename contained a space failed to preload. data/fnt/Super Bouncer.ttf was wrapped as an unquoted CSS url(), which may not contain whitespace, so the descriptor never parsed and no request was made. Only fontface was affected — every other asset type hands its path to the browser, which encodes it.

Found by looking for siblings of the first

  • A collision handler that removed an object crashed the physics step, at four distinct sites depending on how you spelled it — including return false, the documented opt-out from push-out and therefore the likeliest form. "Remove it on pickup or on hit" is the commonest thing a collision handler does; the deferred world.removeChild() was always safe, removeChildNow() was not.
  • A GLTFModel animation callback that removed its own model crashed the frame — the 3D counterpart of the first bug, needing its own fix because GLTFModel re-implements the animation-callback contract rather than sharing it.
  • timer.updateTimers() skipped a timer whenever another fired. It iterated the timer list while removal spliced that same list, so two setTimeouts due on the same frame ran only the first — the second arriving a frame late. Silent, no error.
  • Container.update() updated a child twice when another child's update() removed a sibling at a lower index, double-stepping that child's animation, physics and timers for a frame. Silent. Self-removal was always safe and still is.
  • Renderable.parentApp threw for a renderable in a container never added to a world, instead of returning undefined as its own documentation promises.

Compatibility

No breaking changes. Every fix is a guard on a path that previously threw or silently misbehaved. The behaviours a guard could plausibly have damaged are asserted directly: a callback that merely switches animation keeps playing, ordinary frame advance is unaffected, onCollision returning false still opts out of push-out, and self-removal from a container still works.

@melonjs/planck-adapter and @melonjs/matter-adapter are unaffected and need no update — they work off native engine objects rather than the renderable state involved here, which was verified rather than assumed.

Install

npm install melonjs@20.1.1

Full details in the CHANGELOG.

v20.1.0

Choose a tag to compare

Filter
github-actions released this 23 Aug 23:24
3e46e55

What's New

Collision events, per shape. A body built from several shapes could only ever report one contact: onCollision and the onCollision* lifecycle surface the single pair chosen for physical resolution, so a character's footprint contact masked a simultaneous hurtbox contact. The new onShapeCollisionStart / onShapeCollisionActive / onShapeCollisionEnd report every overlapping shape pair, without changing which one resolves.

onShapeCollisionStart(contact, other) {
    // shapeA / indexShapeA are always YOUR shape
    if (contact.indexShapeA === HURTBOX && contact.isTrigger) {
        this.takeHit(other, contact.normal);
    }
}

Each contact carries both shapes, both indices into body.shapes, the trigger status and the SAT data, receiver-symmetric so shapeA is always your own. Supported on all three physics backends: the builtin detector enumerates shape pairs directly, while @melonjs/planck-adapter and @melonjs/matter-adapter dispatch natively, since Box2D reports one contact per fixture pair and matter one per part, and each maps a melonJS shape onto a single collider.

Purely additive. Physical resolution picks the same pair with the same solid-over-trigger preference from 20.0, every existing handler keeps its signature, and on the builtin detector the enumeration is opt-in: declare none of these handlers and the narrowphase does exactly the work it did before.

Requested by a downstream maintainer building on the per-shape collision filtering from 20.0.

New Features

  • onShapeCollisionStart / onShapeCollisionActive / onShapeCollisionEnd (#1596) on every physics backend, reporting each overlapping shape pair with both shapes, both indices, trigger status and contact geometry
  • ShapeCollisionContact is exported, so TypeScript users can type a handler parameter directly

Bug Fixes

  • The collision lifecycle only ever fired for one pair in the entire world. createGUID() returned the literal string "-1" on every call, so every renderable added to a container shared one GUID. Collision pair identity is built from GUIDs, so with two pairs colliding anywhere at once the second was treated as already-seen and its onCollisionStart / onCollisionActive / onCollisionEnd never fired. onCollision was unaffected, which is how it went unnoticed
  • @melonjs/matter-adapter: a body with more than one shape received no collision events at all. matter reports collisions between compound parts, but only the parent body was registered against its renderable, so every dispatch failed its lookup and returned early. That silently dropped onCollision and the whole onCollision* lifecycle for any multi-shape body, while single-shape bodies worked normally
  • FadeEffect.destroy() and MaskEffect.destroy() threw "Instance is already in pool" when called twice. Reachable without doing anything unusual, since removePostEffect() destroys the effect it removes, so a caller that also destroyed it explicitly got a throw from inside the pool

Compatibility

No breaking changes. Everything is additive, and the adapter peer ranges stay >=20.0.0: they dispatch these hooks themselves, so the events work on any melonJS 20.x.

Released alongside

package version
@melonjs/planck-adapter 1.3.0
@melonjs/matter-adapter 1.2.0

Install

npm install melonjs@20.1.0

Full details in the CHANGELOG.

v20.0.0

Choose a tag to compare

Filter
github-actions released this 21 Aug 02:16
dbaa973

What's New

A new WebGPU backend, and WebGL 2 as the baseline. Version 20.0 introduces a complete WebGPU renderer covering the entire engine feature set, 2D and 3D, verified example-for-example against WebGL. At the same time the aging WebGL 1 path is retired, which frees the WebGL backend to modernize throughout. video.AUTO now tries WebGPU first, falls back to WebGL 2 if that is unavailable, and ultimately to Canvas. See the reworked Hello WebGPU example.

This release has breaking changes. Starting a game is now two steps: construct the Application, then await app.init(). Read the Compatibility section below before upgrading.

New Features

  • WebGPU renderer (#1184) covering the full 2D contract (sprites, text and particles with multi-texture batching, shapes and Path2D, blend modes, patterns, scissor clipping, stencil masks, GPU tilemaps, 2D lights and normal maps, frame captures, gradient fills, compressed textures) and the complete 3D tier (drawMesh, retained geometry under Camera3d, glTF scenes, animated models, Sprite3d billboards, split-screen viewports)
  • Mesh instancing (#1508) via the new InstancedMesh: one geometry drawn many times in a single call. A 100 000-tree forest runs at 60 fps on both GPU backends, with optional per-instance colors and data. See the new Instanced Forest example. glTF EXT_mesh_gpu_instancing loads authored instancing with no user code
  • Ground shadows for 3D objects (#1515): castGroundShadow: true gives a mesh, billboard or whole instanced scatter a soft blob shadow, so 2.5D characters stop reading as floating. One extra draw per object, and one for an entire scatter whatever its size
  • 3D collision (#1476): the new Box3d body shape lets a body be pushed back along Z. The 2D path is unchanged
  • Per-shape collision settings (#1590): each shape on a body may carry its own collisionType / collisionMask, plus isActive and isTrigger. So one body can have a footprint that only hits terrain and a hurtbox that only reacts to attacks. A shape that sets nothing behaves exactly as before
  • Point and spot 3D lights (#1536) on both GPU backends, including authored glTF KHR_lights_punctual lamps
  • Up to 32 lights (#1552), raised from 8, by moving light data into a std140 uniform buffer
  • Dual-language shaders: ShaderEffect and GLShader each carry GLSL and WGSL bodies, so one asset runs on either GPU backend. All 18 built-in effects render identically on both. Existing GLSL-only effects are untouched
  • maxTextures setting (#1585): the WebGL texture pool follows the device instead of a hardcoded 16
  • antiAlias: true survives post effects (#1556); capture targets are now multisampled themselves
  • Mesh textures sample generated mipmaps on both GPU backends, so distant geometry stops shimmering
  • OBJ vertex normals (#1572), MTL specular and per-texel opacity (#1575), and per-material diffuse textures on multi-material models (#1573)
  • exclusion and "none" blend modes on both GPU backends (#1318)
  • Backend-neutral vertex formats and topologies (#1551); the GL-enum form stays supported indefinitely

Performance

Measured against 19.9.1 on the same machine, same harness and geometry on both sides:

19.9.1 20.0.0
2D, 512 quads/frame, one texture past the batch limit 33 draws, 1 626 uploads, 4.71 ms/frame 1 draw, 0 uploads, 0.06 ms/frame 76x
3D, 64 meshes of 5 000 vertices 128 draws, 3.80 ms/frame 64 draws, 0.04 ms/frame 95x

Below the texture limit the new path is marginally slower (0.052 to 0.062 ms), the residual being the per-source residency lookup that buys the rest. Also in this release: immutable texture storage across the WebGL pipeline, and Vertex Array Objects for every batcher.

Bug Fixes

  • Application.destroy() leaked the WebGL context, and destroyed renderers stayed subscribed to global events forever, pinning their GPU objects against garbage collection. Both hit any app that tears down and rebuilds
  • The 3D broadphase silently dropped collisions between bodies at different depths, and an entire 2.5D gameplay plane sat unpartitioned at the root of the octree, degrading it to a linear scan
  • A multi-shape sensor body was still pushed out of collisions (#1591), and Body.destroy() threw for ellipse colliders or when called twice
  • A texture-cache overflow re-created and re-uploaded every texture once per draw, and a ShaderEffect sampler could silently corrupt normal-map lighting (#1585)
  • A scene containing only meshes stopped clearing its depth buffer after the first frame; a lit mesh with no usable normals rendered solid black
  • DropShadowEffect rendered its shadow mirrored when chained with other effects on WebGL; untextured glTF materials rendered washed out; indexed PNGs in glTF assets rendered greyscale on Safari

Compatibility

This is a major release with breaking changes.

  • await app.init() is now mandatory. Construct the Application, then await init(). Construction alone no longer builds the renderer or appends the canvas, so without the call nothing displays. This applies to code already using new Application(...) on 19.x:
    const app = new Application(640, 480, { parent: "screen" });
    await app.init();
  • video.init(), video.renderer, video.createCanvas() and video.getParent() are removed (deprecated since 18.3.0 / 19.7.0). Use app.init(), app.renderer, app.renderer.createCanvas() and app.getParentElement()
  • The WebGL renderer is WebGL 2 only (#1509). video.AUTO falls back to Canvas on WebGL-1-only devices. User shaders need no changes: GLSL ES 1.00 compiles unchanged on WebGL 2
  • video.AUTO now tries WebGPU first. Pin renderer: video.WEBGL (or the #webgl URI fragment) to stay on WebGL
  • Custom batchers extend WebGLBatcher instead of Batcher, which is now the backend-neutral base. One word, no other change
  • Custom mesh shaders receive geometry in model space (#1507); position with uProjectionMatrix * uViewMatrix * uModelMatrix and tint with uTint. Sprite and post-effect shaders are unaffected
  • Application.updateAverageDelta is renamed lastUpdateDelta; the old name keeps working as an alias

See the Upgrade Guide for the full migration.

Released alongside

package version
@melonjs/spine-plugin 4.0.0 (WebGPU support; requires melonJS 20)
@melonjs/planck-adapter 1.2.0
@melonjs/matter-adapter 1.1.1
@melonjs/debug-plugin 16.1.1
@melonjs/tiled-inflate-plugin 1.2.1

Install

npm install melonjs@20.0.0

Full details in the CHANGELOG.

v19.9.1

Choose a tag to compare

Filter
github-actions released this 28 Jul 01:20
ad2f2bd

What's New

19.9.1 is a focused bug-fix release for the 19.9 line — 17 fixes, two long-standing open issues closed, no API additions, drop-in upgrade from 19.9.0.

The headline: video sprites no longer stall in fullscreen. Large videos (1080p/4K) dropped to ~4fps the moment the game went fullscreen while everything else kept rendering at 60 — the browser parks off-DOM video elements on a slow background timer once the page compositor idles, and a fullscreen canvas is exactly what idles it. The engine now keeps a requestVideoFrameCallback pending on the element, which forces full-rate frame delivery, and as a bonus only uploads to the GPU when the video actually presented a new frame.

Also closed: #1231 — the canvas slowly growing on every window resize (alt-tab, devtools) when its container sits inside a wrapper with no fixed CSS size. Two root causes, both fixed: the engine canvas now defaults to display: block (the browser's inline default added a baseline gap that fed back into the auto-scale measurement), and auto-scale measures the container's content box instead of its border box.

Bug Fixes

  • Video fullscreen frame stall — pending requestVideoFrameCallback keeps detached video elements at full frame rate (see above); video sprites also correctly pause on state.pause() now (the listener had a wrong event name since its introduction)
  • Canvas growth feedback loop on resize (#1231) — display: block default + content-box measurement (see above)
  • Renderables batch: onVisibilityChange fired a spurious leave/enter pair every frame for visible objects; Container/Entity destroy() passed an Arguments object to onDestroyEvent instead of the actual arguments; Container.getNextChild() returned the first child for a non-member; Text/BitmapText destroy() wiped a caller-provided string array; NineSliceSprite showed ~2px seams for region sizes not divisible by 4
  • Animation batch: a completed loop: false animation could never be replayed via play()/setCurrentAnimation(); frames with a 0 delay froze the game in an infinite loop (now advance one frame per tick)
  • Camera batch: destroyed cameras kept reacting to game reset / canvas resize (crash after destroy, one leaked camera per state switch with cameraClass — stages now destroy the cameras they construct); the bounds clamp ignored a non-zero world origin, making the far edges unreachable
  • Particles: dead particles were released to the pool while their removal was still deferred — a same-frame respawn could silently vanish, and a same-frame emitter teardown could poison the pool
  • Drag & drop: DropTarget.setCheckMethod() validated the current method instead of the new one, accepting invalid names that crashed later on drop
  • Hardening: ShaderEffect.setTexture() rejects an HTMLVideoElement with a clear TypeError instead of silently freezing on its first frame; BMFont padding was parsed with padLeft/padRight swapped; the texture cache's dead frame-dimension refinement is removed (#1489) with the first-registered-atlas behavior now explicit and test-codified

Performance

  • Video textures upload to the GPU only when a new frame was actually presented — previously a full texImage2D ran on every render tick per sprite (~2× redundant for 30fps video at 60Hz, more when several sprites share one element)

Changed

  • The engine-inserted canvas defaults to display: block; set any inline display style on the canvas to keep your own value
  • Videos now genuinely pause on state.pause() — this was always the documented behavior, but the listener never fired

Install

npm install melonjs@19.9.1

CDN:

<script type="module" src="https://cdn.jsdelivr.net/npm/melonjs@19/+esm"></script>

🤖 Generated with Claude Code

v19.9.0

Choose a tag to compare

Filter
github-actions released this 14 Jul 04:23
41b1c7b

What's New

Shader effects made easy. Effects that used to demand WebGL expertise, like a pond rippling with the scene reflected in it, heat haze, or frosted glass, now take a few lines of shader code: the engine hands your effect the screen behind it and the right coordinates, animated noise textures come built-in, and shaders preload like any other asset. See the new Water Overworld example for all of it in action, and the updated Shader Effects wiki guide.

New Features

  • Shader builtins: screen_texture / screen_uv / noise_uv — annotate a sampler with : screen_texture and the engine keeps it filled with the screen behind the object; screen_uv / noise_uv are free varyings for screen-space and frame-local (atlas-independent) coordinates. No JS plumbing, fully backward compatible (design and demo scene contributed by @Vareniel)
  • renderer.toFrameTexture() — GPU capture of everything drawn so far, as a live Texture2d for shader input (initial approach contributed by @Vareniel)
  • Shader preloading — the "shader" asset type compiles GLSL at load time; loader.getShader() returns the shared instance (clone() for per-renderable uniforms). A {vertex, fragment} pair compiles into a raw GLShader for Mesh / custom-batcher shaders
  • ShaderEffect.setTexture(name, image[, repeat]) — bind extra sampler2D textures (noise, masks, LUTs) to an effect (thanks @Vareniel)
  • ShaderEffect.setTime(seconds) and a shared flag on ShaderEffect / GLShader for effects reused across renderables
  • Procedural noise: Noise + NoiseTexture2d — deterministic CPU-samplable coherent noise (simplex / perlin / value / cellular, fractal layering, domain warp), bakeable as grayscale, a color ramp, or an animated normal map
  • Anchor-point presets + Sprite3d anchor support — anchorPoint: "bottom", "top-left", … uniformly across every renderable; Sprite3d gains the same key, vertex-baked and runtime-mutable (thanks @asadullahbro)
  • Holes & compound paths in Path2D / SVG fills — inner sub-paths render as holes (donuts, letter counters) under both renderers
  • BMFont XML support for BitmapText — the AngelCode XML flavour loads directly, auto-detected
  • Texture2d base class — renderables accept any texture asset directly as image

Bug Fixes

  • Asset loading over the file: protocol works again (Cordova/Capacitor APK) — fetchData falls back to XHR where fetch() refuses file:// (thanks @Vareniel)
  • Loader: unloadAll() no longer throws on fontfaces or leaks videos; load() no longer mutates the caller's asset descriptor; video preloading no longer hangs on autoplay-restricted browsers or forces anonymous CORS; compressed-texture loading no longer crashes on devices missing a compression family
  • Audio batch: preload/retry/unload lifecycle fixes and honest getter/setter types across seek, rate, position, stereo, resume, getCurrentTrack
  • Texture atlas batch: aseprite trim/rotation/pivot and per-frame durations are honored; coordinate-alias cache, video regions, padded spritesheets, and malformed-atlas validation all fixed
  • WebGL batcher batch: texture-unit collisions (lit normal maps, re-uploads, post-effect unit 0), oversized primitive shapes, GPU texture/buffer leaks, and multi-renderer state crosstalk all fixed
  • Rendering: nested multi-effect post-effects no longer crash; gradient fills respect inverted/nested masks; disableScissor() flushes; WebGL clearRect() erases; Canvas clipRect() honors transforms; large pos.z sort keys no longer clip-cull sprites (19.7 regression)
  • Geometry/math: Path2D arcs connect per spec; Rect edge/vertex fixes; moveTowards() lands exactly; Matrix2d 6-argument form fixed; CSS color keyword table spec-validated
  • NineSliceSprite keeps its expanded size when animated (thanks @NemoStein); Text generic CSS font families render correctly

Compatibility

No breaking changes — everything additive / back-compatible.

Install

npm install melonjs@19.9.0

Full details in the CHANGELOG.

v19.8.0

Choose a tag to compare

Filter
github-actions released this 26 Jun 02:22
2488bde

What's New

3D grows up. glTF / GLB scene loading lands — author a scene in Blender (or any DCC tool), export a .glb, and load it like a Tiled map with level.load(...), with node animation, authored lighting, and real 3D bounds. And Sprite3d brings the 2.5D workflow: billboarded, frame-animated cut-out sprites that face a Camera3d (the Paper Mario look), sharing one FrameAnimation engine with the 2D Sprite.

New Features

  • glTF / GLB scene loader (Tier 1) — level.load() instantiates every mesh node; parses the node graph, primitives, materials, perspective cameras, KHR_lights_punctual lights, scene bounds, and node animations. loader.getGLTF() exposes the raw descriptor.
  • glTF node animation + GLTFModel — rig-driven, hierarchy-preserving TRS animation (LERP/SLERP/STEP/CUBICSPLINE), driven through the same setCurrentAnimation / play / pause / stop API as Sprite.
  • Sprite3d — 3D billboard sprites under Camera3d (false / "cylindrical" / "spherical"), frame animation via the shared FrameAnimation engine, packed-atlas parity (rotated + trimmed regions), alpha cutout, and flipX / flipY.
  • Light3d — manipulable directional + ambient lighting managed like Light2d; glTF scenes light meshes by their authored sun.
  • Mesh materials — textureRepeat, textureFilter (decoupled from antiAlias), baseColorFactor + vertex colors, KHR_materials_unlit, alphaCutoff, emissive; OBJ/MTL map_Kd textures auto-load.
  • Mesh.getBounds3d(), Camera3d.worldToScreen(), exported AABB3d, and meshes with >65,535 vertices.
  • loader.preload() / loader.load() are now await-able (Promise form; callback forms unchanged).
  • Aligned 2D + 3D animation API — Sprite gains the options form setCurrentAnimation(name, { loop, speed, onComplete, next }), getAnimationNames(), and play / pause / stop.

Bug Fixes

  • glTF/3D meshes no longer render at the wrong position under Camera3d (anchor-offset no longer leaks into the shared mesh view matrix).
  • Camera3d no longer culls sizeless grouping containers and their whole subtree (fixes a nested GLTFModel rig not rendering).

Performance

  • Allocation-free glTF animation pose path — sampling + re-posing an animated rig allocates nothing per frame.
  • ~30% faster mesh batching (and ~7x less GC) via a versioned typed-array vertex remap — benefits all 3D mesh rendering.

Compatibility

No breaking changes — everything additive / back-compatible. Pairs with @melonjs/debug-plugin@16.1.0 (3D bounding-box overlay).

Install

npm install melonjs@19.8.0

Full details in the CHANGELOG.

v19.7.1

Choose a tag to compare

Filter
github-actions released this 14 Jun 01:46
4148609

What's New

19.7.1 is a focused bug-fix release for the 19.7 line. Three engine bugs and a noise reduction — no breaking changes, no API additions, drop-in upgrade from 19.7.0.

Alongside this release: @melonjs/spine-plugin 3.0.0 ships with Spine 4.3 runtime support, Skeleton.yDown adoption, native WebGL context-loss recovery, and a fully reworked Canvas renderer. The plugin requires melonjs >= 19.7.1 because the blend-cache restore fix below is load-bearing for its lose/restore path.

Bug Fixes

  • WebGL context-restore left blendFunc / blendEquation at driver defaults when the cached blend mode matched the requested one. setBlendMode()'s state cache survived the context loss and short-circuited the re-apply in the webglcontextrestored handler, so scenes using a single blend mode (e.g. all-PMA "normal") rendered transparent texels as opaque black until something else changed the blend mode. The restore handler now invalidates the cache before re-applying. (Surfaced by spine-plugin context-loss verification — 19.6's hardening scenes all happened to change blend modes post-restore, which masked the desync.)
  • UITextButton crashed with TypeError: viewport.isDefault on undefined every draw (#1499, regression introduced with multi-camera support in #1310 — has been broken for ~3 months / 12 releases). UITextButton.draw()'s super.draw(renderer) chain dropped the viewport argument the post-#1310 Container.draw() requires. Two-sided fix: uitextbutton.ts forwards the viewport, and Container.draw() honors its documented-optional viewport parameter — so any legacy subclass override that chains up with super.draw(renderer) keeps working. Two new test files (tests/uitextbutton.spec.js, tests/ui-interaction.spec.js) pin the unit + integration paths.

Changed

  • Spurious "gpuTilemap is enabled but the active renderer is not WebGL 2" warning at every Application init even when no TMX layer was ever loaded. Relocated to TMXLayer and latched once per session — apps without any tilemap (Spine demos, UI-only scenes, etc.) stay quiet; multi-layer maps see the heads-up once instead of N times.

Docs

  • Fixed matrix3d.ts JSDoc referencing images/glOrtho.gif while the actual asset is lowercase images/glortho.gif — the orthogonal-projection diagram was 404'ing on melonjs.github.io.

Install

npm install melonjs@19.7.1

CDN:

<script type="module" src="https://cdn.jsdelivr.net/npm/melonjs@19/+esm"></script>

🤖 Generated with Claude Code

v19.7.0

Choose a tag to compare

Filter
github-actions released this 08 Jun 23:58
fe9d160

What's New in melonJS 19.7.0

Highlights: Camera3d perspective camera lands. Every batched shader carries per-sprite depth as vec3 aVertex, unlocking 3D-projected sprites and meshes. Backward compatible with existing 2D code.

New Features

  • Camera3d — perspective camera extending Camera2d with fov, aspect, pitch, yaw, followOffset, lookAhead. Opt in via new Application(w, h, { cameraClass: Camera3d }) or Stage({ cameras: [new Camera3d(...)] }). Y-down + +Z forward.
  • Octree broadphase — 3D spatial subdivision sibling to QuadTree. World.broadphase reactively swaps based on sortOn so 2D↔3D transitions are transparent. Region queries: queryAABB, querySphere, queryFrustum, queryRay.
  • Sphere geometry primitive (new Sphere(x, y, z, r)) + AABB3d + Frustum — the 3D shape vocabulary. Sphere is the canonical 3D query shape (adapter.querySphere(sphere), Octree.querySphere(sphere)).
  • Camera3d.queryVisible(world) — bulk frustum cull broadphase pass for dense 3D scenes.
  • Mesh under Camera3d — world-space GPU projection with lazy back-face winding reversal.
  • Multi-material OBJ — Mesh draws OBJ files with multiple usemtl directives + MTL, baking per-material Kd into a per-vertex color buffer. Single draw call regardless of material count. New Multi-material OBJ example.
  • Per-sprite depth on the GPU — Quad, LitQuad, Primitive, and GPU TMX batchers all carry .depth as the z component. renderer.setDepth(depth) mirrors setTint.
  • PhysicsAdapter.querySphere? + raycast3d? — optional 3D query surface, capability-gated by capabilities.raycasts3d. BuiltinAdapter implements both; matter / planck omit them.
  • math.lerp(a, b, t) — scalar linear interpolation, single source of truth for vector lerps.
  • math.damp(current, target, lambda, dt) — frame-rate-independent exponential damping (Three.js MathUtils.damp parity). Vector2d.damp / Vector3d.damp follow the same shape.
  • event.GPU_TEXTURE_CACHE_RESET + event.RENDER_TARGET_CHANGED — renderer-agnostic events for cross-batcher state invalidation. Future WebGPU port emits the same events.
  • Application#requestFullscreen / exitFullscreen / isFullscreen — app-instance fullscreen control replacing the deprecated device.requestFullscreen global-game lookup.
  • device.setAutoFocus(enable) (#1486) — function setter for the autofocus-on-visibility behaviour, finally writable from user code (direct field assignment was a read-only ESM binding).

Changed

  • Camera2d smooth follow is now frame-rate independent. updateTarget swaps pos.lerp for pos.damp(target, lambda, dt) with lambda = -ln(1 - damping) * timer.maxfps. Existing damping values keep their feel at the target framerate; high-refresh users finally get the same convergence the dev tuned for. No tuning change required.
  • Mesh rendering clears depth once per target, not per mesh (#1468). MeshBatcher now owns mesh-mode GL state (bind() enters, unbind() restores). Consecutive mesh draws pay zero state-toggle cost between them. Side-effect: intersecting / painter-wrong-order meshes resolve per-pixel via the GPU depth test (closer wins regardless of draw order), where the old per-mesh clear let the second draw silently overwrite.
  • Application fails loudly on WebGL-required misconfiguration (#1479). Throws when renderer: video.WEBGL is requested but WebGL is unavailable (was silent Canvas fallback); warns when cameraClass.defaultSortOn === "depth" (Camera3d or subclass) under a Canvas renderer.
  • device.platform.isMobile drops the dead-platform regexes (wp, BlackBerry, Kindle) — chain is now /Mobi/.test(ua) || iOS || android, covering ~99.9% of mobile traffic per MDN.
  • device.platform.iOS / isMobile correctly identify iPads on iPadOS 13+ (#1467). Since Sept 2019 Safari on iPad ships the desktop Mac UA — no iPad token — so every modern iPad was falling through isMobile as desktop. Detection layers navigator.platform === "MacIntel" && maxTouchPoints > 1 on top of the UA regex.
  • timer.step is now the precise 1000 / maxfps (was Math.ceil), fixing a ~2% tick-interpolation undershoot under frame drops.
  • Breaking-ish: QuadTree dropped from public package exports — broadphase is implementation detail behind world.adapter.*. Direct import { QuadTree } from "melonjs" should be removed; the broadphase instance is still reachable as world.broadphase for tooling.
  • Breaking-ish: aVertex widened from vec2 to vec3 across quad-multi.vert, quad-multi-lit.vert, primitive.vert, orthogonal-tmxlayer.vert. Custom shaders binding by name keep working (attribute vec2 aVertex; is fine, z is dropped).
  • Breaking-ish: VertexArrayBuffer.push() gained a z parameter between y and u. Custom batchers that reimplement addQuad / drawVertices need to insert z (default 0); subclasses that delegate to super.addQuad are unaffected.
  • Camera2d default near / far widened from ±1000 to ±1e6 so depth-participating sprites don't cull-clip under Container.autoDepth or Y-sort patterns on tall maps.
  • system/device converted to TypeScript (#1467).

Bug Fixes

  • WebGLRenderer.createPattern cache-key collision (#1448) — two patterns created from the same source image with different repeat modes used to silently collide on a single GL texture unit. TextureCache now keys units by (source, repeat) via a nested Map<source, Map<repeat, unit>>; each distinct repeat mode gets its own unit. Canvas / WebGL createPattern(image) (no repeat arg) defaults to "no-repeat" in both renderers for parity.
  • GPU TMX layer reset crash (#1471) when a non-material batcher was active at stage change. Reset now pins to batchers.get("quad") instead of grabbing renderer.currentBatcher.
  • WebGL TextureCache cross-batcher binding desync — a unit-pool reset only cleared the current batcher's boundTextures map; stale entries on every other batcher caused meshes to render as black silhouettes and bullets as pure white in mixed-batcher scenes. Fixed via the new event.GPU_TEXTURE_CACHE_RESET event.
  • WebGL color-attribute NaN canonicalization on Apple Metal / ANGLE — MeshBatcher color attribute switched from UNSIGNED_BYTE × 4 normalized to FLOAT × 4; packed-color bytes upload via vertex.toUint8() so they survive driver canonicalization.
  • Stage.reset re-applies the chosen camera's defaultSortOn on every reset — covers the loader-pinned-Camera2d → user-stage handoff so distant meshes no longer paint on top of nearer ones under perspective.

Performance

  • Mesh-state ownership migration unlocks dense-3D-scene CPU wins (50+ mesh draws). Per-mesh state-toggle cost goes to zero between consecutive draws. Extrapolated 3–30ms saved per frame at 50+ mesh scenes; sparse scenes (~5 meshes / frame) are within noise.
  • GPU TMX uniform caching — gl.uniform* calls on hot per-layer paths skip when the value matches the previously-set cached value.

Install

npm install melonjs@19.7.0

v19.6.0

Choose a tag to compare

Filter
obiot released this 22 May 23:31
39631a3

What's New in melonJS 19.6.0

WebGL context-loss hardening release. Fixes a Windows + Chrome crash where a GPU switch lost the WebGL context and a partial GLShader.destroy() left the next frame's setUniform("uTime", …) reading from null uniforms. Beyond the crash, the renderer now transparently recovers the rest of the pipeline (vertex buffer, default GL state, batchers, texture cache) across a webglcontextlost → webglcontextrestored cycle, and shaders replay their cached uniforms on restore — so the game keeps drawing across a GPU switch without any intervention from user code.

New Features

  • Transparent WebGL context recovery, pipeline-wide — the renderer re-creates its vertex buffer, re-applies default GL state, re-initialises every batcher, drops stale texture-unit assignments, and emits ONCONTEXT_RESTORED so GLShader / ShaderEffect instances recompile + replay their cached uniforms. NVIDIA Optimus GPU switches, browser tab eviction recovery, and WEBGL_lose_context teardowns no longer require user code to re-apply uniforms, re-upload textures, or recreate shaders.
  • GLShader.destroyed / GLShader.suspended / ShaderEffect.destroyed — public read-only diagnostic flags. destroyed is the stable "explicitly released" signal (distinct from ShaderEffect.enabled, which auto-toggles across the cycle); suspended is true only between lost and restored.

Changed

  • Platformer example coins migrated to per-instance ShineEffect, matching the pattern of every other 19.5+ shader-using example. GAME_UPDATE subscription tied to onActivateEvent / onDeactivateEvent so pool-recycled coins re-bind on respawn instead of leaking a dead handler per pickup.

Bug Fixes

  • Stale shader references no longer crash — GLShader.destroy() is atomic + idempotent and wraps gl.deleteProgram in a try / catch, so a throw on a dead ANGLE / D3D11 context (the 19.5.0 Windows + Chrome TypeError: Cannot read properties of null (reading 'uTime') crash) can't leave a half-destroyed shader. Public setUniform / bind / getAttribLocation paths also no-op on destroyed shaders instead of throwing.
  • ShaderEffect.destroy() sets enabled = false BEFORE the inner destroy — even if the inner destroy throws outward, the effect's public methods are already in the safe no-op state via the enabled gate.
  • GLShader._uniformCache snapshots arrays / typed arrays via captureValue on write, so caller mutation after setUniform can't silently rewrite what replays on context restore.
  • ShaderEffect.enabled auto-toggle preserves user-set state across a context cycle — an effect the user explicitly disabled stays disabled after restore.

Performance

  • GLShader.setUniform zero-allocation hot path — the replay cache reuses the existing slot's array (via captureValue) when the value's length matches. Steady-state writes for the same uniform name (light positions, animation uniforms, per-frame uTime) no longer allocate a fresh array on every call.

Install

npm install melonjs@19.6.0

Back | FazBrowse Home | New Git URL