사운드 매니저를 통해 BGM을 변환하는 작업 도중이었다.
어제 들었던 특강을 통해 자연스러운 BGM전환에 필요한 작업인 BGMPlay용 AudioSource를 2개를 배열에 두고 하나는 소리를 줄이고, 하나는 소리를 늘리는 방식을 리팩토링 하는 것이 오전 목적이었는데,
<초기 코드>
public void BGMPlay(AudioClip clip)
{
if (bgmPlayer[0].isPlaying)
{
bgmPlayer[1].clip = clip;
StartCoroutine(BGMChanager(bgmPlayer[1], bgmPlayer[0]));
}
else
{
bgmPlayer[0].clip = clip;
StartCoroutine(BGMChanager(bgmPlayer[0], bgmPlayer[1]));
}
}
IEnumerator BGMChanager(AudioSource target, AudioSource turnOff)
{
float lerpTime = 2f; // 브금 전환에 걸릴 시간.
float curTime = 0;
target.Play();
while (curTime < lerpTime) //curTime이 lerpTime보다 작다면 계속 반복됨.
{
curTime += Time.deltaTime; // curTime에 델타타임 더해서 초를 센다.
// 타겟 볼륨은 점점 커지고, 나머지 하나는 볼륨이 작아져야함.
float targetValue = Mathf.Lerp(0, 1, curTime/lerpTime); // curTime/lerpTime 계산값은 처음엔 0이고 마지막엔 1이됨. 1이 되기까지 걸리는 시간은 lerpTime임.
float offValue = Mathf.Lerp(1, 0, curTime/lerpTime); // curTime은 lerpTime과 동일해질 때까지 점점 커지므로,
target.volume = targetValue;
turnOff.volume = offValue;
yield return null;
}
// 타겟의 볼륨은 1이 되었고, 타겟이 아니면 꺼버림.
target.volume = 1;
turnOff.Stop();
turnOff.clip = null;
}
코루틴과 Lerp를 통해 자연스럽게 두 bgm의 소리가 교차되도록 만들어진 코드이다.
문제는 특강에서 알려주신대로 빠르게 BGM이 교체될 때에 플레이어와 코루틴 관리가 없다는 것이었다.
처음에 나는 이 두 개를 하나로 생각해서 플레이어 관련해서만 코드 작업을 진행했다.
<중간 결과>
public void PlayBGM(AudioClip clip)
{
// 현재 재생 중인 오디오 소스를 추적
AudioSource activePlayer = null;
AudioSource inactivePlayer = null;
if (bgmPlayer[0].isPlaying && bgmPlayer[1].isPlaying)
{
// 두 플레이어가 모두 재생 중인 경우
if (bgmPlayer[0].volume > bgmPlayer[1].volume)
{
activePlayer = bgmPlayer[0];
inactivePlayer = bgmPlayer[1];
}
else
{
activePlayer = bgmPlayer[1];
inactivePlayer = bgmPlayer[0];
}
}
else if (bgmPlayer[0].isPlaying)
{
activePlayer = bgmPlayer[0];
inactivePlayer = bgmPlayer[1];
}
else
{
activePlayer = bgmPlayer[1];
inactivePlayer = bgmPlayer[0];
}
// 새로운 클립을 비활성 플레이어에 설정하고 전환 시작
inactivePlayer.clip = clip;
StartCoroutine(BGMChanger(inactivePlayer, activePlayer));
}
이렇게 하면 두 플레이어가 모두 재생중이라면 그들간의 소리 크기를 추적해서 소리가 큰 쪽을 줄이고 작은 쪽을 늘리는 식으로 플레이어 관리가 가능했다.
그런데 이렇게 해놓고 테스트를 하며 씬 전환을 해보니 대부분 잘 작동했지만, 간혹 오디오 클립이 아예 사라져서 작동을 안하는 것이었다.
나는 PlayBGM을 각각의 씬마다 BGMObject라는 것을 만들고 그 오브젝트의 BGMObject 스크립트가 BGM 클립을 가지고 있어서 Start메서드에서 사운드매니저 PlayBGM을 호출하도록 구성했다.
따로 버튼 할당이 없는 방식이라고 생각했기 때문이다.
이런 구조기 때문에 처음엔 호출쪽에 문제가 있었던가 의심했지만 사운드매니저가 호출 시에 Null인 경우는 없을것이라 생각했고,
이게 코루틴 쪽 문제일 수도 있겠다는 생각이 들었다.
그래서 예전에 팀원분이 적 생성 관련한 코루틴에서 고생하셨던 기억이 나서 현재 진행중인 코루틴을 필드에 담아둬서 관리하는 식으로 코드를 바꿔봤다.
private Coroutine bgmChangeCoroutine;
public void PlayBGM(AudioClip clip)
{
// 현재 재생 중인 오디오 소스를 추적
AudioSource activePlayer = null;
AudioSource inactivePlayer = null;
if (bgmPlayer[0].isPlaying && bgmPlayer[1].isPlaying)
{
// 두 플레이어가 모두 재생 중인 경우
if (bgmPlayer[0].volume > bgmPlayer[1].volume)
{
activePlayer = bgmPlayer[0];
inactivePlayer = bgmPlayer[1];
}
else
{
activePlayer = bgmPlayer[1];
inactivePlayer = bgmPlayer[0];
}
}
else if (bgmPlayer[0].isPlaying)
{
activePlayer = bgmPlayer[0];
inactivePlayer = bgmPlayer[1];
}
else
{
activePlayer = bgmPlayer[1];
inactivePlayer = bgmPlayer[0];
}
// 기존 코루틴 중지
if (bgmChangeCoroutine != null)
{
StopCoroutine(bgmChangeCoroutine);
}
// 새로운 클립을 비활성 플레이어에 설정하고 전환 시작
inactivePlayer.clip = clip;
bgmChangeCoroutine = StartCoroutine(BGMChanger(inactivePlayer, activePlayer));
}
이렇게 완성한 코드로 똑같이 씬을 반복해서 빠르게 넘어가는 테스트를 해봤는데, 다행히 한 번도 기존처럼 클립이 사라지는 현상이 없어졌다.
특강에서 써주신 것처럼 player관리와 코루틴 관리 두 개를 모두 적용해서 리팩토링을 하는게 정답이었던 것이다.