게임 사운드는 크게 배경과 이펙트 두가지로 나뉜다. 이때 Unity엔진에서는 용량 및 성능을 위한 설정들을 제공한다. 이펙트와 배경 사운드를 어떻게 설정하는 것이 좋은지, 그리고 스크립트에서 어떻게 사운드를 다루는지 살펴보자.
그 전에 오디오클립의 속성들이 어떤 의미인지 유니티 공식 가이드를 통해, 숙지하는 것이 좋다.
https://docs.unity3d.com/kr/2018.4/Manual/class-AudioClip.html
1. 이펙트 사운드
단발성 사운드들이 이펙트 사운드라고 생각하면 된다. 즉 동전 얻는 소리, 폭탄이 터지는 소리 등, 비교적 짧은 소리들이다.
우선 [Override for Android]를 체크 해 준다. [Decompress On Load]는 메모리를 더 잡아 먹지만, cpu 사용을 줄인다. 때문에 작은 용량의 사운드 파일에 적합하다.
[Preload Audio Data]를 체크해, Scene이 로드 될 때 같이 로드 되도록 한다.
압축 포맷은 빌드 시, sound파일을 압축 방법을 정하는 것인데,
[PCM]은 음질이 좋은 대신에, 파일의 크기가 더 크다.
[ADPCM]은 음질이 좀 떨어지는 대신에, 파일 크기가 3.5배 작다. 폭탄 소리처럼 노이즈가 많은 경우 사용하면 좋다.
Sample Rate Setting은 굳이 건드리지 않았다. 즉 이펙트 사운드의 경우
- [Decompress On Load] + [Preload Audio Data체크] + [ADPCM]
- [Decompress On Load] + [Preload Audio Data체크] + [PCM]
두 가지 중 선택해 사용하는 것이 좋다.
2. 배경 사운드
사운드 플레이 타임이 긴 경우 배경사운드로 취급한다(30초 이상?)
[Load In Backgound]를 사용해, 별도의 스레드로 사운드를 로드하도록 한다. 재생이 조금 지연 될 수 있지만 거의 느껴지지 않는다.
Load Type를 [Streaming]으로 설정해, 메모리 사용을 최소로 한다.
압축 타입
[Vorbis]은 PCM에 비해, 음질은 떨어지지만, 파일 크기가 작아진다. Quality가 100이면 PCM과 같다.
3. 재생
사운드 파일을 재생하기 위해서는, [Audio Source]컴포넌트를 GameObject에 추가하고, 사용할 사운드 파일을 등록하면 된다.
위의 이미지는 AudioSource를 메인 카메라에 등록하고, AudioClip에 재생할 배경음악을 등록한 것이다. 보통 메인 카메라에 Audio Listener가 있는데, 이곳에 사운드가 나오는 스피커가 있다고 생각하면 된다.
배경 음악의 경우는 메인 카메라에 등록하는 하면 되는데, 문제는 이펙트 사운드이다. 이때 좀 고민이 많이 된다.
1. 이펙트 오브젝트 별로 AudioSource를 등록
2. 메인 카메라에 AudioSource를 등록하고, 그때 그때 이펙트 사운드 클립을 교체
1번의 경우, 3d세팅으로 거리가 멀어짐에 따라 소리의 크기를 쉽게 설정할 수 있다. 예를 들어 멀리서 발생한 폭발 소리의 경우 작게 들려야 하는데, 이 경우, [3D Sound Settings]에서 쉽게 설정 할 수 있다. 하지만 오브젝트마다 AudioSource를 직접 추가해야하는 귀찮음이 있다.
2번의 경우는, 싱글톤 클래스를 구현하고, 한곳에서 사운드를 재생해, 전반적인 게임 내 사운드 시스템을 한 곳에서 관리할 수 있다는 장점이 있다. 하지만 앞서 말 한 것 처럼 [3D Sound Settings]의 이점을 사용하기엔 부적절 해, 직접 코드상에서 거리에 따른 음량을 조절해야 하는 단점이 있다.
나는 고민 끝에 2번의 경우로 선택했다. 오브젝트마다 AudioSource를 직접 추가해야하는 귀찮아서 그렇게 했다. 아래는 게임 내 사운드 시스템을 관리할 싱글톤 클래스 이다. 나는 이 스크립트를 메인카메라에 추가해 사용했다.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
public class SoundManager : MonoBehaviour
{
public static SoundManager Instance;
public AudioSource m_audio_bgm;
public AudioSource m_audio_effect;
bool m_enable;
//로드한 AudioClip을 재사용하기 위해
Dictionary<string, AudioClip> m_dic_audio_clip = new Dictionary<string, AudioClip>();
private void Awake()
{
if (Instance == null)
Instance = this;
}
private void Start()
{
int enable = PlayerPrefs.GetInt("SOUND_ENABLE", 1);
Enable = enable != 0;
}
//사운드 음소거 구현 할 경우
public bool Enable
{
get
{
return m_enable;
}
set
{
m_enable = value;
if (m_enable)
{
m_audio_bgm.mute = false;
m_audio_effect.mute = false;
PlayerPrefs.SetInt("SOUND_ENABLE", 1);
PlayerPrefs.Save();
}
else
{
m_audio_bgm.mute = true;
m_audio_effect.mute = true;
PlayerPrefs.SetInt("SOUND_ENABLE", 0);
PlayerPrefs.Save();
}
}
}
//volume은 오브젝트와 AudioListner간의 거리를 계산해 조절한다
public void PlayEffect(string name, float volume = 1.0f)
{
if (volume < 0.05f)
return;
if (volume > 1.0f)
volume = 1.0f;
if (Enable)
{
//오디오 클립을 캐싱한다.
if(!m_dic_audio_clip.ContainsKey(name))
{
//caching
m_dic_audio_clip[name] = Resources.Load(string.Format("Sounds/{0}", name)) as AudioClip;
}
m_audio_effect.PlayOneShot(m_dic_audio_clip[name], volume);
}
}
//중간 크기의 사운드 파일. 5초~10초가량의 사운드의 경우
//이 경우에만 AudioSource를 직접 오브젝트에 추가해 사용하고 있다.
public void PlayEffectAudioSource(AudioSource source, float volume = 1.0f)
{
if (volume < 0.05f)
return;
if (volume > 1.0f)
volume = 1.0f;
if (Enable)
{
source.volume = volume;
source.Play();
}
}
public void PlayBgm(string name = "", float volume = 0.3f)
{
if (volume > 1.0f)
volume = 1.0f;
if (Enable)
{
m_audio_bgm.volume = volume;
if (String.IsNullOrEmpty(name))
{
m_audio_bgm.Play();
return;
}
//오디오 클립을 캐싱한다.
if (!m_dic_audio_clip.ContainsKey(name))
{
//caching
m_dic_audio_clip[name] = Resources.Load(string.Format("Sounds/{0}", name)) as AudioClip;
if (m_dic_audio_clip[name] == null)
LogManager.Debug("Sound Load Faile, " + name);
}
m_audio_bgm.clip = m_dic_audio_clip[name];
m_audio_bgm.Play();
}
}
public void StopBgm()
{
m_audio_bgm.Stop();
}
}
이펙트 같은 경우 PlayOneShot을 사용했다. 이펙트 사운드 들이 중첩 되더라도 PlayOneShot을 사용하면 중간에 끊기지 않고 중첩해 잘 들린다.
'게임을 만들자 > Unity' 카테고리의 다른 글
Unity Json Parser. IL2CPP 에러 해결 (0) | 2020.04.24 |
---|---|
Unity, Shadero 2D Shader(쉐이더) 어셋 With Spine (1) | 2020.03.27 |
Unity New UI Builder 사용하기 (1) | 2020.03.11 |
Unity, Tiled 사용하기 (0) | 2020.03.07 |
Unity 2D 애니메이션 V2, 스켈레톤, PSB (0) | 2020.02.13 |