feat: Add SoundManager for bg music and opendoor sfx
This commit is contained in:
@@ -19,6 +19,11 @@
|
||||
- **Corrección de "Diagonal Leaking"**: Solucionado el problema donde los disparos atravesaban esquinas diagonales entre muros (se verifican ambos vecinos en cruces de vértice).
|
||||
- **Detección de Muros por Conectividad**: Reemplazada la comprobación simple de vacío por `canMoveBetween`, asegurando que los muros entre habitaciones/pasillos contiguos bloquen la visión correctamente si no hay puerta, incluso si ambas celdas tienen suelo.
|
||||
|
||||
3. **Sistema de Audio**:
|
||||
- Implementado `SoundManager` para gestión centralizada de audio.
|
||||
- **Música Ambiental**: Reproducción de `Abandoned_Ruins.mp3` con loop y manejo de políticas de autoplay del navegador.
|
||||
- **Efectos de Sonido (SFX)**: Gatillo de sonido `opendoor.mp3` sincronizado con la apertura visual de puertas.
|
||||
|
||||
### Estado Actual
|
||||
El juego cuenta con una visualización táctica profesional y un sistema de línea de visión robusto y justo, eliminando los fallos de detección en esquinas y muros.
|
||||
|
||||
|
||||
BIN
public/assets/music/ingame/Abandoned_Ruins.mp3
Normal file
BIN
public/assets/music/ingame/Abandoned_Ruins.mp3
Normal file
Binary file not shown.
BIN
public/assets/sfx/opendoor.mp3
Normal file
BIN
public/assets/sfx/opendoor.mp3
Normal file
Binary file not shown.
@@ -2,6 +2,7 @@ import { GameEngine } from './engine/game/GameEngine.js';
|
||||
import { GameRenderer } from './view/GameRenderer.js';
|
||||
import { CameraManager } from './view/CameraManager.js';
|
||||
import { UIManager } from './view/UIManager.js';
|
||||
import { SoundManager } from './view/SoundManager.js';
|
||||
import { MissionConfig, MISSION_TYPES } from './engine/dungeon/MissionConfig.js';
|
||||
|
||||
|
||||
@@ -19,10 +20,15 @@ const renderer = new GameRenderer('app');
|
||||
const cameraManager = new CameraManager(renderer);
|
||||
const game = new GameEngine();
|
||||
const ui = new UIManager(cameraManager, game);
|
||||
const soundManager = new SoundManager();
|
||||
|
||||
// Start Music (Autoplay handling included in manager)
|
||||
soundManager.playMusic('exploration');
|
||||
|
||||
// Global Access
|
||||
window.GAME = game;
|
||||
window.RENDERER = renderer;
|
||||
window.SOUND_MANAGER = soundManager;
|
||||
|
||||
// 3. Connect Dungeon Generator to Renderer
|
||||
const generator = game.dungeon;
|
||||
@@ -222,6 +228,7 @@ const handleClick = (x, y, doorMesh) => {
|
||||
|
||||
// Open door visually
|
||||
renderer.openDoor(doorMesh);
|
||||
if (window.SOUND_MANAGER) window.SOUND_MANAGER.playSound('door_open');
|
||||
|
||||
// Get proper exit data with direction
|
||||
const exitData = doorMesh.userData.exitData;
|
||||
|
||||
115
src/view/SoundManager.js
Normal file
115
src/view/SoundManager.js
Normal file
@@ -0,0 +1,115 @@
|
||||
|
||||
export class SoundManager {
|
||||
constructor() {
|
||||
this.musicVolume = 0.3; // Default volume (not too loud)
|
||||
this.sfxVolume = 0.5;
|
||||
this.currentMusic = null;
|
||||
this.isMuted = false;
|
||||
|
||||
// Asset Library
|
||||
this.assets = {
|
||||
music: {
|
||||
'exploration': '/assets/music/ingame/Abandoned_Ruins.mp3'
|
||||
},
|
||||
sfx: {
|
||||
'door_open': '/assets/sfx/opendoor.mp3'
|
||||
}
|
||||
};
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the audio context if needed (browser restriction handling)
|
||||
* Can be called on the first user interaction (click)
|
||||
*/
|
||||
init() {
|
||||
if (this.initialized) return;
|
||||
this.initialized = true;
|
||||
console.log("[SoundManager] Audio System Initialized");
|
||||
}
|
||||
|
||||
playMusic(key) {
|
||||
if (this.isMuted) return;
|
||||
|
||||
const url = this.assets.music[key];
|
||||
if (!url) {
|
||||
console.warn(`[SoundManager] Music track not found: ${key}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If same track is playing, do nothing
|
||||
if (this.currentMusic && this.currentMusic.src.includes(url) && !this.currentMusic.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop current
|
||||
this.stopMusic();
|
||||
|
||||
// Start new
|
||||
this.currentMusic = new Audio(url);
|
||||
this.currentMusic.loop = true;
|
||||
this.currentMusic.volume = this.musicVolume;
|
||||
|
||||
// Handle autoplay promises
|
||||
const playPromise = this.currentMusic.play();
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(error => {
|
||||
console.log("[SoundManager] Autoplay prevented. Waiting for user interaction.");
|
||||
// We can add a one-time click listener to window to resume if needed
|
||||
const resume = () => {
|
||||
this.currentMusic.play();
|
||||
window.removeEventListener('click', resume);
|
||||
window.removeEventListener('keydown', resume);
|
||||
};
|
||||
window.addEventListener('click', resume);
|
||||
window.addEventListener('keydown', resume);
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[SoundManager] Playing music: ${key}`);
|
||||
}
|
||||
|
||||
stopMusic() {
|
||||
if (this.currentMusic) {
|
||||
this.currentMusic.pause();
|
||||
this.currentMusic.currentTime = 0;
|
||||
this.currentMusic = null;
|
||||
}
|
||||
}
|
||||
|
||||
setMusicVolume(vol) {
|
||||
this.musicVolume = Math.max(0, Math.min(1, vol));
|
||||
if (this.currentMusic) {
|
||||
this.currentMusic.volume = this.musicVolume;
|
||||
}
|
||||
}
|
||||
|
||||
toggleMute() {
|
||||
this.isMuted = !this.isMuted;
|
||||
if (this.isMuted) {
|
||||
if (this.currentMusic) this.currentMusic.pause();
|
||||
} else {
|
||||
if (this.currentMusic) this.currentMusic.play();
|
||||
}
|
||||
return this.isMuted;
|
||||
}
|
||||
|
||||
playSound(key) {
|
||||
if (this.isMuted) return;
|
||||
|
||||
const url = this.assets.sfx[key];
|
||||
if (!url) {
|
||||
console.warn(`[SoundManager] SFX not found: ${key}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const audio = new Audio(url);
|
||||
audio.volume = this.sfxVolume;
|
||||
// Fire and forget, but catch errors
|
||||
audio.play().catch(e => {
|
||||
// Check if error is NotAllowedError (autoplay) - silently ignore usually for SFX
|
||||
// or log if needed
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user