This document describes the sound system in Fluffy-Swizz Interactive, including audio management, implementation details, and best practices.
The game uses a centralized SoundManager class to handle all audio playback.
Methods:
constructor(scene)- Initializes the sound manager for a sceneinit(options)- Sets up sound manager with configuration optionsplaySound(key, config)- Plays a sound effect with optional configurationplayMusic(key, config)- Plays background music with optional configurationstopMusic(key)- Stops specific background musicstopAllMusic()- Stops all background musicfadeMusic(key, duration)- Fades out specific music trackcrossFadeMusic(fromKey, toKey, duration)- Smoothly transitions between trackssetMasterVolume(volume)- Sets overall volume for all soundssetSoundVolume(volume)- Sets volume for sound effects onlysetMusicVolume(volume)- Sets volume for music onlymute()- Mutes all audiounmute()- Unmutes all audiotoggleMute()- Toggles mute statedestroy()- Cleans up resources when scene is destroyed
Properties:
sounds- Map of all loaded sound effectsmusic- Map of all loaded music tracksoptions- Configuration optionsisMuted- Current mute statemasterVolume- Overall volume level (0-1)soundVolume- Sound effects volume level (0-1)musicVolume- Music volume level (0-1)
The sound manager is initialized in each scene that requires audio:
// In scene's create method
this.soundManager = new SoundManager(this);
this.soundManager.init({
masterVolume: 0.8,
soundVolume: 1.0,
musicVolume: 0.6,
defaultSoundConfig: {
rate: 1,
detune: 0,
seek: 0,
loop: false,
delay: 0
},
defaultMusicConfig: {
rate: 1,
detune: 0,
seek: 0,
loop: true,
delay: 0,
volume: 0.6
}
});Sound effects are played using the playSound method:
// Simple sound playback
this.soundManager.playSound('explosion');
// With configuration
this.soundManager.playSound('gunshot', {
volume: 0.8,
rate: 1.2, // Slightly higher pitch
detune: 0,
seek: 0,
loop: false,
delay: 0
});
// With position-based volume and panning
const distance = Phaser.Math.Distance.Between(
this.player.x, this.player.y,
x, y
);
const maxDistance = 500;
const volume = Math.max(0, 1 - (distance / maxDistance));
const pan = Phaser.Math.Clamp((x - this.player.x) / maxDistance, -1, 1);
this.soundManager.playSound('explosion', {
volume: volume,
pan: pan
});Background music is played using the playMusic method:
// Start background music
this.soundManager.playMusic('gameplay_theme');
// Crossfade to boss music
this.soundManager.crossFadeMusic('gameplay_theme', 'boss_theme', 2000);To prevent repetitive sounds, the manager supports sound variation:
// Play a random variation of a sound
playRandomizedSound(baseKey, variations = 3, config = {}) {
const randomIndex = Math.floor(Math.random() * variations) + 1;
const soundKey = `${baseKey}_${randomIndex}`;
return this.playSound(soundKey, config);
}
// Example usage
this.soundManager.playRandomizedSound('footstep', 4, { volume: 0.5 });player_hit- Player takes damageenemy_hit- Enemy takes damageenemy_death- Enemy is defeatedminigun_fire- Minigun weapon firingshotgun_fire- Shotgun weapon firingreload- Weapon reloadpickup_xp- Collecting XP orbpickup_cash- Collecting cashpickup_health- Collecting healthlevel_up- Player levels upwave_start- New wave beginswave_complete- Wave is completedgame_over- Player is defeatedvictory- Player wins (wave mode)button_click- UI button interaction
menu_music- Main menu themegameplay_music- Standard gameplayboss_music- Boss encountersvictory_music- Victory screengame_over_music- Game over screen
The game supports multiple audio formats for cross-browser compatibility:
// In Preloader.js
preload() {
// Load sound effects with fallbacks
this.load.audio('explosion', [
'assets/audio/explosion.mp3',
'assets/audio/explosion.ogg'
]);
// Load music with fallbacks
this.load.audio('gameplay_music', [
'assets/audio/gameplay_music.mp3',
'assets/audio/gameplay_music.ogg'
]);
// Additional audio assets...
}The sound manager implements audio pooling to prevent performance issues with many simultaneous sounds:
playPooledSound(key, maxInstances = 4, config = {}) {
// Check if we've reached the maximum instances for this sound
if (!this.soundInstances[key]) {
this.soundInstances[key] = 0;
}
if (this.soundInstances[key] >= maxInstances) {
// Find the oldest instance and stop it
const oldestSound = this.activeSounds[key].shift();
if (oldestSound && oldestSound.isPlaying) {
oldestSound.stop();
}
} else {
this.soundInstances[key]++;
}
// Play the sound
const sound = this.playSound(key, config);
// Add to active sounds list
if (!this.activeSounds[key]) {
this.activeSounds[key] = [];
}
this.activeSounds[key].push(sound);
// Set up cleanup when sound finishes
sound.once('complete', () => {
this.soundInstances[key]--;
const index = this.activeSounds[key].indexOf(sound);
if (index !== -1) {
this.activeSounds[key].splice(index, 1);
}
});
return sound;
}For better performance and realism, sounds are attenuated based on distance from the player:
playPositionalSound(key, x, y, config = {}) {
// Calculate distance from player
const distance = Phaser.Math.Distance.Between(
this.scene.player.x, this.scene.player.y,
x, y
);
// Set maximum audible distance
const maxDistance = config.maxDistance || 800;
// If beyond max distance, don't play the sound
if (distance > maxDistance) {
return null;
}
// Calculate volume based on distance (inverse linear falloff)
const volume = Math.max(0, 1 - (distance / maxDistance));
// Calculate stereo pan (-1 to 1) based on position relative to player
const pan = Phaser.Math.Clamp(
(x - this.scene.player.x) / (maxDistance / 2),
-1, 1
);
// Merge with provided config
const soundConfig = {
...config,
volume: volume * (config.volume || 1),
pan: pan
};
// Play the sound with calculated parameters
return this.playSound(key, soundConfig);
}If sounds aren't playing when a scene first loads but work after reloading:
-
Check initialization order:
- SoundManager must be initialized before sounds are played
- Ensure assets are fully loaded before attempting playback
-
Browser autoplay policies:
- Modern browsers require user interaction before playing audio
- Add a "Click to Start" button for initial user interaction
If multiple music tracks play at once:
-
Check for proper stopping:
- Always stop current music before starting new tracks
- Use
stopAllMusic()when changing scenes
-
Use crossfading:
- The SoundManager handles crossfading between tracks
- Ensure only one music track is playing at a time
If audio doesn't play in certain browsers:
- Provide multiple formats:
- Include both MP3 and OGG versions of audio files
- Different browsers support different audio formats
This documentation is maintained by the Fluffy-Swizz Interactive development team.