Ajoute un son de fusion dans La Corne d'Abondance
Build and deploy / deploy (push) Successful in 36s
Build and deploy / deploy (push) Successful in 36s
Pas de fichier audio dans le projet, donc un petit "pop" synthétisé via Web Audio (src/lib/melon/sound.ts), joué à chaque fusion. La hauteur baisse avec le palier de la pièce obtenue, pour que fusionner deux grosses pièces sonne différemment de deux petites. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
MAX_FRAME_DT_MS,
|
||||
WALL_THICKNESS,
|
||||
} from "@/lib/melon/constants";
|
||||
import { playMergeSound } from "@/lib/melon/sound";
|
||||
import type { MemberRow } from "./page";
|
||||
|
||||
type Status = "playing" | "dead";
|
||||
@@ -329,10 +330,12 @@ export function MelonGame({
|
||||
const newTier = merge.tier + 1;
|
||||
scoreRef.current += newTier;
|
||||
spawned.push(spawnPiece(newTier, merge.x, merge.y));
|
||||
playMergeSound(newTier, maxTier);
|
||||
} else {
|
||||
// Palier maximum déjà atteint : pas de pièce plus grande, juste
|
||||
// un bonus de score pour la fusion finale.
|
||||
scoreRef.current += maxTier * 2;
|
||||
playMergeSound(maxTier, maxTier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Petit "pop" synthétisé via Web Audio (pas de fichier audio dans le
|
||||
// projet, cohérent avec le reste de l'app qui dessine tout plutôt que de
|
||||
// dépendre d'assets externes — voir les ornements SVG/canvas ailleurs).
|
||||
// AudioContext créé paresseusement au premier appel : le construire avant
|
||||
// une interaction utilisateur déclenche des avertissements navigateur
|
||||
// (autoplay policy) — ici le premier merge n'arrive jamais avant un
|
||||
// premier lâcher, donc toujours après un vrai geste utilisateur.
|
||||
let audioContext: AudioContext | null = null;
|
||||
|
||||
function getAudioContext(): AudioContext | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
if (!audioContext) {
|
||||
const Ctor =
|
||||
window.AudioContext ??
|
||||
(window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
|
||||
if (!Ctor) return null;
|
||||
audioContext = new Ctor();
|
||||
}
|
||||
if (audioContext.state === "suspended") {
|
||||
audioContext.resume().catch(() => {});
|
||||
}
|
||||
return audioContext;
|
||||
}
|
||||
|
||||
// Fréquence plus grave à mesure que le palier de la pièce issue de la
|
||||
// fusion monte : deux petites pièces qui fusionnent ne doit pas "sonner"
|
||||
// comme deux grosses.
|
||||
export function playMergeSound(tier: number, maxTier: number): void {
|
||||
const ctx = getAudioContext();
|
||||
if (!ctx) return;
|
||||
|
||||
const progress = maxTier > 0 ? Math.min(1, Math.max(0, tier / maxTier)) : 0;
|
||||
const baseFreq = 720 - progress * 380;
|
||||
const now = ctx.currentTime;
|
||||
|
||||
const oscillator = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
oscillator.type = "sine";
|
||||
oscillator.frequency.setValueAtTime(baseFreq, now);
|
||||
oscillator.frequency.exponentialRampToValueAtTime(baseFreq * 1.6, now + 0.08);
|
||||
|
||||
gain.gain.setValueAtTime(0.0001, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.25, now + 0.01);
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.18);
|
||||
|
||||
oscillator.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
oscillator.start(now);
|
||||
oscillator.stop(now + 0.2);
|
||||
}
|
||||
Reference in New Issue
Block a user