Sound

·2023년 4월 25일
0

Unity

목록 보기
20/22

📌유니티 소리 3요소


MP3 Player -> AudioSource
MP3 음원 -> AudioClip
관객(귀) -> AudioListener

AudioListener

한씬에 하나씩만 있으면 되는데 대부분 default로 Main Camera에 달려있다

Audio Source, AudioClip

AudioClip : 들을 소리
Play On Awake : 실행 후 바로 들리게
Volume : 소리크기
Pitch : 재생속도
Loop : 반복

소리재생

    public AudioClip audioClip;

    private void OnTriggerEnter(Collider other)
    {
        AudioSource audio = GetComponent<AudioSource>();
        audio.PlayOneShot(audioClip);
    }

AudioClip으로 등록한 소리가 난다

소리재생(두가지)

    public AudioClip audioClip;
    public AudioClip audioClip2;

    private void OnTriggerEnter(Collider other)
    {
        AudioSource audio = GetComponent<AudioSource>();
        audio.PlayOneShot(audioClip);
        audio.PlayOneShot(audioClip2);

    }

소리가 겹쳐서 두가지 나게된다

오브젝트 파괴시 소리는?

    public AudioClip audioClip;

    private void OnTriggerEnter(Collider other)
    {
        AudioSource audio = GetComponent<AudioSource>();
        audio.PlayOneShot(audioClip);
        Destroy(gameObject, 0.2f);
    }

게임오브젝트가 파괴될 시 소리가 바로 끊기게 된다

소리 끝나고 파괴

   public AudioClip audioClip;
    public AudioClip audioClip2;

    private void OnTriggerEnter(Collider other)
    {
        AudioSource audio = GetComponent<AudioSource>();
        audio.PlayOneShot(audioClip);
        audio.PlayOneShot(audioClip2);
     

        float lifeCycle = Mathf.Max(audioClip.length, audioClip2.length);
        Destroy(gameObject, lifeCycle);
    }

audioClip 둘중 더 긴 시간이 지난뒤에 물체를 파괴하면 정상적으로 소리가 들린뒤에 파괴된다

이렇게 만드는게 맞을까?

Animation 이런것도 있는데 사운드만 가지고 생명주기를 만드는건 올바르지 않다
AudioSource를 물쳬(여기서는 큐브)가 아니라 사운드 매니저에 넣어두면 더 괜찮지 않을까?


📌사운드 매니저


Enum

 public enum Sound
    {
        Bgm,
        Effect,
        MaxCount,
    }

enum으로 Bgm/Effect를 구분한다

초기세팅

AudioSource는 new로 만들지 못하기 때문에 아래와 같이 Init으로 만들어줘야한다

AudioSource[] _audioSources = new AudioSource[((int)Define.Sound.MaxCount)];
    public void Init()
    {
        //Sound Manager를 담아둘 게임오브젝트만들기 
        GameObject root = GameObject.Find("@Sound");
        if(root == null)
        {
            root = new GameObject { name = "@Sound" };
            Object.DontDestroyOnLoad(root);

            //Define.Sound 목록의 이름가진 게임오브젝트만듬
            string[] soundNames = System.Enum.GetNames(typeof(Define.Sound));
            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)Define.Sound.Bgm].loop = true;
        }

    }

Play만들기

//음반의 경로, 속도조절
    public void Play(string path, Define.Sound type = Define.Sound.Effect, float pitch = 1.0f)
    {
        //Sounds라는 경로가 없다면 Sounds경로를 넣어줌
        if(path.Contains("Sounds/") == false)
            path = $"Sounds/{path}";

        //bgm같은 경우는 루프 돌려야 하기 때문에
        if(type == Define.Sound.Bgm)
        {
            AudioClip audioClip = Managers.Resource.Load<AudioClip>(path);

            //혹시 audioClip이 null이라면 
            if(audioClip == null)
            {
                Debug.Log($"AudioClip Massing ! {path}");
                return;
            }

            //이미 실행된 BGM이 있다면 멈추기
            AudioSource audioSource = _audioSources[(int)Define.Sound.Bgm];
            if (audioSource.isPlaying)
                audioSource.Stop();

            audioSource.pitch = pitch;
            audioSource.clip = audioClip;
            audioSource.Play();
        }
        //단발성 사운드
        else
        {
            AudioClip audioClip = Managers.Resource.Load<AudioClip>(path);

            //혹시 audioClip이 null이라면 
            if (audioClip == null)
            {
                Debug.Log($"AudioClip Massing ! {path}");
                return;
            }

            AudioSource audioSource = _audioSources[(int)Define.Sound.Effect];
            audioSource.pitch = pitch;
            audioSource.PlayOneShot(audioClip);
        }
    }

사용하기

매번 OnTriggerEnter가 작동하면 BGM이 바뀌게 된다

int i = 0;
    private void OnTriggerEnter(Collider other)
    {
        i++;
        if(i % 2 == 0)
            Managers.Sound.Play("Unitychan/univ0001", Define.Sound.Bgm);
        else
            Managers.Sound.Play("Unitychan/univ0002", Define.Sound.Bgm);

    }

그냥 Effect는 아래와 같이 사용하면 된다

 Managers.Sound.Play("Unitychan/univ0003");

캐싱역할 만들기

단발성 사운드는 여러번 실행되기에 매번 Load해오기에는 메모리에 부담가서 캐싱이 필요하다

AudioClip audioClip = Managers.Resource.Load<AudioClip>(path);

저장하기 위해서 Dictionary를 추가해준다

Dictionary<string, AudioClip> _audioClips = new Dictionary<string, AudioClip>();

Dictionary에 저장된 오디오클립이 없다면 추가될 것이며
있다면 기록에 남아있는것을 반환한다

 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;
    }

그리고 단발성 사운드는

 //단발성 사운드
        else
        {
            //단발성 사운드는 여러번 실행되기에 매번 Load해오기에는 메모리에 부담간다 캐싱이 필요하다
            AudioClip audioClip = GetOrAddAudioClip(path);

            //혹시 audioClip이 null이라면 
            if (audioClip == null)
            {
                Debug.Log($"AudioClip Massing ! {path}");
                return;
            }

            AudioSource audioSource = _audioSources[(int)Define.Sound.Effect];
            audioSource.pitch = pitch;
            audioSource.PlayOneShot(audioClip);
        }

이렇게 구현하면 조금더 메모리의 이점을 가져올 수 있다

문제점

DontDestroyOnLoad에 캐싱역할을 하는 딕셔너리가 저장되어 있는데
새로운 사운드를 계속해서 누적이 될 것이다 그러다보면 메모리에 영향을 끼친다 그래서 Clear하는 부분도 추가해준다

    public void Clear()
    {
        foreach(AudioSource audioSource in _audioSources)
        {
            audioSource.clip = null;
            audioSource.Stop();
        }
        _audioClips.Clear();
    }

씬이 바뀔때 Clear해야 하지만 Sound 뿐만 아니라 다른것도 Clear를 만들 수 가 있어서 Manager에 Clear부분을 만들어둔다

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

Manager를 사용해서 지금현재 처리 안되는 부분2가지
1.3D사운드(물체가 멀어지면 멀어질수록 소리 작아지게)
2.유저가 스킬 시전하는도중에 채널링 사운드가 날 때 죽거나 움직여서 그 사운드를 실시간으로 끊기


📌3D 사운드


Spatial Blend를 Right로 놔두면 3D사운드 구현이 가능하고
아래 그래프로 거리에 따라서 어떻게 들릴지 표현하는 그래프


참고자료

Part3: 유니티 엔진
섹션 9.Sound(사운드)

profile
개인공부저장용(하루의 기록)

0개의 댓글