Il flipper alleggerito senza cambiarne l'aspetto: il mobile statico sullo sfondo CSS della tela invece di una copia a tutto schermo per fotogramma, estremità e giunti arrotondati solo nei livelli in cache; arretrato grafico da 4,2 s a 0,03 s per 150 fotogrammi; la misura dell'arretrato nella prova nel browser al posto dell'intervallo fra frame, che non misurava nulla (Andrea)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
This commit is contained in:
@@ -44,15 +44,31 @@ function glow(g, x, y, radius, color, alpha = 1) {
|
||||
g.save(); g.globalAlpha *= alpha; g.globalCompositeOperation = 'screen'; g.drawImage(sprite, x - radius, y - radius, radius * 2, radius * 2); g.restore();
|
||||
}
|
||||
const layers = new Map();
|
||||
function cached(g, key, paint, bounds = [0, 0, 640, 360]) {
|
||||
if (typeof document === 'undefined') { paint(g); return; }
|
||||
function layerOf(key, paint, bounds) {
|
||||
let layer = layers.get(key);
|
||||
if (!layer) {
|
||||
layer = Object.assign(document.createElement('canvas'), { width: bounds[2] * 2, height: bounds[3] * 2 });
|
||||
const l = layer.getContext('2d'); l.scale(2, 2); l.translate(-bounds[0], -bounds[1]); paint(l); layers.set(key, layer);
|
||||
}
|
||||
g.drawImage(layer, ...bounds);
|
||||
return layer;
|
||||
}
|
||||
function cached(g, key, paint, bounds = [0, 0, 640, 360]) {
|
||||
if (typeof document === 'undefined') { paint(g); return; }
|
||||
g.drawImage(layerOf(key, paint, bounds), ...bounds);
|
||||
}
|
||||
// Il mobile, che non cambia mai, sta sullo sfondo CSS della tela in pagina invece di essere copiato a ogni fotogramma:
|
||||
// una copia a tutto schermo fra gli altri tratti costava da sola mezzo secondo ogni 150 fotogrammi (swiftshader). La
|
||||
// tela si pulisce e il mobile resta sotto; si ridisegna sulla tela solo durante la scossa, che lo sposta.
|
||||
const sheets = new Map();
|
||||
function cabinetBehind(g, key, paint) {
|
||||
const canvas = g.canvas;
|
||||
if (typeof document === 'undefined' || !(canvas instanceof HTMLCanvasElement) || !canvas.isConnected) return false;
|
||||
let url = sheets.get(key);
|
||||
if (!url) { url = `url(${layerOf(key, paint, [0, 0, 640, 360]).toDataURL()})`; sheets.set(key, url); }
|
||||
if (canvas.dataset.cabinet !== key) { canvas.style.backgroundImage = url; canvas.style.backgroundSize = '100% 100%'; canvas.dataset.cabinet = key; }
|
||||
return true;
|
||||
}
|
||||
export function cabinetAway(canvas) { canvas.style.backgroundImage = ''; canvas.style.backgroundSize = ''; delete canvas.dataset.cabinet; }
|
||||
function crown(g, x, y, size, color) {
|
||||
poly(g, [[x - size, y + size / 2], [x - size * 1.1, y - size / 2], [x - size / 2, y], [x, y - size], [x + size / 2, y], [x + size * 1.1, y - size / 2], [x + size, y + size / 2]], color);
|
||||
line(g, [[x - size, y + size * 0.8], [x + size, y + size * 0.8]], color, 1);
|
||||
@@ -357,10 +373,14 @@ function effects(g, c, time) {
|
||||
}
|
||||
export function drawFlipper(g, c, def, bg, glass) {
|
||||
const time = c.visual.time;
|
||||
g.save(); g.imageSmoothingEnabled = true; g.lineCap = 'round'; g.lineJoin = 'round';
|
||||
// Niente estremità e giunti arrotondati nei tratti di ogni fotogramma: costano più di tutto il resto (vedi la
|
||||
// memoria «effetti leggeri»); il mobile e i binari, in cache, li tengono.
|
||||
g.save(); g.imageSmoothingEnabled = true; g.lineCap = 'butt'; g.lineJoin = 'miter';
|
||||
const shake = c.fx.reduce((n, f) => f.kind === 'shake' ? Math.max(n, 1 - (time - f.t) / 0.3) : n, 0);
|
||||
if (shake > 0) { g.fillStyle = INK; g.fillRect(0, 0, 640, 360); g.translate(Math.sin(time * 75) * shake * 1.3, Math.cos(time * 63) * shake); }
|
||||
cached(g, `cabinet:${!!bg}:${!!glass}`, layer => cabinet(layer, c, bg, glass));
|
||||
const key = `cabinet:${!!bg}:${!!glass}`, paint = layer => cabinet(layer, c, bg, glass);
|
||||
if (shake > 0) { g.fillStyle = INK; g.fillRect(0, 0, 640, 360); g.translate(Math.sin(time * 75) * shake * 1.3, Math.cos(time * 63) * shake); cached(g, key, paint); }
|
||||
else if (cabinetBehind(g, key, paint)) g.clearRect(0, 0, 640, 360);
|
||||
else cached(g, key, paint);
|
||||
for (const [i, l] of c.lanes.entries()) {
|
||||
if (l.lit) glow(g, l.x, l.y, 17, RIONI[i], 0.55);
|
||||
disc(g, l.x, l.y, 5, '#06151d', l.lit ? RIONI[i] : '#47616b');
|
||||
@@ -378,7 +398,6 @@ export function drawFlipper(g, c, def, bg, glass) {
|
||||
const color = [TINT.sinistra, TINT.destra, TINT.super][i];
|
||||
b.trail.forEach(([x, y], k) => { g.save(); g.globalAlpha = k / b.trail.length * 0.35; disc(g, x, y - 1, 1 + k / b.trail.length * 2, color); g.restore(); });
|
||||
}
|
||||
// Ogni rampa copre gli oggetti sotto; la sua pallina si disegna sul proprio piano.
|
||||
for (const b of balls) if (!b.ramp && b.releaseAt <= c.sim) ball(g, b);
|
||||
for (const r of c.ramps) {
|
||||
if (!rampOpen(c, r)) continue;
|
||||
|
||||
Reference in New Issue
Block a user