스파르타 게임개발종합반(Unity)/TIL - 본캠프 매일 공부 기록

2024.05.22 TIL - SoundManager로 배경음과 효과음 관리 및 재생하기

테크러너 2024. 5. 22.

효과음이 재생안되던 문제

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player")) // 플레이어와 충돌했을 때
        {          
            HealthSystem healthSystem = collision.GetComponent<HealthSystem>();
            if (healthSystem.CurrentHealth < healthSystem.MaxHealth)
            {
                healthSystem.ChangeHealth(healAamount);
                SoundManager.Sound.Play("DrinkPotion.mp3", SoundType.Effect);
                gameObject.SetActive(false);
            }
        }
    }

위의 코드는 회복 아이템이 캐릭터랑 충돌이나면 캐릭터의 체력이 1 증가하고, 회복 아이템이 사라지는 코드이다.

 

SoundManager.Sound.Play("DrinkPotion.mp3", SoundType.Effect);

원래 이 자리에 해당 오브젝트에 부착되어 있던 오디오 클립으로 효과음을 재생시키는 코드가 있었다.

 

gameObject.SetActive(false);

그런데 위의 코드가 너무 빨리 실행되니까 포션이 없어지면서 효과음도 같이 없어졌다. 왜냐하면 포션에 AudioSource가 부착되어 있었기 때문이다.

 

 

해결 방법 - SoundManger.cs

그래서 `SoundManager.cs`를 만들어서 배경음과 효과음을 관리하기로 했다.

public class SoundManager : MonoBehaviour
{
    private static SoundManager _instance;
    public static SoundManager Sound
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<SoundManager>();
                if (_instance == null)
                {
                    GameObject soundManager = new GameObject("SoundManager");
                    _instance = soundManager.AddComponent<SoundManager>();
                    DontDestroyOnLoad(soundManager);
                }
            }
            return _instance;
        }
    }
    
    private void Awake()
    {
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(gameObject);
            Init();
        }
        else if (_instance != this)
        {
            Destroy(gameObject);
        }
    }
}

우선 사운드매니저는 싱글톤으로 만들었다. 그래야 어느 스크립트에서든 사운드매니저를 통해 음악을 재생할 수 있기 때문이다.

 

public enum SoundType
{
    Bgm,
    Effect,
    MaxCount,  // 아무것도 아님, 끝을 알기 위함
}
    // 배경음, 효과음 중첩이 가능하기 위함
    private AudioSource[] _audioSources = new AudioSource[(int)SoundType.MaxCount];
    private Dictionary<string, AudioClip> _audioClips = new Dictionary<string, AudioClip>();

그리고 AudioSource는 배경음, 효과음 각각 따로 생성했다.

왜냐하면 배경음이 실행되는 도중 끊기지 않고 효과음도 중첩으로 실행될 수 있게 하기 위함이다.

 

    public void Init()
    {
        GameObject root = GameObject.Find("@Sound");
        if (root == null)
        {
            root = new GameObject { name = "@Sound" };
            Object.DontDestroyOnLoad(root);

            string[] soundNames = System.Enum.GetNames(typeof(SoundType));
            for (int i = 0; i < soundNames.Length - 1; i++)
            {
                GameObject go = new GameObject { name = soundNames[i] };
                _audioSources[i] = go.AddComponent<AudioSource>();
                go.transform.parent = root.transform;
            }

            _audioSources[(int)SoundType.Bgm].loop = true; // 배경음은 무한 반복 재생
        }
    }

 

Init()이 실행되면 위의 그림처럼 배경음, 효과음 따로 오브젝트가 생성되고, 각각 AudioSource 컴포넌트가 부착된다.

 

    // 배경음, 효과음 재생
    public void Play(AudioClip clip, SoundType type = SoundType.Effect, float volume = 1.0f) // = 디폴트값
    {
        if (clip == null)
            return;
        if (type == SoundType.Effect)
        {
            AudioSource audioSource = _audioSources[(int)SoundType.Effect];
            if (audioSource.isPlaying)
            {
                audioSource.Stop();
            }
            audioSource.volume = volume;
            audioSource.PlayOneShot(clip); // 한 번만 재생
        }
        else // Sound.Bgm
        {
            AudioSource audioSource = _audioSources[(int)SoundType.Bgm];
            audioSource.volume = volume;
            audioSource.clip = clip;
            audioSource.Play();
        }
    }

효과음, 배경음에 따라 `PlayOneShot`방식과 `Play`방식으로 나뉜다.

효과음은 한 번만 실행하고, 배경음은 계속 실행되기 때문이다.

 

            if (audioSource.isPlaying)
            {
                audioSource.Stop();
            }

효과음에 위의 코드를 넣은 이유는 효과음이 2개가 한 번에 연속으로 재생될 때 A효과음에서 B효과음으로 넘어가면서 A효과음의 볼륨설정값이 B효과음 재생시에 잠깐 반영된 후 원하던 볼륨으로 돌아오기 때문이다.

 

    public void Play(string path, SoundType type = SoundType.Effect, float volume = 1.0f)
    {
        AudioClip audioClip = GetOrAddAudioClip(path, type);
        Play(audioClip, type, volume);
    }

    AudioClip GetOrAddAudioClip(string path, SoundType type = SoundType.Effect)
    {
        if (path.Contains("Assets/Sounds") == false)
            path = $"Assets/Sounds/{path}"; // Sound 폴더 안에 저장될 수 있도록

        AudioClip audioClip = null;

        if (type == SoundType.Bgm) // BGM 배경음악 클립 붙이기
        {
            audioClip = AssetDatabase.LoadAssetAtPath<AudioClip>(path);
        }
        else // Effect 효과음 클립 붙이기
        {
            if (_audioClips.TryGetValue(path, out audioClip) == false)
            {
                audioClip = AssetDatabase.LoadAssetAtPath<AudioClip>(path);
                _audioClips.Add(path, audioClip);
            }
        }

        if (audioClip == null)
            Debug.Log($"AudioClip Missing ! {path}");

        return audioClip;
    }

이 코드는 음악 파일이 있는 경로안의 오디오 클립을 가져와 실행시키기 위해 작성됐다.

 

SoundManager.Sound.Play("Explode.mp3", SoundType.Effect, 0.2f);

사용방법은 위와 같다.

"Assets/Sounds" 경로까지는 같기 때문에 뒤에 "Explode.mp3"만 적어주면 해당 음악 클립이 재생된다.

타입은 효과음이고, 볼륨이 0.2f이다.

 

 

해결 후 성과

  • 효과음이 실행될 오브젝트마다 AudioSource 컴포넌트를 부착해주고, 클립을 달아주고, 소스코드에서는 해당 컴포넌트 불러와서 재생시켜주는 이 과정들이 점점 늘어날수록 너무 귀찮았다.
  • 더욱 문제인점은 이 오브젝트가 삭제되면 AudioSource 컴포넌트도 같이 삭제되는 것이기 때문에 효과음도 같이 사라진다는 것이다.

그래서 SoundManager.cs를 만들어 배경음과 효과음을 관리하는 로직을 작성해보는 경험을 해봐서 실력이 향상된 것 같다.

반응형

댓글