사운드 매니저의 두번째 버전이 있습니다.
//SoundManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//사운드의 타입이다. 사운드를 중단을 식별하기위해 사용한다.
public enum SoundType
{
BGM,
EFFECT,
QUOTEBEEP,
STEP,
}
public class SoundManager : MonoBehaviour
{
//Core는 하이어라키에서 AudioSource가 담긴 오브젝트들을 담는 부모이다.
[Header("BGM")][SerializeField] private GameObject mBgmCore;
//BGM을 담는다.
private AudioSource[] mBgmClips;
[Header("Effect")][SerializeField] private GameObject mEffectCore;
//효과 사운드를 담는다.
private AudioSource[] mEffectClips;
[Header("QuoteBeeps")][SerializeField] private GameObject mQuoteCore;
//대사를 출력할 때 비프음을 담는다.
private AudioSource[] mQuoteBeepClips;
[Header("StepSounds")][SerializeField] private GameObject mStepCore;
//캐릭터들이 움직일 때 발이 땋에 닿는 순간에 바닥의 유형에 맞는 발소리를 재생하는 소리를 담는다.
private AudioSource[] mStepClips;
//발자국에 따라 사운드를 출력을 호출하는 매니저이다.
private StepManager mStepManager;
//옵션에서 설정된 현재 배경음악과 효과 사운드의 불륨이다. 효과는 BGM을 제외한 모든 소리의 불륨을 담당한다.
//ShowOnly는 인스펙터에서 ReadOnly 속성으로 보여주는 외부 스크립트 기능을 추가한것이다.
[ShowOnly][SerializeField] private float mCurrentBGMVolume;
[ShowOnly][SerializeField] private float mCurrentEffectVolume;
//Core의 자식들을 Clips 배열에 집어넣는다.
private void Awake()
{
mBgmClips = mBgmCore.GetComponentsInChildren<AudioSource>();
mEffectClips = mEffectCore.GetComponentsInChildren<AudioSource>();
mQuoteBeepClips = mQuoteCore.GetComponentsInChildren<AudioSource>();
mStepClips = mStepCore.GetComponentsInChildren<AudioSource>();
mStepManager = FindObjectOfType<StepManager>();
}
//특정 상황에서 대사 출력 사운드를 바꾸기 위한 부가기능 함수.
public void SwapQuoteSound(int from, int to)
{
AudioSource mTempSource = mQuoteBeepClips[from];
mQuoteBeepClips[from] = mQuoteBeepClips[to];
mQuoteBeepClips[to] = mTempSource;
}
//특수 애니메이션에서 사용할 발자국 소리를 임의로 재생하기 위한 부가기능 함수.
public void PlayCustomStepSound(StepData.StepType type, float delay, float times)
{
StartCoroutine(CustomStepSound(type, delay, times));
}
//PlayCustomStepSound(...) 함수에서 호출되어 delay초마다 times만큼 type을 재생하기 위한 코루틴
IEnumerator CustomStepSound(StepData.StepType type, float delay, float times)
{
WaitForSeconds delayWait = new WaitForSeconds(delay);
while (true)
{
--times;
mStepManager.PlayStepSound(type);
yield return delayWait;
if (times <= 0)
{
yield break;
}
}
}
//씬이 로드될 때 옵션 매니저에의해 모든 사운드 불륨을 저장된 옵션의 크기로 초기화시키는 함수.
public void InitVolumes(float bgm, float effect)
{
SetBGMVolume(bgm);
SetEffectVolume(effect);
}
//옵션을 변경할 때 소리의 불륨을 조절하는 함수
public void SetBGMVolume(float value)
{
for (int i = 0; i < mBgmClips.Length; ++i)
{
mBgmClips[i].volume = value;
}
mCurrentBGMVolume = value;
}
//옵션을 변경할 때 소리의 불륨을 조절하는 함수
public void SetEffectVolume(float value)
{
for (int i = 0; i < mEffectClips.Length; ++i)
{
mEffectClips[i].volume = value;
}
for (int i = 0; i < mQuoteBeepClips.Length; ++i)
{
mQuoteBeepClips[i].volume = value;
}
for (int i = 0; i < mStepClips.Length; ++i)
{
mStepClips[i].volume = value;
}
mCurrentEffectVolume = value;
}
//BGM을 재생한다.
public void PlayBGM(int index, bool isLoop = true)
{
mBgmClips[index].loop = isLoop;
mBgmClips[index].Play();
}
//효과 사운드를 재생한다.
public void PlayEffect(int index, bool isLoop = false)
{
mEffectClips[index].loop = isLoop;
mEffectClips[index].Play();
}
//효과 사운드를 특정 위치에서 일회성 재생한다.
public void PlayEffect(int index, Vector3 pos)
{
AudioSource.PlayClipAtPoint(mEffectClips[index].clip, pos);
}
//게임이 일시정지된 상태에서 지속적으로 호출되는 사운드가 멈춤에도 불구하고 재생되는 문제를 해결하기위해 만든 부가기능 함수.
public void PlayEffectUpdate(int index, bool isLoop = false)
{
if (Framework.ButtonManager.GetIsPauseGame()) return;
if (mEffectClips[index].isPlaying) return;
mEffectClips[index].loop = isLoop;
mEffectClips[index].Play();
}
//효과 사운드를 딜레이 초 후에 재생하기위한 함수.
public void PlayEffectDelay(int index, float delayTime)
{
StartCoroutine(PlayEffectInDelay(index, delayTime));
}
//PlayEffectDelay(...) 함수에서 호출되는 코루틴.
IEnumerator PlayEffectInDelay(int index, float delayTime)
{
yield return new WaitForSeconds(delayTime);
mEffectClips[index].Play();
}
//대사가 출력될 때 글자마다 사운드를 재생하게하는 함수.
public void PlayQuoteBeep(int index)
{
if (index == -1) return;
mQuoteBeepClips[index].Play();
}
//발자국 소리를 출력하는 함수.
public void PlayStepSound(int id)
{
mStepClips[id].Play();
}
//특정한 사운드를 중지하기 위한 함수.
public void Stop(SoundType type, int index)
{
switch (type)
{
case SoundType.BGM:
{
mBgmClips[index].Stop();
break;
}
case SoundType.EFFECT:
{
mEffectClips[index].Stop();
break;
}
case SoundType.QUOTEBEEP:
{
mQuoteBeepClips[index].Stop();
break;
}
case SoundType.STEP:
{
mStepClips[index].Stop();
break;
}
}
}
//특정한 사운드를 서서히 중지하기 위한 오버로딩 함수.
public void Stop(SoundType type, int index, float FadeSpeedMultiply)
{
StartCoroutine(StopSlowly(type, index, FadeSpeedMultiply));
}
//특정한 사운드를 서서히 중지하기 위한 코루틴.
IEnumerator StopSlowly(SoundType type, int index, float FadeSpeedMultiply)
{
AudioSource targetSource = null;
switch (type)
{
case SoundType.BGM:
{
targetSource = mBgmClips[index];
break;
}
case SoundType.EFFECT:
{
targetSource = mEffectClips[index];
break;
}
case SoundType.QUOTEBEEP:
{
targetSource = mQuoteBeepClips[index];
break;
}
case SoundType.STEP:
{
targetSource = mStepClips[index];
break;
}
}
while (true)
{
targetSource.volume -= Time.deltaTime * FadeSpeedMultiply;
if (targetSource.volume < 0.01f)
{
targetSource.Stop();
switch (type)
{
case SoundType.BGM:
{
targetSource.volume = mCurrentBGMVolume;
break;
}
default:
{
targetSource.volume = mCurrentEffectVolume;
break;
}
}
yield break;
}
yield return null;
}
}
//게임이 일시정지 되는경우 모든 사운드를 일시정지하기위한 함수.
public void PauseAllClips()
{
for (int i = 0; i < mEffectClips.Length; ++i)
{
mEffectClips[i].Pause();
}
for (int i = 0; i < mQuoteBeepClips.Length; ++i)
{
mQuoteBeepClips[i].Pause();
}
for (int i = 0; i < mStepClips.Length; ++i)
{
mStepClips[i].Pause();
}
for (int i = 0; i < mBgmClips.Length; ++i)
{
mBgmClips[i].Pause();
}
}
//게임이 일시정지에서 해제되어 일시정지된 모든 사운드의 일시정지를 해제하는 함수.
public void UnPauseAllClips()
{
for (int i = 0; i < mEffectClips.Length; ++i)
{
mEffectClips[i].UnPause();
}
for (int i = 0; i < mQuoteBeepClips.Length; ++i)
{
mQuoteBeepClips[i].UnPause();
}
for (int i = 0; i < mStepClips.Length; ++i)
{
mStepClips[i].UnPause();
}
for (int i = 0; i < mBgmClips.Length; ++i)
{
mBgmClips[i].UnPause();
}
}
}
|
cs |
public enum SoundType
- 사운드의 타입이다. 필자는 게임 내에 BGM, Effect와 별개로 QuoteBeep(대사 비프음)과 Step(발자국)소리를 더해 게임을 제작하였다.
- 사운드 타입은 언제든지 커스텀이 가능하며, 기능으로 인한 특정한 유형(번개소리, 등)을 추가하여 구현할 수 있다.
[Header("...")][SerializeField] private GameObject Core;
- BGM, Effect등의 사운드들을 씬 내의 인스펙터에 미리 등록을 해두는데, 이들을 자식으로 지니게 하는 부모 오브젝트이다. 오브젝트 자체에 기능은 없으며, 단순히 AudioSource가 담긴 오브젝트의 자식들을 가지는 부모 오브젝트이다.
- 하이어라키 구조를 보면 SoundManager.cs가 들어있는 ----Sound Manager, 그 하위에 Core가 있다. Core자체는 AudioSource 컴포넌트가 들어있는 핵심 사운드 오브젝트들을 담는 부모 오브젝트이고, 아무런 기능이 없다. 각 Core의 자식들은 AudioSource 컴포넌트를 가지고있어 사운드 재생이 가능한 오브젝트들이다.
- 각 Core오브젝트를 인스펙터에서 미리 로드시켜주어 스크립트 내에서 추가 작업이 가능하게 해준다.
private AudioSource[] mClips;
- Core의 자식들인 소리 오브젝트들을 모두 담는 Clips이다.
- mClips[index]로 index번호를 통해 사운드에 접근하도록 구성하였다.
[ShowOnly][SerializeField] private float mCurrentBGMVolume;
[ShowOnly][SerializeField] private float mCurrentEffectVolume;
- 옵션의 스크롤바를 통해 불륨을 조절할 수 있는데, 조절된 현재 값을 저장한다.
- 소리를 서서히 줄이며 중지하는 함수가 있는데, 후에 소리 불륨을 초기화하기 위해 사용한다.
- [ShowOnly][SerializeField] 는 인스펙터에서 수정이 불가능한 ReadOnly 속성으로 보여주기 위한 외부 스크립트(링크) 기능을 가져와 사용하였다.
private void Awake()
- Core 오브젝트들에 담겨져있는 자식AudioSource들을 찾아 mClips에 저장한다.
- GetComponentsInChildren<AudioSource>();로 찾아서 저장한다.
public void InitVolumes(float bgm, float effect)
- 씬이 로드될때 저장된 불륨 데이터를 OptionDataManager.cs이라는 다른 스크립트로부터 호출하여 불륨을 초기화는 함수이다.
- 게임을 플레이하며 불륨 옵션을 조절하는데, 게임을 종료 후 다시시작하면 그 값을 저장해야한다. 그 때 OptionDataManager에서 로컬로 파일을 저장해 읽은 후 이 데이터로 InitVolumes() 함수를 호출하는 구조이다.
public void SetBGMVolume(float value)
public void SetEffectVolume(float value)
- 옵션 내 불륨 스크롤바를 조절하는경우 해당 함수들이 호출되어 타입에 맞는 모든 불륨을 조절한다.
public void PlayBGM(int index, bool isLoop = true)
public void PlayEffect(int index, bool isLoop = false)
- BGM 또는 효과 사운드를 재생하는 함수이다.
- BGM은 사운드 특성을 고려해 loop가 적합하다고 판단해 isLoop 기본 매개변수를 true로 하였다.
- 반대로 Effect는 일회성이 적합하다고 판단해 isLoop 기본 매개변수를 false로 하였다.
- 하지만 예외로 loop이거나, 아니거나를 설정하여 호출할 수 있도록 하였다.
public void PlayEffect(int index, Vector3 pos)
- 효과 사운드를 특정 위치에서 일회성 재생하기 위한 추가기능의 오버로딩 함수이다.
public void PlayEffectUpdate(int index, bool isLoop = false)
- 게임이 일시정지되면 Time.deltaTime을 0으로 하여 처리하는데, 이 때 AudioClip.Pause() 인 상태에서 어딘가에 Loop가 돌고있어 PlayEffect()가 호출되면 Pause()가 무시되고 사운드가 재생된다.
- 이 문제를 해결하기위해 PlayEffectUpdate()에는 게임이 멈춰있는지 확인한 후 멈춰있다면 재생을 하지 않는 함수를 추가로 작성하였다.
- Loop가 돌아 일시정지 상태에서도 재생 가능성이 있는경우 해당 함수를 사용할 수 있도록 하였다.
public void PlayEffectDelay(int index, float delayTime)
IEnumerator PlayEffectInDelay(int index, float delayTime)
- 기획측에서 어떠한 시점에서 효과 사운드를 재생할 때, n초 후에 재생하고 싶을 때가 있다. 이런 상황에서 delayTime만큼 기다린 후 사운드를 재생하도록 하는 함수와 코루틴이다.
public void PlayQuoteBeep(int index)
- 대사가 출력될 때 글자마다 소리를 출력하기 위한 함수이다.
- QuotesManager.cs(대사 매니저)로부터 초기화되는 index로부터 호출된다. index가 -1인경우는 대사 소리를 사용하지 않는다.
public void PlayStepSound(int id)
- mStepManager로부터 호출되는 함수로 발자국 소리를 내기 위한 함수이다.
public void Stop(SoundType type, int index)
- 재생중인 사운드를 중지하기위한 함수이다. SoundType으로 하나의 함수로 모든 유형의 사운드를 중지할 수 있도록 구현하였다.
public void Stop(SoundType type, int index, float FadeSpeedMultiply)
IEnumerator StopSlowly(SoundType type, int index, float FadeSpeedMultiply)
- 재생중인 사운드를 서서히 중지하기 위한 추가 기능의 오버로드 함수이다.
- 코루틴을 호출하여 서서히 중단하게 한다.
- AudioSource targetSource = null;는 switch(type)을 매번 검사해 해당 Clips[index]를 감소시키는 방법도 가능하지만, 스크립트의 가독성의 향상과, switch문의 불필요한 지속적인 검사를 피하기위해 지역변수를 선언하여 해당 targetSource의 불륨을 줄이도록 하였다.
- if (targetSource.volume < 0.01f) 조건이 참이되면, 해당 AudioSource는 중지하고 기존 타입에 맞게 불륨을 다시 돌려놓는다.
public void PauseAllClips()
public void UnPauseAllClips()
- 게임이 일시정지될때 모든 사운드를 일시정지하고 다시 재개하기 위한 함수이다.
'unity game modules' 카테고리의 다른 글
[유니티] .obj 파일을 인터넷으로 다운로드하여 사용 (0) | 2022.07.31 |
---|---|
[유니티] 카메라 벽 통과 방지(카메라암) (0) | 2022.07.19 |
[유니티] 대사 관리 및 다국어지원 (0) | 2022.07.18 |
[유니티] 대사 말풍선 기능 (0) | 2022.07.17 |
[유니티] 오브젝트와 UI에 gif 이미지 출력 (0) | 2022.07.17 |