본문 바로가기

[unity] 모듈 제작 : 사운드 플레이어 만들기(1)

앤디가이 2022. 5. 16.

앱 및 게임 제작 시 공통으로 사용하는 모듈을 제작해보자.  첫번째 제작 모듈은 사운드 플레이어 모듈이다. 사운드 플레이 기능은 각 씬이나 화면에서 공통으로 사용할 수 있게 모듈화 해 놓으면 상당히 유용하다.

 

1. 사운드 플레이어 모듈 제작 준비

 - 유니티 버전은 2019 LTS 버전을 사용한다. (상위 버전에서도 큰 문제는 없다.)

 - Assets/Moduls/Sound 폴더를 생성한다. ( 마우스 우클릭 -> Create -> Folder )

 - Sound 폴더 밑에 Prefabs와 Scripts 폴더를 생성한다. 

 

2. 사운드 플레이어 모듈 기능 정의

 - 하나의 사운드는 메모리에 올려져 있으며, 2개 이상 사운드가 동시에 출력되야 할 경우(이펙트 사운드) 하나를 추가적으로 동적으로 생성해서 재생한다.

 - 사운드는 BGM 과 Effect 로 나눠서 구분 ( BGM은 기본적으로 Loop 재생, Effect 사운드는 짧게 재생 후 소멸의 차이점을 가짐) 한다.

 - 사운드의 재생, 일시정지, 멈춤 등의 기능을 제공한다.

 - 사운드의 볼륨 제어한다.

 - 싱글톤 클래스로 제작하여, 씬 이동 간 영향을 받지 않도록 구성한다.

 

3. 사운드 플레이어 스크립트 작성

 - Scripts 폴더에 C# 스크립트인 SoundPlayer.cs를 생성한다. ( 마우스 우클릭 -> Create -> C# Script )

 - SoundPlayer.cs 의 코드를 아래와 같이 작성해 주자.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace Container.Sound
{
    /// <summary>
    /// 사운드 플레이어 클래스
    /// 싱글톤으로 관리
	/// 기본 1개 SoundObject를 사용하며, 두개 이상 동시 사운드 처리시 SoundObject 동적으로 생성
    /// </summary>
    public class SoundPlayer : MonoBehaviour
    {
        public static SoundPlayer Instance { get; private set; }

        [SerializeField]
        protected AudioSource audio_bgm = null;
        [SerializeField]
		protected SoundObject audio_basic = null;

        [SerializeField]
        protected AudioClip[] arr_AudioEffectClip = null;

        protected List<SoundObject> list_Sound = new List<SoundObject>();

        protected Dictionary<SoundType, AudioClip> dic_EffectSound = new Dictionary<SoundType, AudioClip>();

        protected readonly string SOUND_NAME = "_Sound";

        protected readonly float BGM_VOLUME = 0.5f;

        void Awake()
        {
            Instance = this;

            dic_EffectSound.Clear();

			list_Sound.Add(audio_basic);

            for (int i = 0; i < arr_AudioEffectClip.Length; i++)
            {
                dic_EffectSound.Add((SoundType)Enum.Parse(typeof(SoundType), arr_AudioEffectClip[i].name), arr_AudioEffectClip[i]);
            }

        }

        /// <summary>
        /// This function is called when the MonoBehaviour will be destroyed.
        /// </summary>
        void OnDestroy()
        {
            Instance = null;
        }

        public bool IsBgmPlaying
        {
            get{
                return audio_bgm.isPlaying;
            }
        }

        /// <summary>
        /// 사운드 재생 중인지 여부.
        /// </summary>
        public bool IsPlaying()
        {
            foreach (SoundObject snd in list_Sound)
            {
				if (snd.IsPlaying)
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsPause()
        {
            if (Time.timeScale <= 0)
                return true;
            return false;
        }

        /// <summary>
        /// 사운드 재생 중인지 여부.
        /// </summary>
        public bool IsPlaying(AudioClip clip)
        {
            foreach (SoundObject snd in list_Sound)
            {
                if (snd.Clip == clip)
                {
					if (snd.IsPlaying)
                    {
                        return true;
                    }
                }
            }
            
            return false;
        }

        /// <summary>
        /// get Sound Clip
        /// </summary>
        /// <returns>The sound clip.</returns>
        /// <param name="type">Common Sound Type.</param>
        public AudioClip GetSoundClip(SoundType type)
        {
            if (dic_EffectSound.ContainsKey(type))
            {
                return dic_EffectSound[type];
            }
            else
                return null;
        }

        #region BGM

        public void PlayBGM(AudioClip clip)
        {
            PlayBGM(clip, BGM_VOLUME);
        }

        public void PlayBGM(AudioClip clip, float volume)
        {
            PlayBGM(clip, volume, true);
        }

        public void PlayBGM(AudioClip clip, float volume, bool isloop)
        {
            if (clip == null)
            {
                Debug.Log("BGM clip is null");
                return;
            }

            if(IsBgmPlaying)
            {
                audio_bgm.Stop();
            }

            audio_bgm.clip = clip;
            audio_bgm.volume = volume;
            audio_bgm.loop = isloop;
            audio_bgm.Play();
        }

        public void StopBGM()
        {
            audio_bgm.Stop();
        }

        public void PauseBGM()
        {
            if (audio_bgm.isPlaying)
                audio_bgm.Pause();
        }

        public void UnpausBGM()
        {
            audio_bgm.UnPause();
        }

        #endregion

        #region PlaySound
        /// <summary>
        /// 사운드 플레이
        /// </summary>
        /// <param name="soundType">Sound type.</param>
        public void PlaySound(SoundType soundType)
        {
            PlaySound(soundType, true);
        }

        /// <summary>
		/// 사운드 플레이
        /// </summary>
        /// <param name="soundType">Sound type.</param>
        /// <param name="isStopEnable">If set to <c>true</c> is stop enable.</param>
        public void PlaySound(SoundType soundType , bool isStopEnable)
        {
            if (soundType == SoundType.None) return;

            PlaySound(GetSoundClip(soundType), 1f, 0, false, isStopEnable, null);
        }

		public void PlaySound(SoundType soundType, float volume){
			if (soundType == SoundType.None) return;

			PlaySound(GetSoundClip(soundType), volume, null);
		}

        public void PlaySound(AudioClip clip)
        {
            PlaySound(clip, null);
        }
		public IEnumerator CoPlaySound(AudioClip clip, float minWaitSec = 0.5f){
			float waitSec = (clip != null && clip.length > minWaitSec) ? clip.length : minWaitSec;
			PlaySound(clip, null);
			yield return new WaitForSeconds(waitSec);
		}

        /// <summary>
		/// 사운드 재생
        /// </summary>
        /// <param name="clip">Clip.</param>
        /// <param name="finishListener">Finish listener.</param>
        public void PlaySound(AudioClip clip, System.Action finishListener)
        {
            PlaySound(clip, 1f, finishListener);
        }

        /// <summary>
		/// 사운드 재생
        /// </summary>
		/// <param name="clip">Audio Clip.</param>
		/// <param name="volume">Audio Volume. 0 ~ 1</param>
        /// <param name="finishListener">Finish listener.</param>
        public void PlaySound(AudioClip clip, float volume, System.Action finishListener)
        {
            PlaySound(clip, volume, 0, false, finishListener);
        }

        /// <summary>
		/// 사운드 재생
        /// </summary>
		/// <param name="clip">Audio Clip.</param>
		/// <param name="volume">Audio Volume. 0 ~ 1</param>
		/// <param name="delaySeonds">사운드 플레이 전 delay 초</param>
        /// <param name="finishListener">Finish listener.</param>
		public void PlaySound(AudioClip clip, float volume, float delaySeonds, System.Action finishListener)
        {
			PlaySound(clip, volume, delaySeonds, false, finishListener);
        }

        /// <summary>
		/// 사운드 재생
        /// </summary>
		/// <param name="clip">Audio Clip.</param>
		/// <param name="volume">Audio Volume. 0 ~ 1</param>
		/// <param name="delaySeonds">사운드 플레이 전 delay 초</param>
        /// <param name="isLoop">If set to <c>true</c> is loop.</param>
        /// <param name="finishListener">Finish listener.</param>
        public void PlaySound(AudioClip clip, float volume,float delaySeonds, bool isLoop, System.Action finishListener)
        {
			PlaySound(clip, volume, delaySeonds, isLoop, true, finishListener);
        }

        /// <summary>
		/// 사운드 재생
        /// </summary>
        /// <param name="clip">Audio Clip.</param>
        /// <param name="volume">Audio Volume. 0 ~ 1</param>
        /// <param name="delaySeconds">사운드 플레이 전 delay 초</param>
        /// <param name="isLoop">If set to <c>true</c> is loop.</param>
        /// <param name="isStopEnable">If set to <c>true</c> is stop enable.</param>
        /// <param name="finishListener">Finish listener.</param>
		public void PlaySound(AudioClip clip, float volume, float delaySeconds, bool isLoop, bool isStopEnable, System.Action finishListener)
        {

            if (clip == null)
            {
                Debug.Log("Sound Clip is Null");
                return;
            }
            
            if (IsPlaying() || IsPause())
            {            
                SoundObject obj_Sound = CreateSoundObject(clip);
                
				list_Sound.Add(obj_Sound);
				obj_Sound.Play(volume, delaySeconds, isLoop, isStopEnable, () =>
				 {
					 list_Sound.Remove(obj_Sound);
					 if (finishListener != null)
						 finishListener();
				 });
                
            }
            else
            {
				audio_basic.Clip = clip;
				audio_basic.Play(volume, delaySeconds, isLoop, isStopEnable, () =>
                {
                    if (finishListener != null)
                        finishListener();
                });
            }
        }

        #endregion

        /// <summary>
        /// Unpauses all sound.
        /// </summary>
        public void UnpauseAllSound(bool includeBGM = true)
        {
            foreach (SoundObject snd in list_Sound)
            {
                snd.unPause();
            }

            if (includeBGM)
                audio_bgm.UnPause();
         
        }


        /// <summary>
        /// Pauses all sound.
        /// </summary>
        public void PauseAllSound(bool includeBGM = true)
        {
            Debug.Log("Pause All Sound");
            foreach (SoundObject snd in list_Sound)
            {
                snd.Pause();
            }

            if (includeBGM)
                audio_bgm.Pause();
        }




        /// <summary>
        /// Stops the sound.
        /// </summary>
        /// <returns><c>true</c>, if sound was stoped, <c>false</c> otherwise.</returns>
        /// <param name="_clip">Clip.</param>
        public bool StopSound(AudioClip _clip)
        {
			for (int i = 0; i < list_Sound.Count; i++)
			{
				if(list_Sound[i].Clip == _clip)
				{
					list_Sound[i].Stop();

					if(i != 0)
					    list_Sound.Remove(list_Sound[i]);

                    return true;
				}
			}

			return false;
        }

        /// <summary>
        /// Stops all sound.
        /// 배경음은 stop 제외
        /// </summary>
        /// <returns><c>true</c>, if all sound was stoped, <c>false</c> otherwise.</returns>
        public bool StopAllSound()
        {
            return StopAllSound(false);
        }

        /// <summary>
        /// Stops all sound.
        /// </summary>
        /// <returns><c>true</c>, 배경음 포함 모든 사운드 제거 <c>false</c> 배경음 제외 모든 사운드 제거.</returns>
        /// <param name="includeBGM">If set to <c>true</c> include bgm.</param>
        public bool StopAllSound(bool includeBGM)
        {

            foreach (SoundObject snd in list_Sound)
            {
                snd.Stop();
            }

			if (includeBGM)
                audio_bgm.Stop();

            ClearAllSound();
         
            return true;
        }


        /// <summary>
        /// Clears all sound.
        /// </summary>
        protected void ClearAllSound()
        {
            list_Sound.Clear();
			list_Sound.Add(audio_basic);
        }


        /// <summary>
        /// 사운드 객체를 생성하여 리턴
        /// </summary>
        /// <param name="clip">Audio Clip</param>
        /// <returns></returns>
        protected SoundObject CreateSoundObject(AudioClip clip)
        {
            GameObject _soundOBJ = new GameObject(SOUND_NAME);
            _soundOBJ.transform.SetParent(audio_basic.transform);
            SoundObject snd = _soundOBJ.AddComponent<SoundObject>();
            snd.Clip = clip;
            return snd;

        }

    }
}

 - 싱글톤 함수이기 때문에, 씬에 해당 스크립트가 올라가져 있으면, SoundPlayer.Instance 를 통해 Instance에 접근할 수 있다.

 - PlayBGM() 함수와 PlaySound() 함수를 호출하여, 사운드를 재생시킬 수 있다.

 

댓글