Skip to content

Version Packages - #7

Merged
DavidArayan merged 1 commit into
mainfrom
changeset-release/main
Sep 8, 2026
Merged

DavidArayan merged 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@ignifx/2d@0.2.1

Patch Changes

  • Updated dependencies [388b0f6]
    • @ignifx/core@0.2.1

@ignifx/3d@0.2.1

Patch Changes

  • 7adf987: The look rigs re-arm pointer lock, ignore an unlocked mouse, and read a stick as a rate

    FirstPersonController and ThirdPersonCamera were each doing their own look arithmetic, and both
    were wrong in the same four ways. The look half of both now lives in one internal module and the
    four defects are fixed together.

    Pointer lock re-arms. FirstPersonController set a latch on its first request and never cleared
    it, so once the browser released the lock — Escape, which the templates also bind to the pause menu,
    or a focus change — no later click could take it back and the player finished the session with an
    unlocked mouse. The lock is now requested on every frame that carries a pointerdown
    while the lock is not held; the only guard is a request already in flight. A refusal is logged and
    dropped, exactly as before.

    An unlocked mouse no longer turns the view. While lockPointerOnClick is on and the lock is not
    held, a Look reading whose InputAction.activeDevice is Mouse or Pointer is ignored. Before,
    the view spun whenever the cursor crossed the canvas — on its way to a menu button, say — and stopped
    dead when the cursor left the frame. Gamepad, touch, and virtual sticks are never gated: they have no
    cursor to lose. lockPointerOnClick: false keeps unconditional mouse look for drag-to-look designs.

    A stick is a rate, a pointer is a displacement. Both rigs did yaw += look.x * sensitivity for
    every device. That is right for a pointer delta, which is this frame's motion, and wrong for a stick,
    whose value is a deflection: at 120 Hz the stick turned twice as fast as at 60 Hz, and games papered
    over it with a scale(18) processor. A gamepad or virtual reading is now multiplied by the new
    stickLookSpeed (degrees per second at full deflection, default 180) and the frame delta, so the
    same push turns through the same angle in the same second at any frame rate, and slow motion slows
    the look with everything else. sensitivity keeps its meaning for pointer readings — degrees per unit,
    which with @ignifx/input's CSS-pixel deltas is degrees per CSS pixel, so 0.08–0.15 suits most mice
    on every display and at every render scale.

    Up is up on every device. A screen's movementY grows downward and a stick's y grows upward,
    and both rigs subtracted look.y from their pitch regardless — so one invertY could only ever suit
    one device family, and a first-person mouse looked down when it was pushed forward. The pitch axis
    is normalised where the device is known: Mouse, Pointer, and Touch readings are negated, gamepad
    and virtual are not, and the rigs apply their own sign and invertY on top as before. Moving the
    mouse forward and pushing a stick up now both look up in first person, and both lower the boom and
    aim the camera up in third person; invertY flips every device together.

    ThirdPersonCamera also gains lockPointerOnClick, defaulting to false: a rig that orbits on a
    held mouse button needs its cursor, so nothing changes for an existing third-person game until it
    opts in to the console-style "click to capture, Escape to release" behaviour.

    Public API change: FirstPersonController.stickLookSpeed, ThirdPersonCamera.stickLookSpeed, and
    ThirdPersonCamera.lockPointerOnClick are new schema fields. No field was removed or renamed. Two
    behaviour changes need a look from any game that ships a look binding: drop the scale(...)
    processors from stick look bindings, which double-count against stickLookSpeed, keeping the
    deadzone(...); and drop any scale(1, -1) that existed to line a stick up with the mouse, because
    the rigs agree on which way is up now — reach for invertY instead.

  • 6a39fae: shapeCast can sweep past one body, and the third-person boom and step probe no longer report the character's own capsule

    Lite's ShapeCastQuery carries no collision masks — unlike physicsRaycast — so a shape sweep
    cannot be filtered by layer inside Havok; layerMask only ever decided which hit was attributed
    an entity, and a body outside the mask still stopped the sweep, reported with entity: null. Two
    callers in @ignifx/3d were sweeping from inside the character's own capsule and were stopped by it
    at fraction zero every time:

    • ThirdPersonCamera sweeps from its shoulder pivot, which sits inside the capsule, out along the
      boom. For every yaw whose boom crossed the capsule the sweep hit it immediately, currentDistance
      collapsed to 0 and the camera sat inside the character's head — measured on 2026-09-08 in the
      third-person template at every yaw from 30° to 180° at the spawn, with no wall anywhere near.
      collisionLayers: ["Level", "Prop"] could not prevent it, because the mask never reached the sweep.
    • ThirdPersonController's step probe sweeps a sphere forward from the character's feet; both of its
      sweeps found the capsule first, so stepHeight never lifted anything.

    ShapeCastOptions — QueryOptions plus ignore?: Entity | null — names the one body the sweep
    passes through, resolved to its Havok body whether the entity is a Rigidbody, a collider-only
    static, or a CharacterController (whose capsule body Lite exposes through getBody()). The camera
    rig ignores its target; the step probe ignores its own entity. A masked-out body other than the
    ignored one still shortens the boom, which for scenery is the point and is now said so on
    collisionLayers.

    What a game author sees: a third-person camera that holds its distance through a full orbit and a
    stepHeight that climbs steps; and, for their own queries, a way to sweep out of a body they are
    standing in.

    Public API change: ShapeCastOptions is new and PhysicsService.shapeCast takes it in place of
    QueryOptions (a widening — every existing call compiles). Nothing was removed or renamed.

  • ef054ec: ThirdPersonCamera no longer rolls the horizon when the mouse moves sideways

    The rig built its rotation with one Quat.fromEulerDegrees(pitch, yaw, 0) call. That helper composes
    in intrinsic XYZ order, so the boom was pitched first and then yawed about the tilted axis: with
    any pitch at all — and the template starts twelve degrees down — a horizontal mouse motion tilted the
    camera rather than turning it, and the camera rose and fell as it went round (measured 2026-09-08:
    at 15 degrees of pitch its height followed cos(yaw)).

    The rotation is now Ry(yaw) * Rx(pitch): yaw about the world's up, then pitch about the camera's
    own right, which is the one order in which looking sideways is a turn about the vertical. The
    starting angles are read off the entity's forward vector at awake instead of its Euler angles, so
    an authored downward tilt seeds the same pitch whatever Euler order wrote it.

    What a game author sees: a third-person camera whose horizon stays level and whose height stays put
    through a full orbit, at every pitch.

    Public API change: none.

  • Updated dependencies [21a4ba7]

  • Updated dependencies [6a39fae]

  • Updated dependencies [388b0f6]

  • Updated dependencies [6a39fae]

    • @ignifx/input@0.2.1
    • @ignifx/physics@0.2.1
    • @ignifx/core@0.2.1

@ignifx/audio@0.2.1

Patch Changes

  • Updated dependencies [388b0f6]
    • @ignifx/core@0.2.1

@ignifx/core@0.2.1

Patch Changes

  • 388b0f6: PostProcessStack fields are live: a slider bound to bloom.threshold now changes the picture

    The stack built its chain once, the first time any effect was enabled, and every sync after that
    only flipped the chain's executionEnabled to match the component. The bloom and SMAA records were
    read at that one moment and never again, so post.bloom.threshold = 0.4 on a running game did
    nothing — the website's bloom example had four sliders and none of them moved the frame — and
    post.bloom.enabled = false on a one-effect chain left bloom running, because the chain's identity
    was checked only while no chain existed yet.

    Lite's BloomPostProcessTask exposes weight, kernel, threshold and exposure as writable
    fields and updateUniforms() re-uploads every sub-pass from them; SmaaPostProcessTask documents
    the same for threshold, maxSearchSteps, diagonalDetection and cornerDetection (verified
    against @babylonjs/lite@1.27.0, index.d.ts 1287 and 11622). The chain now keeps a typed handle
    to each of those tasks and, once per frame, uploads whatever changed since the last upload — a frame
    in which nothing moved uploads nothing. The one tuning Lite fixes at creation is bloom's
    bloomScale, which sizes the blur targets, so bloom.scale joins "which effects, in which order"
    in the chain's identity: a change to any of them disposes the old chain and records a new one. Lite
    cannot remove a task from a frame graph, so the old tasks stay in it, disabled and with their GPU
    resources freed, at one branch per frame each — toggling an effect's enabled in a settings menu
    costs a rebuild per click, whereas toggling the whole component's enabled keeps the chain and
    skips it, which is what the templates' settings screens do.

    What a game author sees: an inspector edit, a settings slider or a script write to any bloom or SMAA
    field takes effect on the next frame, and turning a single effect off actually turns it off.

    Public API change: none. PostProcessChain.applySettings and the two adapter functions it calls are
    @internal.

@ignifx/devtools@0.2.1

Patch Changes

  • Updated dependencies [21a4ba7]
  • Updated dependencies [6a39fae]
  • Updated dependencies [6a39fae]
  • Updated dependencies [388b0f6]
  • Updated dependencies [6a39fae]
    • @ignifx/input@0.2.1
    • @ignifx/physics-2d@0.2.1
    • @ignifx/physics@0.2.1
    • @ignifx/core@0.2.1
    • @ignifx/ui@0.2.1
    • @ignifx/audio@0.2.1

@ignifx/electron@0.2.1

Patch Changes

  • Updated dependencies [388b0f6]
    • @ignifx/core@0.2.1

ignifx@0.2.1

Patch Changes

  • Updated dependencies [7adf987]
  • Updated dependencies [21a4ba7]
  • Updated dependencies [6a39fae]
  • Updated dependencies [6a39fae]
  • Updated dependencies [388b0f6]
  • Updated dependencies [6a39fae]
  • Updated dependencies [ef054ec]
    • @ignifx/3d@0.2.1
    • @ignifx/input@0.2.1
    • @ignifx/physics-2d@0.2.1
    • @ignifx/physics@0.2.1
    • @ignifx/core@0.2.1
    • @ignifx/devtools@0.2.1
    • @ignifx/electron@0.2.1
    • @ignifx/ui@0.2.1
    • @ignifx/2d@0.2.1
    • @ignifx/audio@0.2.1

@ignifx/input@0.2.1

Patch Changes

  • 21a4ba7: Pointer deltas are CSS pixels, the canvas wheel no longer scrolls the page, InputAction.activeDevice, and pointer lock asks for raw mouse motion

    Four fixes to the things a look control is built out of, all found while chasing "the camera on the
    third-person example does not behave correctly" and "scroll interferes with page scrolling".

    The wheel is taken non-passively. The canvas wheel listener was registered { passive: true },
    which forbids preventDefault, so a wheel over a running game zoomed the camera and scrolled the
    page underneath it — obvious on ignifx.com, where the examples live in an <iframe> and the wheel
    took the article with it. The listener is now non-passive and prevents the default on every event it
    queues. The wheel is still read from the canvas alone, so a wheel over the page's own chrome is
    untouched, and the queued entry is unchanged.

    Pointer deltas are CSS pixels; positions stay backing-store pixels. <Mouse>/delta,
    <Pointer>/delta, <Touch>/…/delta and event.deltaX/deltaY were multiplied by
    canvas.width / rect.width, the same scale positions need. A delta is hand motion, not a place on
    the render target: the scale doubled every look sensitivity on a device-pixel-ratio-2 display and
    moved it again whenever a settings screen changed renderer.resolutionScale. Deltas are now the
    browser's raw movementX/movementY, and for the pointer types that leave those at zero (touch,
    some pens) the DOM adapter derives the motion from that pointer's own successive clientX/clientY
    — in CSS pixels, never from the queued position. DeviceWriter derives nothing at all now, so
    simulateEvent reports exactly the delta a test states. Positions are unchanged, so
    renderer.pickAsync(pointer.position) and Camera.screenToRay are still exact at every ratio.
    A game author sees one number to tune: a mouse look sensitivity in degrees per CSS pixel, around
    0.08–0.15, the same on every display.

    InputAction.activeDevice names the device family behind the binding whose magnitude won the
    frame, and null when the action is at rest or disabled — stable for the frame like every other
    reading. One Look action bound to both a mouse and a stick carries two different quantities, and
    this is what lets a rig tell them apart: @ignifx/3d uses it to ignore mouse look until the pointer
    is locked and to read a stick as a rate. A composite reports the device of its first part, which is
    the only sensible answer for a 2DVector whose four parts are one device.

    PointerLock.request() asks for unadjustedMovement: true before it asks plainly, falling back
    when the browser rejects the option by throwing or by rejecting the returned promise. That is raw
    mouse motion with the desktop's pointer-acceleration curve removed, which is what a first-person look
    wants — with acceleration on, a fast flick turns further than a slow one over the same desk distance,
    which is most of what players describe as a jumpy look. Settle semantics are unchanged: true on
    pointerlockchange, false on pointerlockerror, IGX-0809 on a headless app.

    Public API change: InputAction.activeDevice: DeviceKind | null is new. No signature changed, but
    the meaning of <Mouse>/delta, <Pointer>/delta and <Touch>/…/delta did: they are CSS pixels
    now, so a project that tuned its sensitivity on a retina display re-tunes it once (invert and
    scale(...) processors still apply as before).

  • Updated dependencies [388b0f6]

    • @ignifx/core@0.2.1

@ignifx/physics@0.2.1

Patch Changes

  • 6a39fae: The interpolated display pose is written before update, so camera rigs and scripts see the pose the frame draws

    Poses are still snapshotted on the fixed step and interpolated with time.fixedStepAlpha, but the
    system that writes lerp(prev, cur, alpha) into a Rigidbody's or CharacterController's node
    moved from Systems(PreRender, −500) to Systems(Update, −900) — straight after the fixed loop and
    lifecycle flush B, in front of scripts.update.

    Why: ThirdPersonCamera, FirstPersonController, bone attachments and every hand-written follow camera
    run in update/lateUpdate, which used to be before the display pose existed. They framed the
    character where the last fixed step left it while the renderer drew it at lerp(prev, cur, alpha),
    so at any refresh rate the character juddered against the camera by up to one fixed step of motion —
    about 0.1 m at sprint speed, every frame. Writing the pose at the top of Update means update,
    lateUpdate, animation, camera rigs and the render sync all read the same pose the frame presents.
    This is Unity's model.

    What a game author sees: a smooth third- or first-person camera, and transform.position read from
    update/lateUpdate now returning the interpolated pose rather than the last fixed one. The
    simulation is unchanged: the restore system at FixedUpdate −100 still runs before
    scripts.fixedUpdate, so fixedUpdate and Havok only ever see authoritative poses, and a kinematic
    body moved by writing its transform behaves exactly as before — the restore already overwrote such a
    write before the step read it. A system that must read an authoritative pose outside the fixed loop
    registers at Phase.Update with an order below −900.

    Public API change: none. The order constant and the interpolation system are @internal.

  • 6a39fae: shapeCast can sweep past one body, and the third-person boom and step probe no longer report the character's own capsule

    Lite's ShapeCastQuery carries no collision masks — unlike physicsRaycast — so a shape sweep
    cannot be filtered by layer inside Havok; layerMask only ever decided which hit was attributed
    an entity, and a body outside the mask still stopped the sweep, reported with entity: null. Two
    callers in @ignifx/3d were sweeping from inside the character's own capsule and were stopped by it
    at fraction zero every time:

    • ThirdPersonCamera sweeps from its shoulder pivot, which sits inside the capsule, out along the
      boom. For every yaw whose boom crossed the capsule the sweep hit it immediately, currentDistance
      collapsed to 0 and the camera sat inside the character's head — measured on 2026-09-08 in the
      third-person template at every yaw from 30° to 180° at the spawn, with no wall anywhere near.
      collisionLayers: ["Level", "Prop"] could not prevent it, because the mask never reached the sweep.
    • ThirdPersonController's step probe sweeps a sphere forward from the character's feet; both of its
      sweeps found the capsule first, so stepHeight never lifted anything.

    ShapeCastOptions — QueryOptions plus ignore?: Entity | null — names the one body the sweep
    passes through, resolved to its Havok body whether the entity is a Rigidbody, a collider-only
    static, or a CharacterController (whose capsule body Lite exposes through getBody()). The camera
    rig ignores its target; the step probe ignores its own entity. A masked-out body other than the
    ignored one still shortens the boom, which for scenery is the point and is now said so on
    collisionLayers.

    What a game author sees: a third-person camera that holds its distance through a full orbit and a
    stepHeight that climbs steps; and, for their own queries, a way to sweep out of a body they are
    standing in.

    Public API change: ShapeCastOptions is new and PhysicsService.shapeCast takes it in place of
    QueryOptions (a widening — every existing call compiles). Nothing was removed or renamed.

  • Updated dependencies [388b0f6]

    • @ignifx/core@0.2.1

@ignifx/physics-2d@0.2.1

Patch Changes

  • 6a39fae: A CharacterController2D walks through triggers instead of bouncing off them, and the display pose is written before update so follow cameras stop juddering

    Triggers were walls. Rapier's KinematicCharacterController.computeColliderMovement takes
    filterFlags as its third argument, and the adapter passed undefined, which leaves sensors in the
    obstacle set. Measured on 2026-09-08 against @dimforge/rapier2d-compat@0.20.0 (macOS arm64,
    Node 24): a kinematic box driven by the controller towards a static sensor ball stops dead at the
    sensor's surface — x = 0.49 for a sensor at x = 1 with radius 0.3, a 0.2 half-width character and
    a 0.01 offset — and because it never overlaps, drainCollisionEvents reports nothing at all. With
    QueryFilterFlags.EXCLUDE_SENSORS the same character reaches x = 3.0 and the queue reports the
    started/stopped pair. Every controller move now passes that flag.

    What a game author sees: a collectible finally works. The side-scroller template's coins are a
    CircleCollider2D { isTrigger: true } on an entity with no rigidbody, and the player is a
    CharacterController2D — so every coin was an invisible bump the player stopped against and
    onTriggerEnter never fired. The same was true of the top-down template's shrine pads and of any
    trigger zone a controller-driven character is meant to enter. The flip side is intended: a sensor no
    longer appears in CharacterController2D.onCollided, because a trigger is not an obstacle. Use
    onTriggerEnter/onTriggerExit for pickups and zones and onCollided for the walls and floors the
    character actually pushed against.

    The display pose is written at the top of Update, not in PreRender. Poses are still
    snapshotted on the fixed step and interpolated with time.fixedStepAlpha, but the system that writes
    lerp(prev, cur, alpha) moved from Systems(PreRender, −500) to Systems(Update, −900), which runs
    straight after the fixed loop and before scripts.update. Camera2DFollow and every hand-written
    follow camera run in update/lateUpdate, so they used to frame the character where the last fixed
    step left it while the renderer drew it interpolated: up to one fixed step of relative motion — about
    0.1 m at sprint speed — of judder every frame, and with the pixel-perfect camera the two poses are
    quantised independently, so the sprite visibly shook while running. Now scripts, animation, camera
    rigs and the render sync all read the pose the frame presents, which is Unity's model. fixedUpdate
    and Rapier are unaffected: the restore system at FixedUpdate −100 still runs before
    scripts.fixedUpdate, so the simulation only ever sees authoritative poses, and a kinematic body
    moved by writing its transform behaves exactly as before.

    Public API change: none. The order constants and both systems are @internal.

  • Updated dependencies [388b0f6]

    • @ignifx/core@0.2.1
    • @ignifx/2d@0.2.1

@ignifx/ui@0.2.1

Patch Changes

  • Updated dependencies [21a4ba7]
  • Updated dependencies [388b0f6]
    • @ignifx/input@0.2.1
    • @ignifx/core@0.2.1

@ignifx/vite-plugin@0.2.1

Patch Changes

  • Updated dependencies [388b0f6]
    • @ignifx/core@0.2.1

@ignifx/cli@0.2.1

No changes in this release.

@DavidArayan
DavidArayan merged commit 9c77ccb into main Sep 8, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant