From c4c7d7119143f6d3b1c0f870911fbb78aac86bb1 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Mon, 13 Jul 2026 08:32:59 -0400 Subject: [PATCH] fix(camera): derive focus from last player pos when renderer focusPoint is missing The SP camera still intermittently pinned the whole level to the top-left at game start: the per-frame _applyCamera read renderer.focusPoint as null (a start-of-game race, or a tick reading a just-(re)created renderer instance before its first render) and fell back to a scale-only transform. Add a fallback: when focusPoint is absent, derive it from SP._lastState.player (tiled coords, cell=24) so the camera always has a target and centers the player. Also read host dims via clientW/H||offsetW/H for resilience. --- html/js/render/spGameRenderer.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/html/js/render/spGameRenderer.js b/html/js/render/spGameRenderer.js index 2234337..5563ff7 100644 --- a/html/js/render/spGameRenderer.js +++ b/html/js/render/spGameRenderer.js @@ -104,9 +104,18 @@ if (!r || !r.canvas || !host) return; var scale = SP._zoom || 1.7; var fp = r.focusPoint; + // ROBUSTNESS: if the renderer hasn't reported a focus this frame — a race at game start, or a + // camera tick reading a just-(re)created renderer instance before its first render — derive the + // focus straight from the last known player cell (tiled coords). Without this the camera falls + // back to a scale-only transform that pins the whole level to the top-left corner. + if (!fp && r.name === 'tiles' && SP._lastState && SP._lastState.player && + typeof SP._lastState.player.x === 'number') { + var _c = r.cell || 24; + fp = { x: (SP._lastState.player.x + 0.5) * _c, y: (SP._lastState.player.y + 0.5) * _c }; + } r.canvas.style.transformOrigin = '0 0'; if (!fp) { r.canvas.style.transform = 'scale(' + scale + ')'; return; } - var w = host.clientWidth || 640, h = host.clientHeight || 400; + var w = host.clientWidth || host.offsetWidth || 640, h = host.clientHeight || host.offsetHeight || 400; var tx = w / 2 - fp.x * scale, ty = h / 2 - fp.y * scale; // Smooth follow: glide toward the target so single steps aren't jerky; SNAP on a big jump // (level-change teleport / a stale-then-updated focus) rather than slowly panning the map.