using UnityEngine; using System.Collections.Generic; namespace BulletHellTemplate { /// /// Serializable structure to hold custom audio tag and its volume. /// [System.Serializable] public struct CustomAudioTag { public string tag; [Range(0, 1)] public float volume; } /// /// Singleton class that manages audio volumes and plays audio clips globally with performance optimizations. /// public class AudioManager : MonoBehaviour { [Header("Audio Source")] [Tooltip("The main audio source for Master volume.")] public AudioSource masterAudioSource; [Header("Volume Settings")] [Tooltip("Global volume for all sounds.")] [Range(0, 1)] public float masterVolume = 1.0f; [Tooltip("Volume for VFX sounds.")] [Range(0, 1)] public float vfxVolume = 1.0f; [Tooltip("Volume for ambient sounds.")] [Range(0, 1)] public float ambienceVolume = 1.0f; [Tooltip("Custom tag volumes, developers can add their own audio tags.")] public List customTagVolumes = new List(); [Header("Audio Limitations")] [Tooltip("The maximum number of audios that can be played at the same time. 0 = unlimited")] public int maxConcurrentAudio = 10; private int currentAudioCount = 0; public AudioSource ambientAudioSource; // Separate source for ambient sounds public AudioSource loadingAudioSource; public static AudioManager Singleton; private void Awake() { if (Singleton == null) { Singleton = this; DontDestroyOnLoad(gameObject); ambientAudioSource.loop = true; // Loop for ambient sounds } else { Destroy(gameObject); } } /// /// Plays an ambient audio clip in a loop with the specified tag. /// Does not affect the current audio count or maxConcurrentAudio. /// /// The audio clip to play. /// The tag used to determine the volume. public void PlayAmbientAudio(AudioClip clip, string tag) { // Stop the loading audio if it's playing if (loadingAudioSource.isPlaying) { loadingAudioSource.Stop(); } if (ambientAudioSource.isPlaying) { ambientAudioSource.Stop(); } // Play ambient audio float volume = GetVolumeByTag(tag); ambientAudioSource.volume = volume; ambientAudioSource.clip = clip; ambientAudioSource.loop = true; // Ensure the ambient audio is set to loop ambientAudioSource.Play(); } /// /// Plays a loading menu audio clip and stops the ambient audio. /// The ambient audio will be stopped and prevented from playing again until reloaded. /// /// The audio clip to play. /// The tag used to determine the volume. public void PlayLoadingMenu(AudioClip clip, string tag) { // Stop the ambient audio to prevent it from playing during the loading menu if (ambientAudioSource.isPlaying) { ambientAudioSource.Stop(); ambientAudioSource.clip = null; // Prevent ambient audio from automatically replaying } // Play the loading menu audio float volume = GetVolumeByTag(tag); loadingAudioSource.volume = volume; loadingAudioSource.PlayOneShot(clip); // PlayOneShot doesn't require setting the clip to null } /// /// Stops all currently playing audio, including ambient and loading audio. /// This function is useful when the game ends or the player dies, /// ensuring that no audio is left playing. /// public void StopAllAudioPlay() { // Stop the master audio source if it's playing if (masterAudioSource.isPlaying) { masterAudioSource.Stop(); } // Stop the ambient audio source if it's playing if (ambientAudioSource.isPlaying) { ambientAudioSource.Stop(); } // Stop the loading audio source if it's playing if (loadingAudioSource.isPlaying) { loadingAudioSource.Stop(); } // Reset the current audio count currentAudioCount = 0; } public void StopLoadingAudioPlay() { // Stop the loading audio source if it's playing if (loadingAudioSource.isPlaying) { loadingAudioSource.Stop(); } } /// /// Plays an audio clip using PlayOneShot with the specified tag. /// Limits the number of concurrently playing audio clips and adjusts the volume based on the tag. /// public void PlayAudio(AudioClip clip, string tag) { if (maxConcurrentAudio > 0 && currentAudioCount >= maxConcurrentAudio) { Debug.Log("Audio limit reached. Skipping this sound."); return; } // Determine the volume based on the tag float volume = GetVolumeByTag(tag); masterAudioSource.volume = volume; masterAudioSource.PlayOneShot(clip); currentAudioCount++; // Automatically reduce the count after the audio finishes StartCoroutine(ReduceAudioCountAfterPlaying(clip.length)); } /// /// Gets the volume based on the tag. /// private float GetVolumeByTag(string tag) { switch (tag.ToLower()) { case "master": return masterVolume; case "vfx": return vfxVolume; case "ambient": return ambienceVolume; default: foreach (var customTag in customTagVolumes) { if (customTag.tag == tag) { return customTag.volume; } } Debug.LogWarning($"Audio tag '{tag}' not recognized. Using Master volume as default."); return masterVolume; } } /// /// Reduces the audio count after a certain amount of time (the clip length). /// private IEnumerator ReduceAudioCountAfterPlaying(float clipLength) { yield return new WaitForSeconds(clipLength); currentAudioCount--; } } }