Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / Managers / AudioManager.cs
using Eflatun.SceneReference;
using MyCollections.Managers;
using MyCollections.SceneManagement;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.SceneManagement;

public class AudioManager : MonoBehaviour
{
    // Singleton instance
    public static AudioManager Instance { get; private set; }

    //Audio Mixer settings
    [Header("Mixer Settings")]
    public AudioMixer audioMixer; // Reference to AudioMixer for volume control
    public AudioMixerGroup musicMixerGroup;
    public AudioMixerGroup sfxMixerGroup;

    //Audio sources
    [Header("Audio Sources")]
    [SerializeField] private AudioSource musicSource; // Audio source for music
    [SerializeField] private AudioSource sfxSource; // Audio source for SFX

    [Header("Audio Clips")]
    public List<Sound> sounds = new List<Sound>(); // Array of sounds

    private Coroutine musicTransitionCoroutine;
    private GameStateManager gameStateManager;
    private bool isTransitioning = false;


    // Music fade duration
    [Header("Transition Settings")]
    public float musicTransitionTime = 2f;


    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // Persist across scenes
        }

        if (musicSource == null)
        {
            //Audio Source for the music
            //Only one is necessary
            musicSource = gameObject.AddComponent<AudioSource>();
            musicSource.outputAudioMixerGroup = musicMixerGroup;
        }

        InitializeSounds();
        //SceneLoader
    }

    private void Start()
    {

        gameStateManager = GameStateManager.Instance;
        gameStateManager.OnGameStateChange += PlayMusicOnStateChange;
    }

    // Control volume via the AudioMixer
    public void SetVolume(string parameter, float value)
    {
        audioMixer.SetFloat(parameter, Mathf.Log10(value) * 20); // Convert linear slider value to logarithmic
    }

    public void InitializeSounds()
    {
        // Initialize each sound with its settings
        foreach (Sound sound in sounds)
        {
            sound.source = sound.audioMixerGroup == Sound.AudioType.SFX ? gameObject.AddComponent<AudioSource>() : musicSource;
            sound.source.clip = sound.clip;
            sound.source.volume = sound.volume;
            sound.source.pitch = sound.pitch;
            sound.source.loop = sound.loop;
            sound.source.outputAudioMixerGroup = sound.audioMixerGroup == Sound.AudioType.Music ? musicMixerGroup : sfxMixerGroup;
        }
    }

    private void Update()
    {
        if (!musicSource.isPlaying && !isTransitioning)
        {
            foreach (Sound sound in sounds)
            {
                if (sound.audioMixerGroup == Sound.AudioType.Music)
                {
                    Debug.Log("I FOUND A SOUND TO PLAY");
                    PlayMusic(sound.name);
                    break;
                }
            }
        }
    }

    public void Play(AudioSource source, string name)
    {
        Sound s = sounds.Find(sound => sound.name == name);

        if (s == null)
        {
            Debug.LogWarning($"Sound: {name} not found!");
            return;
        }
        source.clip = s.clip;
        source.Play();
    }

    public void Play(AudioSource source, AudioClip clip)
    {
        source.clip = clip;
        source.Play();
    }

    //Play SFX sound
    public void Play(string name)
    {
        Sound s = sounds.Find(sound => sound.name == name);

        if (s == null)
        {
            Debug.LogWarning($"Sound: {name} not found!");
            return;
        }
        s.source.Play();
    }

    public void PlayMusic(string name)
    {
        Sound s = sounds.Find(sound => sound.name == name);

        if (s == null)
        {
            Debug.LogWarning($"Music: {name} not found!");
            return;
        }
        
        musicSource.clip = s.clip;
        musicSource.Play();
    }

    public void PlayMusic(Sound.AudioTag tag)
    {
        Sound s = sounds.Find(sound => sound.audioTag == tag);

        if (s == null)
        {
            Debug.LogWarning($"Music: {tag} not found!");
            return;
        }

        // If already transitioning or playing the same music, don't restart the transition
        if (musicSource.isPlaying && musicSource.clip == s.clip)
        {
            return;
        }

        PlayTransitionMusic(tag);
    }

    private void PlayMusicOnStateChange(GameState newState)
    {
        switch (newState)
        {
            case(GameState.Combat):
                PlayMusic(Sound.AudioTag.EnterCombat);
            break;

            case (GameState.Normal):
                PlayMusic(Sound.AudioTag.Calm);
            break;

            case (GameState.Loading):
                Stop(musicSource);
            break;
        }
    }

    //Plays music
    public void PlayTransitionMusic(Sound.AudioTag tag)
    {
        Sound s = sounds.Find(sound => sound.audioTag == tag);

        if (s == null)
        {
            Debug.LogWarning($"Music with tag {tag} not found!");
            return;
        }

        if (musicTransitionCoroutine != null)
            StopCoroutine(musicTransitionCoroutine);

        // Transition to the new music
        musicTransitionCoroutine = StartCoroutine(TransitionMusic(s));
    }


    //Stop sound
    public void Stop(string name)
    {
        Sound s = sounds.Find(sound => sound.name == name);
        if (s == null)
        {
            Debug.LogWarning($"Sound: {name} not found!");
            return;
        }
        s.source.Stop();
    }

    public void Stop(AudioSource source)
    {
        Sound s = sounds.Find(sound => sound.name == name);
        if (s == null)
        {
            Debug.LogWarning($"Sound: {name} not found!");
            return;
        }
        source.Stop();
    }

    // Coroutine to handle smooth transition between music tracks
    private IEnumerator TransitionMusic(Sound newMusic)
    {
        isTransitioning = true;
        // Slight delay to give time for audio state to update
        yield return new WaitForSeconds(0.1f);

        Debug.Log("Is Music Playing: " + musicSource.isPlaying);

        // Fade out the current music if any is playing
        if (musicSource.isPlaying)
        {
            Debug.Log("Fading out current music...");
            yield return StartCoroutine(FadeOut(musicSource, musicTransitionTime));
        }

        // Set the new music clip and play it
        Debug.Log("Fading in new music...");
        musicSource.clip = newMusic.clip;
        musicSource.Play();

        // Fade in the new music
        yield return StartCoroutine(FadeIn(musicSource, musicTransitionTime));
        isTransitioning = false;
    }

    //Smoothly fades Out a music
    private IEnumerator FadeOut(AudioSource audioSource, float duration)
    {
        float startVolume = audioSource.volume;
        float targetVolume = 0f;

        Debug.Log("Starting fade out");

        for (float t = 0; t < duration; t += Time.deltaTime)
        {
            audioSource.volume = Mathf.Lerp(startVolume, targetVolume, t / duration);
            yield return null;
        }

        audioSource.volume = targetVolume;
        audioSource.Stop();
        Debug.Log("Stop fade out");
    }

    //Smoothly fades In a music
    private IEnumerator FadeIn(AudioSource audioSource, float duration)
    {
        float targetVolume = 1f;
        audioSource.volume = 0;
        audioSource.Play();

        for (float t = 0; t < duration; t += Time.deltaTime)
        {
            audioSource.volume = Mathf.Lerp(0, targetVolume, t / duration);
            yield return null;
        }
    }

    //Load sounds when a scene or a gorup of scenes is loaded
    public void LoadSounds(Sound[] sceneSounds) => sounds.AddRange(sceneSounds);

    //Unloads sound
    //If a scene is unloaded, there's no need to keep sounds in memory
    public void UnloadSounds(Sound[] sceneSounds)
    {
        foreach (Sound sound in sceneSounds)
        {
            Sound s = sounds.Find(s => s.name == sound.name);

            if (s != null)
                sounds.Remove(s);
        }
    }
}

[Serializable]
public class Sound
{
    public enum AudioType { Music, SFX } // Separate music and sound effects
    public enum AudioTag { Calm, EnterCombat, ExitCombat}
    public string name;
    public AudioClip clip;
    public AudioType audioMixerGroup;
    public AudioTag audioTag;

    [Range(0f, 1f)] public float volume = 0.7f;
    [Range(0.1f, 3f)] public float pitch = 1f;
    public bool loop;

    public AudioSource source;
}