diff --git a/AlientAttack.MonoGame/AlienAttackGame.cs b/AlientAttack.MonoGame/AlienAttackGame.cs index c1eea88..d41ed45 100644 --- a/AlientAttack.MonoGame/AlienAttackGame.cs +++ b/AlientAttack.MonoGame/AlienAttackGame.cs @@ -1,4 +1,5 @@ -using AlienAttack.MonoGame.GameLoops; +using AlienAttack.MonoGame.Audio; +using AlienAttack.MonoGame.GameLoops; using AlienAttack.MonoGame.View; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; @@ -20,6 +21,7 @@ public class AlienAttackGame : Game public SpriteBatch SpriteBatch => _spriteBatch; public ViewTransform ViewTransform => _viewTransform; + public AudioManager Audio { get; private set; } = new(); private const string SDL = "SDL2.dll"; @@ -47,27 +49,7 @@ public class AlienAttackGame : Game MaximizeWindow(); - PlayBackgroundMusic(); - } - - private void PlayBackgroundMusic() - { - Song song = Content.Load($@"Music\Untitled"); - - // Set whether the song should repeat when finished - MediaPlayer.IsRepeating = true; - - // Adjust the volume (0.0f to 1.0f) - MediaPlayer.Volume = 0.5f; - - // Check if the media player is already playing, if so, stop it - if (MediaPlayer.State == MediaState.Playing) - { - MediaPlayer.Stop(); - } - - // Start playing the background music - MediaPlayer.Play(song); + //Audio.PlayMusic(Content, @"Music\Untitled"); } private void ConfigureWindow() @@ -91,11 +73,16 @@ public class AlienAttackGame : Game { _spriteBatch = new SpriteBatch(GraphicsDevice); _gameLoop = new GameLoop(this); + // TODO: use this.Content to load your game content here + Audio.LoadContent(Content); } protected override void Update(GameTime gameTime) { + if (IsActive == false) + return; + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit(); diff --git a/AlientAttack.MonoGame/Audio/AudioManager.cs b/AlientAttack.MonoGame/Audio/AudioManager.cs new file mode 100644 index 0000000..d62d6b8 --- /dev/null +++ b/AlientAttack.MonoGame/Audio/AudioManager.cs @@ -0,0 +1,284 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Audio; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Media; +using System; + +namespace AlienAttack.MonoGame.Audio; + +public sealed class AudioManager +{ + public float MasterVolume { get; set; } = 1f; + public float SfxVolume { get; set; } = 0.8f; + public float MusicVolume { get; set; } = 0.7f; + + private readonly Random _random = new(12345); + + // Pools + private VariantSoundPool _playerGunPool = default!; + private VariantSoundPool _enemyGunPool = default!; + private VariantSoundPool _explosionPool = default!; + private VariantSoundPool _impactPool = default!; + + // Rate limiters (global caps per category/event) + private RateLimiter _playerGunLimiter = new(0.06f); // max ~16 plays/sec + private RateLimiter _enemyGunLimiter = new(0.12f); // max ~8 plays/sec (global for all enemies) + private RateLimiter _impactLimiter = new(0.04f); + + // Music + private Song? _currentSong; + private float _musicDuck = 1f; // 1.0 = normal, <1 = ducked + private float _musicDuckTarget = 1f; + private float _musicDuckSpeed = 6f; // how fast it moves toward target (bigger = snappier) + + public void LoadContent(ContentManager content) + { + // Load SoundEffects once + LoadPlayerGunPool(content); + LoadEnemyGunPool(content); + LoadImpactPool(content); + LoadExplosionPool(content); + + //var playerGun = content.Load("Audio/Sfx/player_fire"); + //var enemyGun = content.Load("Audio/Sfx/enemy_fire"); + //var explosion = content.Load("Audio/Sfx/explosion"); + //var impact = content.Load("Audio/Sfx/impact"); + + // Create pools with sane voice caps + //_playerGunPool = new SoundPool(playerGun, maxVoices: 2); + //_enemyGunPool = new SoundPool(enemyGun, maxVoices: 3); + //_explosionPool = new SoundPool(explosion, maxVoices: 6); + //_impactPool = new SoundPool(impact, maxVoices: 4); + } + + private void LoadPlayerGunPool(ContentManager content) + { + var playerGunVariants = new[] + { + content.Load("Sfx/GUNAuto_Assault Rifle A Fire_01_SFRMS_SCIWPNS"), + content.Load("Sfx/GUNAuto_Assault Rifle A Fire_02_SFRMS_SCIWPNS"), + content.Load("Sfx/GUNAuto_Assault Rifle A Fire_03_SFRMS_SCIWPNS"), + content.Load("Sfx/GUNAuto_Assault Rifle A Fire_04_SFRMS_SCIWPNS"), + content.Load("Sfx/GUNAuto_Assault Rifle A Fire_05_SFRMS_SCIWPNS"), + content.Load("Sfx/GUNAuto_Assault Rifle A Fire_06_SFRMS_SCIWPNS") + }; + + _playerGunPool = new VariantSoundPool(playerGunVariants, voicesPerVariant: 1, rng: _random); + } + + private void LoadEnemyGunPool(ContentManager content) + { + var variants = new[] + { + content.Load("Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS"), + content.Load("Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS"), + content.Load("Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS"), + content.Load("Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS"), + content.Load("Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS"), + content.Load("Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS"), + }; + + _enemyGunPool = new VariantSoundPool(variants, voicesPerVariant: 1, rng: _random); + } + + private void LoadImpactPool(ContentManager content) + { + var variants = new[] + { + content.Load("Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS"), + content.Load("Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS"), + content.Load("Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS"), + content.Load("Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS"), + content.Load("Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS"), + content.Load("Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS"), + }; + + _impactPool = new VariantSoundPool(variants, voicesPerVariant: 1, rng: _random); + } + + private void LoadExplosionPool(ContentManager content) + { + var variants = new[] + { + content.Load("Sfx/Explosions/EXPLDsgn_Explosion Impact_01_SFRMS_SCIWPNS"), + content.Load("Sfx/Explosions/EXPLDsgn_Explosion Impact_02_SFRMS_SCIWPNS"), + content.Load("Sfx/Explosions/EXPLDsgn_Explosion Impact_03_SFRMS_SCIWPNS"), + content.Load("Sfx/Explosions/EXPLDsgn_Explosion Impact_04_SFRMS_SCIWPNS"), + content.Load("Sfx/Explosions/EXPLDsgn_Explosion Impact_05_SFRMS_SCIWPNS"), + content.Load("Sfx/Explosions/EXPLDsgn_Explosion Impact_06_SFRMS_SCIWPNS") + }; + + _explosionPool = new VariantSoundPool(variants, voicesPerVariant: 1, rng: _random); + } + + // Call this once per frame + public void Update(GameTime gameTime) + { + float dt = (float)gameTime.ElapsedGameTime.TotalSeconds; + + _playerGunLimiter.Update(dt); + _enemyGunLimiter.Update(dt); + _impactLimiter.Update(dt); + + // Smoothly approach the duck target + _musicDuck = Approach(_musicDuck, _musicDuckTarget, _musicDuckSpeed * dt); + + // Apply music volume + MediaPlayer.Volume = Clamp01(MasterVolume * MusicVolume * _musicDuck); + } + + // ----------------------- + // Public SFX entry points + // ----------------------- + + public void PlayPlayerFire() + { + if (!_playerGunLimiter.TryConsume()) return; + + // Bullet sounds should be short + quiet-ish + float baseVol = 0.35f; + float vol = baseVol * RandRange(0.92f, 1.05f); + float pitch = RandRange(-0.05f, 0.05f); + + _playerGunPool.Play( + volume: Clamp01(MasterVolume * SfxVolume * vol), + pitch: pitch + ); + } + + public void PlayEnemyFire() + { + if (!_enemyGunLimiter.TryConsume()) return; + + float baseVol = 0.28f; + float vol = baseVol * RandRange(0.90f, 1.05f); + float pitch = RandRange(-0.07f, 0.07f); + + _enemyGunPool.Play( + volume: Clamp01(MasterVolume * SfxVolume * vol), + pitch: pitch + ); + } + + public void PlayImpact() + { + if (!_impactLimiter.TryConsume()) return; + + float baseVol = 0.25f; + float vol = baseVol * RandRange(0.90f, 1.10f); + float pitch = RandRange(-0.08f, 0.08f); + + _impactPool.Play( + volume: Clamp01(MasterVolume * SfxVolume * vol), + pitch: pitch + ); + } + + public void PlayExplosion(bool duckMusic = true) + { + float baseVol = 0.85f; + float vol = baseVol * RandRange(0.95f, 1.05f); + float pitch = RandRange(-0.04f, 0.04f); + + _explosionPool.Play( + volume: Clamp01(MasterVolume * SfxVolume * vol), + pitch: pitch + ); + + if (duckMusic) + { + // Duck music quickly, then let it recover + DuckMusic(amount: 0.55f, holdSeconds: 0.20f, releaseSeconds: 0.35f); + } + } + + // ----------------------- + // Music + // ----------------------- + + public void PlayMusic(ContentManager content, string songAssetName, bool loop = true) + { + // Example asset name: "Audio/Music/level1" + _currentSong = content.Load(songAssetName); + + MediaPlayer.IsRepeating = loop; + MediaPlayer.Volume = Clamp01(MasterVolume * MusicVolume * _musicDuck); + MediaPlayer.Play(_currentSong); + } + + public void StopMusic() => MediaPlayer.Stop(); + public void PauseMusic() => MediaPlayer.Pause(); + public void ResumeMusic() => MediaPlayer.Resume(); + + // Duck helper: amount < 1.0 means quieter music (e.g., 0.55) + public void DuckMusic(float amount, float holdSeconds, float releaseSeconds) + { + amount = Math.Clamp(amount, 0.05f, 1f); + _musicDuckTarget = amount; + + // Use a tiny internal timer without introducing a scheduler: + // We'll set a one-shot "return to 1" using a lightweight countdown. + _duckReturnTimer = holdSeconds; + _duckReleaseSeconds = Math.Max(0.01f, releaseSeconds); + } + + private float _duckReturnTimer = 0f; + private float _duckReleaseSeconds = 0.35f; + + // Call from Update to manage duck release + private void HandleDuckRelease(float dt) + { + if (_musicDuckTarget < 1f) + { + _duckReturnTimer -= dt; + if (_duckReturnTimer <= 0f) + { + // start releasing + _musicDuckTarget = 1f; + _musicDuckSpeed = 1f / _duckReleaseSeconds; // approx + // after it reaches 1, you can restore default speed if you want + } + } + else + { + // restore default “snappiness” once fully normal + if (Math.Abs(_musicDuck - 1f) < 0.001f) + _musicDuckSpeed = 6f; + } + } + + // Update override to include duck release handling + public void UpdateWithDuck(GameTime gameTime) + { + float dt = (float)gameTime.ElapsedGameTime.TotalSeconds; + + _playerGunLimiter.Update(dt); + _enemyGunLimiter.Update(dt); + _impactLimiter.Update(dt); + + HandleDuckRelease(dt); + + _musicDuck = Approach(_musicDuck, _musicDuckTarget, _musicDuckSpeed * dt); + MediaPlayer.Volume = Clamp01(MasterVolume * MusicVolume * _musicDuck); + } + + // ----------------------- + // Utilities + // ----------------------- + + private float RandRange(float min, float max) + => (float)(min + _random.NextDouble() * (max - min)); + + private static float Clamp01(float v) => Math.Clamp(v, 0f, 1f); + + private static float Approach(float current, float target, float maxDelta) + { + if (current < target) + return Math.Min(current + maxDelta, target); + + if (current > target) + return Math.Max(current - maxDelta, target); + + return current; + } +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Audio/RateLimiter.cs b/AlientAttack.MonoGame/Audio/RateLimiter.cs new file mode 100644 index 0000000..f526b8f --- /dev/null +++ b/AlientAttack.MonoGame/Audio/RateLimiter.cs @@ -0,0 +1,25 @@ +namespace AlienAttack.MonoGame.Audio; + +public sealed class RateLimiter(float cooldownSeconds) +{ + private readonly float _cooldownSeconds = cooldownSeconds <= 0 ? 0.0001f : cooldownSeconds; + private float _timer = 0f; + + public void Update(float deltaTime) + { + _timer -= deltaTime; + + if (_timer < 0f) + _timer = 0f; + } + + public bool TryConsume() + { + if (_timer > 0f) + return false; + + _timer = _cooldownSeconds; + + return true; + } +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Audio/SoundPool.cs b/AlientAttack.MonoGame/Audio/SoundPool.cs new file mode 100644 index 0000000..5f529cb --- /dev/null +++ b/AlientAttack.MonoGame/Audio/SoundPool.cs @@ -0,0 +1,37 @@ +using Microsoft.Xna.Framework.Audio; +using System; + +namespace AlienAttack.MonoGame.Audio; + +public sealed class SoundPool +{ + private readonly SoundEffectInstance[] _voices; + private int _cursor; + + public SoundPool(SoundEffect effect, int maxVoices) + { + ArgumentOutOfRangeException.ThrowIfLessThan(maxVoices, 1); + + _voices = new SoundEffectInstance[maxVoices]; + + for (int i = 0; i < maxVoices; i++) + _voices[i] = effect.CreateInstance(); + } + + public void Play(float volume = 1f, float pitch = 0f, float pan = 0f) + { + SoundEffectInstance instance = _voices[_cursor]; + + _cursor = (_cursor + 1) % _voices.Length; + + // "Voice stealing": stop if currently playing, then reuse immediately + if (instance.State == SoundState.Playing) + instance.Stop(); + + instance.Volume = Math.Clamp(volume, 0f, 1f); + instance.Pitch = Math.Clamp(pitch, -1f, 1f); + instance.Pan = Math.Clamp(pan, -1f, 1f); + + instance.Play(); + } +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Audio/VariantSoundPool.cs b/AlientAttack.MonoGame/Audio/VariantSoundPool.cs new file mode 100644 index 0000000..d2be631 --- /dev/null +++ b/AlientAttack.MonoGame/Audio/VariantSoundPool.cs @@ -0,0 +1,48 @@ +using Microsoft.Xna.Framework.Audio; +using System; + +namespace AlienAttack.MonoGame.Audio; + +public sealed class VariantSoundPool +{ + private readonly SoundPool[] _variantPools; + private readonly Random _rng; + private int _lastIndex = -1; + + public VariantSoundPool(SoundEffect[] variants, int voicesPerVariant, Random rng) + { + if (variants == null || variants.Length == 0) + throw new ArgumentException("At least one variant is required.", nameof(variants)); + + ArgumentOutOfRangeException.ThrowIfLessThan(voicesPerVariant, 1); + + _rng = rng ?? throw new ArgumentNullException(nameof(rng)); + + _variantPools = new SoundPool[variants.Length]; + + for (int i = 0; i < variants.Length; i++) + _variantPools[i] = new SoundPool(variants[i], voicesPerVariant); + } + + public void Play(float volume = 1f, float pitch = 0f, float pan = 0f, bool avoidImmediateRepeat = true) + { + int idx = PickVariantIndex(avoidImmediateRepeat); + _variantPools[idx].Play(volume, pitch, pan); + } + + private int PickVariantIndex(bool avoidImmediateRepeat) + { + if (_variantPools.Length == 1) + return 0; + + int idx = _rng.Next(_variantPools.Length); + if (!avoidImmediateRepeat) { _lastIndex = idx; return idx; } + + // Avoid playing the exact same sample twice in a row (nice polish) + if (idx == _lastIndex) + idx = (idx + 1 + _rng.Next(_variantPools.Length - 1)) % _variantPools.Length; + + _lastIndex = idx; + return idx; + } +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Content/Content.mgcb b/AlientAttack.MonoGame/Content/Content.mgcb index adb44de..116d957 100644 --- a/AlientAttack.MonoGame/Content/Content.mgcb +++ b/AlientAttack.MonoGame/Content/Content.mgcb @@ -151,6 +151,78 @@ /processorParam:Quality=Best /build:Sfx/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav +#begin Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS.wav;Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS + +#begin Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS.wav;Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS + +#begin Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS.wav;Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS + +#begin Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS.wav;Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS + +#begin Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS.wav;Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS + +#begin Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS.wav;Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS + +#begin Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS + +#begin Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS + +#begin Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS + +#begin Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS + +#begin Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS + +#begin Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS + #begin Sfx/Shield/SCIEnrg_Shield Activate_01_SFRMS_SCIWPNS.wav /importer:WavImporter /processor:SoundEffectProcessor @@ -181,6 +253,42 @@ /processorParam:Quality=Best /build:Sfx/Shield/SCIEnrg_Shield Activate_05_SFRMS_SCIWPNS.wav;Sfx/Shield/SCIEnrg_Shield Activate_05_SFRMS_SCIWPNS +#begin Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_01_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_01_SFRMS_SCIWPNS.wav;Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_01_SFRMS_SCIWPNS + +#begin Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_02_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_02_SFRMS_SCIWPNS.wav;Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_02_SFRMS_SCIWPNS + +#begin Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_03_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_03_SFRMS_SCIWPNS.wav;Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_03_SFRMS_SCIWPNS + +#begin Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_04_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_04_SFRMS_SCIWPNS.wav;Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_04_SFRMS_SCIWPNS + +#begin Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_05_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_05_SFRMS_SCIWPNS.wav;Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_05_SFRMS_SCIWPNS + +#begin Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_06_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_06_SFRMS_SCIWPNS.wav;Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_06_SFRMS_SCIWPNS + #begin Sprites/Asteroid 01_png_processed.png /importer:TextureImporter /processor:TextureProcessor diff --git a/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..45ce3e1 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_01_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..6b3edc3 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_02_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..400977c Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_03_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..cc18d6f Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_04_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..b1bfd44 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_05_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..b909359 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Impact/BLLTImpt_Impact Object_06_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..a0b0456 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_01_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..66fa9a2 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_02_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..d3ccaf2 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_03_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..bb9aa3a Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_04_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..87dedb7 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_05_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..12494bf Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_01_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_01_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..7f4af01 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_01_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_02_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_02_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..92b4621 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_02_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_03_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_03_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..1d89413 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_03_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_04_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_04_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..9f692d1 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_04_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_05_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_05_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..f55f2ff Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_05_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_06_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_06_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..e851f55 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Silenced Pistol/GUNSupr_Silenced Pistol Fire Short_06_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/GameLoops/GameLoopBase.cs b/AlientAttack.MonoGame/GameLoops/GameLoopBase.cs index d1696ff..f7d12a9 100644 --- a/AlientAttack.MonoGame/GameLoops/GameLoopBase.cs +++ b/AlientAttack.MonoGame/GameLoops/GameLoopBase.cs @@ -1,4 +1,5 @@ -using AlienAttack.MonoGame.View; +using AlienAttack.MonoGame.Audio; +using AlienAttack.MonoGame.View; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; @@ -11,6 +12,7 @@ internal abstract class GameLoopBase(AlienAttackGame game) : IGameLoop protected readonly ContentManager Content = game.Content; protected readonly SpriteBatch SpriteBatch = game.SpriteBatch; protected readonly ViewTransform ViewTransform = game.ViewTransform; + protected readonly AudioManager Audio = game.Audio; public void Draw(GameTime gameTime) { @@ -23,6 +25,7 @@ internal abstract class GameLoopBase(AlienAttackGame game) : IGameLoop public void Update(GameTime gameTime) { + Audio.UpdateWithDuck(gameTime); OnUpdate(gameTime); } diff --git a/AlientAttack.MonoGame/Things/Bullets/Bullet.cs b/AlientAttack.MonoGame/Things/Bullets/Bullet.cs index f1e9446..f3e3694 100644 --- a/AlientAttack.MonoGame/Things/Bullets/Bullet.cs +++ b/AlientAttack.MonoGame/Things/Bullets/Bullet.cs @@ -47,5 +47,7 @@ internal class Bullet(float x, float y, float xVel, float yVel, Sprite owner) : } context.SpawnSprite(new MiniExplosion((int)XPosition, (int)YPosition, xVel, yVel)); + + context.AudioManager.PlayImpact(); } } \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Enemies/GreenEnemy.cs b/AlientAttack.MonoGame/Things/Enemies/GreenEnemy.cs index 18c3b2d..fde7551 100644 --- a/AlientAttack.MonoGame/Things/Enemies/GreenEnemy.cs +++ b/AlientAttack.MonoGame/Things/Enemies/GreenEnemy.cs @@ -88,6 +88,8 @@ internal class GreenEnemy : EnemyShip context.SpawnSprite(new MinigunBulletSmall(originX + 14, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this)); CurrentFireThreshold = FireThreshold; + + context.AudioManager.PlayEnemyFire(); } //CheckMove(context); @@ -100,10 +102,12 @@ internal class GreenEnemy : EnemyShip { context.SpawnSprite(new Explosion((int)XPosition, (int)YPosition, XVelocity, YVelocity)); - int number = context.Random.Next(1, 7); + //int number = context.Random.Next(1, 7); - SoundEffect soundEffect = context.Content.Load(@$"Sfx\Explosions\EXPLDsgn_Explosion Impact_0{number}_SFRMS_SCIWPNS"); - soundEffect.Play(0.95f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + //SoundEffect soundEffect = context.Content.Load(@$"Sfx\Explosions\EXPLDsgn_Explosion Impact_0{number}_SFRMS_SCIWPNS"); + //soundEffect.Play(0.95f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + + context.AudioManager.PlayExplosion(); } private void CheckFire(SpriteUpdateContext context) diff --git a/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs b/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs index 4154b04..02955e0 100644 --- a/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs +++ b/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs @@ -73,10 +73,12 @@ internal class RedEnemy : EnemyShip { context.SpawnSprite(new Explosion((int)XPosition, (int)YPosition, XVelocity, YVelocity)); - int number = context.Random.Next(1, 7); + //int number = context.Random.Next(1, 7); - SoundEffect soundEffect = context.Content.Load(@$"Sfx\Explosions\EXPLDsgn_Explosion Impact_0{number}_SFRMS_SCIWPNS"); - soundEffect.Play(0.95f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + //SoundEffect soundEffect = context.Content.Load(@$"Sfx\Explosions\EXPLDsgn_Explosion Impact_0{number}_SFRMS_SCIWPNS"); + //soundEffect.Play(0.95f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + + context.AudioManager.PlayExplosion(); } private void TryFire(SpriteUpdateContext context) @@ -94,6 +96,8 @@ internal class RedEnemy : EnemyShip context.SpawnSprite(new MinigunBulletSmall(originX + 14, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this)); CurrentFireThreshold = FireThreshold; + + context.AudioManager.PlayEnemyFire(); } } diff --git a/AlientAttack.MonoGame/Things/Enemies/TealEnemy.cs b/AlientAttack.MonoGame/Things/Enemies/TealEnemy.cs index 325a012..c0afc86 100644 --- a/AlientAttack.MonoGame/Things/Enemies/TealEnemy.cs +++ b/AlientAttack.MonoGame/Things/Enemies/TealEnemy.cs @@ -66,10 +66,12 @@ internal class TealEnemy : EnemyShip { context.SpawnSprite(new Explosion((int)XPosition, (int)YPosition, XVelocity, YVelocity)); - int number = context.Random.Next(1, 7); + //int number = context.Random.Next(1, 7); - SoundEffect soundEffect = context.Content.Load(@$"Sfx\Explosions\EXPLDsgn_Explosion Impact_0{number}_SFRMS_SCIWPNS"); - soundEffect.Play(0.95f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + //SoundEffect soundEffect = context.Content.Load(@$"Sfx\Explosions\EXPLDsgn_Explosion Impact_0{number}_SFRMS_SCIWPNS"); + //soundEffect.Play(0.95f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + + context.AudioManager.PlayExplosion(); } private void TryFire(SpriteUpdateContext context) @@ -87,6 +89,8 @@ internal class TealEnemy : EnemyShip context.SpawnSprite(new MinigunBulletSmall(originX + 14, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this)); CurrentFireThreshold = FireThreshold; + + context.AudioManager.PlayEnemyFire(); } } diff --git a/AlientAttack.MonoGame/Things/Player.cs b/AlientAttack.MonoGame/Things/Player.cs index de8f85f..a408ca5 100644 --- a/AlientAttack.MonoGame/Things/Player.cs +++ b/AlientAttack.MonoGame/Things/Player.cs @@ -97,11 +97,13 @@ internal class Player : MoveableSprite public override void Update(SpriteUpdateContext context) { + base.Update(context); + //UpdateExhaustAnimationThreshold(); CheckMove(context); CheckFire(context); - base.Update(context); + //base.Update(context); } private void UpdateExhaustAnimationThreshold() diff --git a/AlientAttack.MonoGame/Things/SpriteUpdateContext.cs b/AlientAttack.MonoGame/Things/SpriteUpdateContext.cs index d6ecb47..ef97140 100644 --- a/AlientAttack.MonoGame/Things/SpriteUpdateContext.cs +++ b/AlientAttack.MonoGame/Things/SpriteUpdateContext.cs @@ -1,4 +1,5 @@ -using AlienAttack.MonoGame.View; +using AlienAttack.MonoGame.Audio; +using AlienAttack.MonoGame.View; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using System; @@ -12,6 +13,7 @@ public class SpriteUpdateContext(AlienAttackGame game) public required Random Random { get; init; } public required GameTime GameTime { get; init; } public required ContentManager Content { get; init; } + public AudioManager AudioManager => game.Audio; } public class SpriteCollisionContext(AlienAttackGame game) @@ -21,4 +23,5 @@ public class SpriteCollisionContext(AlienAttackGame game) public required Action SpawnSprite { get; init; } public required Random Random { get; init; } public required ContentManager Content { get; init; } + public AudioManager AudioManager => game.Audio; } \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Weapons/Minigun.cs b/AlientAttack.MonoGame/Things/Weapons/Minigun.cs index 918752f..e67fa61 100644 --- a/AlientAttack.MonoGame/Things/Weapons/Minigun.cs +++ b/AlientAttack.MonoGame/Things/Weapons/Minigun.cs @@ -22,10 +22,12 @@ public class Minigun : Weapon context.SpawnSprite(bullet1); context.SpawnSprite(bullet2); - int number = context.Random.Next(1, 7); + //int number = context.Random.Next(1, 7); - SoundEffect soundEffect = context.Content.Load(@$"Sfx\GUNAuto_Assault Rifle A Fire_0{number}_SFRMS_SCIWPNS"); - soundEffect.Play(0.25f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + //SoundEffect soundEffect = context.Content.Load(@$"Sfx\GUNAuto_Assault Rifle A Fire_0{number}_SFRMS_SCIWPNS"); + //soundEffect.Play(0.25f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + + context.AudioManager.PlayPlayerFire(); } }