강의, 책/[Unity] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈

Section 9. Sound - Sound Manager(2)

hye3193 2024. 2. 3. 11:11

Effect의 경우, 같은 effect를 여러 번 불러오게 되면, 불러올 때마다 manager를 호출하는 게 부하를 줄 수 있으므로 캐싱을 해둔다

public class SoundManager
{
    Dictionary<string, AudioClip> _audioClips = new Dictionary<srting, AudioClip>();
    
    public void Init()
    {
        // Init
    }
    
    // Scene 이동 시 캐싱된 audio source&clip 초기화
    public void Clear()
    {
        foreach (AudioSource audioSource in _audioSources)
        {
            audioSource.clip = null;
            audioSource.Stop();
        }
        _audioClips.Clear();
    }
    
    public void Play(string path, Define.Sound type = Define.Sound.Effect, float pitch = 1.0f)
    {
        if (type == Define.Sound.Bgm)
        {
            // BGM 실행
        }
        else
        {
            AudioClip audioClip = GetOrAddAudioClip(path);
            if (audioClip == null)
            {
                Debug.Log($"AudioClip Missing : {path}");
                return;
            }
            
            AudioSource audioSource = _audioSources[(int)Define.Sound.Effect];
            audioSource.pitch = pitch;
            audioSource.clip = audioClip;
            audioSource.PlayOneShot(audioClip);
        }
    }
    
    // 이전에 불러왔던 clip을 캐싱
    AudioClip GetOrAddAudioClip(string path)
    {
        AudioClip audioClip = null;
        if (_audioClips.TryGetValue(path, out audioClip) == false)
        {
            audioClip = Managers.Resource.Load<AudioClip>(path);\
            _audioClips.Add(path, audioClip);
        }
        return audioClip;
    }
}

 

Managers.cs에서 Scene이 이동할 때마다 데이터들을 날려주기 위한 Clear 함수를 추가한다

public static void Clear()
{
    Input.Clear();
    Sound.Clear();
    Scene.Clear();
    UI.Clear();
}

 

InputManager에서도 초기화를 위한 Clear 함수를 만들어준다

public void Clear()
{
    // Action 선언해두었던 것들 초기화
    KeyAction = null;
    MouseAction = null;
}

 

그리고 씬이 바뀔 때 초기화를 해주기 위하여 SceneManagerEx 스크립트에서도 아래와 같이 수정해준다

public class SceneManagerEx
{
    public void LoadScene(Define.Scene type)
    {
        Managers.Clear();
        SceneManager.LoadScene(GetSceneName(type));
    }
    
    string GetSceneName(Define.Scene type)
    {
        // Scene Manager 참고
    }
    
    public void Clear()
    {
        CurrentScene.Clear();
    }
}

 

UI Manager에서도 pop stack 등을 초기화시켜준다

public void Clear()
{
    CloseAllPopupUI();
    _sceneUI = null;
}

 

Sound Manager에서 새로운 함수(clip을 직접 받는 버전)를 추가해주면서, 함수를 기능 단위로 나눠준다

// 기존
public void Play(string path, Define.Sound type = Define.Sound.Effect, float pitch = 1.0f)
{
    AudioClip audioClip = GetOrAddAudioClip(path, type);
    Play(audioClip, type, pitch);
}

// Clip을 직접 받음
public void Play(AudioClip audioClip, Define.Sound type = Define.Sound.Effect, float pitch = 1.0f)
{
    if (audioClip == null)
        return;
        
    if (type == Define.Sound.Bgm)
    {
        AudioSource audioSource = _audioSources[(int)Define.Sound.Bgm];
        if (audioSource.isPlaying)
            audioSource.Stop();

        audioSource.pitch = pitch;
        audioSource.clip = audioclip;
        audioSource.Play();
    }
    else
    {
        AudioSource audioSource = _audioSources[(int)Define.Sound.Effect];
        audioSource.pitch = pitch;
        audioSource.PlayOneShot(audioClip);
    }
}

AudioClip GetOrAddAudioClip(string path, Define.Sound type = Define.Sound.Effect)
{
    if (path.Contains("Sounds/") == false)
    path = $"Sounds/{path}";
    
    AudioClip audioClip = null;
    
    if (type == Define.Sound.Bgm)
    {
        audioClip = Managers.Resource.Load<AudioClip>(path);
    }
    else
    {
        if (_audioClips.TryGetValue(path, out audioClip) == false)
        {
            audioClip = Managers.Resource.Load<AudioClip>(path);
            _audioClips.Add(path, audioClip);
        }
    }
    
    if (audioClip == null)
    Debug.Log($"AudioClip Missing : {path}");
    
    return audioClip;
}

 

+

PlayClipAtPoint(): 지정한 클립을 특정 위치에서 재생할 수 있게 하는 함수