diff --git a/js/visuals/UiCameras.js b/js/visuals/UiCameras.js index e7f8407..44aa975 100644 --- a/js/visuals/UiCameras.js +++ b/js/visuals/UiCameras.js @@ -55,6 +55,44 @@ export function isScreenPinned(obj) { ); } +/** + * The stored split, validated against the LIVE camera list — or null + * (and cleared) when it is stale. + * + * A scene restart (the jump cut — GameScene._finishJump) runs the + * camera manager's shutdown, which DESTROYS every camera (fx-ui + * included) and re-adds a single fresh main. The stored split would + * then point at dead cameras, and worse: a later `main.ignore()` sets + * the OLD main's id bit on the object, and the re-created main reuses + * id 1 — so an object "assigned to the UI pass" is ignored by the ONLY + * live pass and drawn by no camera at all. That is the jump-clip + * "plays in the background" bug: after leaving an effects system, the + * next system's clip + opaque backdrop rendered nowhere while the world + * stayed fully visible. A split whose cameras are no longer in the + * scene's list is invalid — drop it so the pipeline falls back to a + * single pass (depth order restores the clip's topness) and a fresh + * split can be created if/when one is needed. + * + * @param {object} scene Phaser scene + * @returns {{ main: object, ui: object } | null} the live split, or null + */ +function liveSplit(scene) { + const split = scene._fxUiCameras; + const cams = scene.cameras?.cameras; + if ( + split && + split.ui && + split.main && + Array.isArray(cams) && + cams.includes(split.main) && + cams.includes(split.ui) + ) { + return split; + } + if (split) scene._fxUiCameras = null; // stale (a restart rebuilt the cameras) — forget it + return null; +} + /** * Create (once) the fixed UI camera and partition the scene's * top-level objects between the two passes. Idempotent — subsequent @@ -65,7 +103,7 @@ export function isScreenPinned(obj) { * the scene has no camera plugin (defensive). */ export function ensureUiCameras(scene) { - if (scene._fxUiCameras) return scene._fxUiCameras; + if (liveSplit(scene)) return scene._fxUiCameras; const cameras = scene.cameras; if (!cameras?.cameras || cameras.cameras.length === 0) return null; @@ -98,7 +136,7 @@ export function ensureUiCameras(scene) { * @param {object} child object to attach to the UI pass */ export function assignUi(scene, child) { - const split = scene._fxUiCameras; + const split = liveSplit(scene); if (split && child) split.main.ignore(child); } @@ -112,6 +150,6 @@ export function assignUi(scene, child) { * @param {object} child object to keep on the world pass */ export function assignWorld(scene, child) { - const split = scene._fxUiCameras; + const split = liveSplit(scene); if (split && child) split.ui.ignore(child); }