Fix UI camera split going stale after a scene restart
A jump cut destroys all cameras and rebuilds a fresh main, leaving the stored split pointing at dead cameras. Worse, `main.ignore()` on the old main sets the id bit that the new main reuses, so objects assigned to the UI pass get ignored by the only live pass — rendering nowhere (the "clip plays in the background" bug). - Add `liveSplit()` which validates the stored split against the scene's live camera list and clears it when stale - Route `ensureUiCameras`, `assignUi`, and `assignWorld` through `liveSplit()` so a stale split is dropped and the pipeline falls back to a single pass
This commit is contained in:
parent
332431ecb5
commit
098b41b2fc
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue