207 lines
7.1 KiB
C#
Raw Normal View History

2025-09-19 14:56:58 +05:00
using UnityEngine;
using System.Collections.Generic;
namespace BulletHellTemplate
{
/// <summary>
/// Serializable structure to hold custom audio tag and its volume.
/// </summary>
[System.Serializable]
public struct CustomAudioTag
{
public string tag;
[Range(0, 1)]
public float volume;
}
/// <summary>
/// Singleton class that manages audio volumes and plays audio clips globally with performance optimizations.
/// </summary>
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<CustomAudioTag> customTagVolumes = new List<CustomAudioTag>();
[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);
}
}
/// <summary>
/// Plays an ambient audio clip in a loop with the specified tag.
/// Does not affect the current audio count or maxConcurrentAudio.
/// </summary>
/// <param name="clip">The audio clip to play.</param>
/// <param name="tag">The tag used to determine the volume.</param>
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();
}
/// <summary>
/// Plays a loading menu audio clip and stops the ambient audio.
/// The ambient audio will be stopped and prevented from playing again until reloaded.
/// </summary>
/// <param name="clip">The audio clip to play.</param>
/// <param name="tag">The tag used to determine the volume.</param>
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
}
/// <summary>
/// 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.
/// </summary>
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();
}
}
/// <summary>
/// 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.
/// </summary>
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));
}
/// <summary>
/// Gets the volume based on the tag.
/// </summary>
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;
}
}
/// <summary>
/// Reduces the audio count after a certain amount of time (the clip length).
/// </summary>
private IEnumerator<WaitForSeconds> ReduceAudioCountAfterPlaying(float clipLength)
{
yield return new WaitForSeconds(clipLength);
currentAudioCount--;
}
}
}