Musica, vecchietti con sprite, nuove scene del paese e generatore degli sfondi
Lavoro della sessione precedente: colonna sonora (titolo, paese, ricordi) con muto e volume, vecchietta e vecchietto sulla panchina, scene di Pieve, oratorio, Via de' Medici, Villa Adami, salita, campeggio, Bosco ai Frati e viale dei cipressi, panorama dell'introduzione, tools/genera.py per gli sfondi dalle foto. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdeKd62opy9PKgpQXtVctF
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// Colonna sonora: un brano alla volta, in loop, con dissolvenza quando cambia.
|
||||
// Quale brano suona lo decidono i dati (story.music e scene.music). Si prova a partire subito; se il browser non lo
|
||||
// permette prima di un gesto del giocatore, il brano riparte al primo clic o tasto (unlock).
|
||||
|
||||
// Il brano del momento: titolo, fine, oppure quello della scena (o quello comune del gioco).
|
||||
export function trackFor(story, { screen, scene }) {
|
||||
const m = story.music ?? {};
|
||||
if (screen === 'title' || screen === 'intro') return m.title ?? null;
|
||||
if (screen === 'ending') return m.ending ?? m.game ?? null;
|
||||
return story.scenes[scene]?.music ?? m.game ?? null;
|
||||
}
|
||||
|
||||
const FADE = 1.2;
|
||||
|
||||
export class Music {
|
||||
constructor({ muted = false, volume = 0.6 } = {}) {
|
||||
this.muted = muted;
|
||||
this.volume = volume;
|
||||
this.blocked = null;
|
||||
this.want = null;
|
||||
this.current = null; // { src, audio }
|
||||
this.old = [];
|
||||
}
|
||||
|
||||
// Chiamata a ogni fotogramma con il brano voluto: avvia, cambia e sfuma.
|
||||
update(src, dt) {
|
||||
this.want = src;
|
||||
if (src !== this.current?.src) {
|
||||
if (this.current) this.old.push(this.current.audio);
|
||||
this.current = src ? { src, audio: this.start(src) } : null;
|
||||
}
|
||||
// Il brano in corso sale (o scende) verso il volume scelto; quelli vecchi sfumano fino a zero.
|
||||
const step = dt / FADE, target = this.muted ? 0 : this.volume;
|
||||
if (this.current) {
|
||||
const a = this.current.audio;
|
||||
a.volume = a.volume < target ? Math.min(target, a.volume + step) : Math.max(target, a.volume - step);
|
||||
}
|
||||
for (const a of this.old) a.volume = Math.max(0, a.volume - step);
|
||||
this.old = this.old.filter(a => a.volume > 0 || (a.pause(), false));
|
||||
}
|
||||
|
||||
start(src) {
|
||||
const audio = new Audio(src);
|
||||
audio.loop = true;
|
||||
audio.volume = 0;
|
||||
audio.play().catch(() => { this.blocked = audio; }); // senza permesso si riprova al primo gesto
|
||||
return audio;
|
||||
}
|
||||
|
||||
unlock() {
|
||||
const a = this.blocked;
|
||||
this.blocked = null;
|
||||
if (a && a === this.current?.audio) a.play().catch(() => {});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.muted = !this.muted;
|
||||
return this.muted;
|
||||
}
|
||||
|
||||
setVolume(v) {
|
||||
this.volume = Math.max(0, Math.min(1, v));
|
||||
if (this.volume > 0) this.muted = false;
|
||||
return this.volume;
|
||||
}
|
||||
|
||||
settings() { return { volume: this.volume, muted: this.muted }; }
|
||||
}
|
||||
Reference in New Issue
Block a user