사운드를 직접 제작하거나 할 수 없기때문에 최대한 저작권 없는 무료 소스들만을 가지고 사운드를 넣어봤다.
그래서 원하는 사운드를 구하지 못해 보스전에서의 사운드는 많이 없는 상태이다.
이 외에도 추가적인 사운드 들이 존재한다.
이것들을 관리하기 위해 사운드 매니저를 만들어준다.
SoundManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SoundManager
{
AudioSource[] _audioSources = new AudioSource[(int)Define.Sound.MaxCount];
Dictionary<string, AudioClip> _audioClips = new Dictionary<string, AudioClip>();
Dictionary<string, AudioClip> _audioBgms = new Dictionary<string, AudioClip>();
public void Init()
{
GameObject root = GameObject.Find("@Sound");
if (root == null)
{
root = new GameObject { name = "@Sound" };
Object.DontDestroyOnLoad(root);
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;
}
}
public void Clear()
{
for(int i = 1; i < (int)Define.Sound.MaxCount; i++)
{
_audioSources[i].clip = null;
_audioSources[i].Stop();
}
_audioClips.Clear();
}
public void Play(string path, Define.Sound type = Define.Sound.Effect, AudioSource objectAudio = null ,float pitch = 1.0f)
{
AudioClip audioClip = GetOrAddAudioClip(path, type);
Play(audioClip, type, objectAudio, pitch);
}
public void Play(AudioClip audioClip, Define.Sound type = Define.Sound.Effect, AudioSource objectAudio = null, float pitch = 1.0f)
{
if (audioClip == null)
return;
if (type == Define.Sound.Bgm)
{
AudioSource audioSource = _audioSources[(int)Define.Sound.Bgm];
if (audioSource.clip != null && audioSource.clip.name.Equals(audioClip.name) == true)
return;
Managers.Instance.BgmSoundChange(audioSource, audioClip, pitch, 1);
}
else
{
if(objectAudio == null)
{
AudioSource audioSource = _audioSources[(int)Define.Sound.Effect];
audioSource.pitch = pitch;
audioSource.PlayOneShot(audioClip);
}
else
{
objectAudio.pitch = pitch;
objectAudio.PlayOneShot(audioClip);
}
}
}
AudioClip GetOrAddAudioClip(string path, Define.Sound type = Define.Sound.Effect)
{
if (path.Contains("Sounds/") == false)
path = $"Sounds/{path}";
AudioClip audioClip = null;
if (type == Define.Sound.Bgm)
{
if (_audioBgms.TryGetValue(path, out audioClip) == false)
{
audioClip = Managers.Resource.Load<AudioClip>(path);
_audioBgms.Add(path, audioClip);
}
}
else
{
if (_audioClips.TryGetValue(path, out audioClip) == false)
{
audioClip = Managers.Resource.Load<AudioClip>(path);
_audioClips.Add(path, audioClip);
}
}
if (audioClip == null)
Debug.Log($"AudioClip Missing ! {path}");
return audioClip;
}
public void SetAudioVolume(Define.Sound type, float volume)
{
AudioSource audioSource = null;
if (type == Define.Sound.Bgm)
{
audioSource = _audioSources[(int)Define.Sound.Bgm];
Managers.Instance.data.bgmVolume = volume;
}
else if(type == Define.Sound.Effect)
{
audioSource = _audioSources[(int)Define.Sound.Effect];
Managers.Instance.data.eftVolume = volume;
}
if (audioSource != null)
audioSource.volume = volume;
}
public void SetBgmLoading(string path, AudioClip audioClip)
{
if(_audioBgms.ContainsKey(path) == false)
_audioBgms.Add(path, audioClip);
}
}
이렇게 해서 우리는 Play 인터페이스만 숙지하면 모든 사운드를 컨트롤 할 수 있다.
중요한 점은 사운드를 캐싱한다는 점이다.
일반적으로 오디오를 사용하는 경우는 크게 두가지로 나눠볼 수 있다.
배경음악과 효과음 으로 나눌 수 있는데, 배경음악의 경우 2분에서 3분 내지의 사운드가 Loop하는 형식이기에 배경음악을 Load하는 것이 게임에 딜레이를 줄 수 있다. 또한 효과음의 경우 1초 내의 짧은 수많은 효과음이 계속 실행 될 터인데 이때마다 Load하는 것 역시 과부하를 줄 수 있기 때문에 Dictionary에 캐싱해 두었다가 한번 Load된 효과음을 불러오는 형식으로 메모리에 올려두는 것이다.
문제는 만약 여러 씬을 돌아다니면서 계속 저 배경음과 효과음 Dictionary에 계속 쌓일 수 있다. (배경음악은 현재 3개 밖에 사용하지 않아서 3개를 메모리에 전체 들고 있어도 지금 당장에는 문제를 일으키지 않는다.) 그렇기 때문에 씬이 넘어갈 때마다 Dictionary를 초기화해서 그 씬에서만 사용할 효과음들만 담아두는 방식을 채택했다.
사용하기
실질적인 사용은 다음과 같이한다.
Managers.Sound.Play("LoginBgm", Define.Sound.Bgm);
public AudioSource audioSource;
public void SfxSoundPlay(string name)
{
Managers.Sound.Play(name, Define.Sound.Effect, audioSource);
}
효과음의 경우 3D 게임 답게 사운드를 3D 사운드를 지원하기 때문에 오디오 소리를 내는 주체가 필요하기 때문에 추가적인 변수를 넣어준 것이다.
볼륨 조절하기와 Bgm 부드럽게 바꾸기
먼저 볼륨을 조절할 UI를 만들어준다.
using DG.Tweening;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
using Google.Protobuf.Protocol;
public class UI_Setting_Popup : MonoBehaviour
{
public Slider bgm;
public Slider eft;
public TextMeshProUGUI text;
string SceneName;
public void Start()
{
bgm.value = Managers.Instance.data.bgmVolume;
eft.value = Managers.Instance.data.eftVolume;
SceneName = SceneManager.GetActiveScene().name;
if (SceneName.Equals("Game") || SceneName.Equals("Boss"))
{
text.text = "캐릭터 선택창";
}
else
{
text.text = "게임 종료";
}
GetComponent<CanvasGroup>().DOFade(1f, 0.5f);
}
IEnumerator FadeOutCanvas()
{
CanvasGroup cg = GetComponent<CanvasGroup>();
Tween tw = cg.DOFade(0f, 0.5f);
yield return tw.WaitForCompletion();
Destroy(gameObject);
}
public void ExitBtn()
{
Managers.Sound.Play("ButtonClick");
StartCoroutine(FadeOutCanvas());
}
public void ChangeBgmValue()
{
float value = bgm.value;
Managers.Sound.SetAudioVolume(Define.Sound.Bgm, value);
}
public void ChangeEftValue()
{
float value = eft.value;
Managers.Sound.SetAudioVolume(Define.Sound.Effect, value);
}
public void GameEndBtn()
{
if(SceneName.Equals("Game") || SceneName.Equals("Boss"))
{
C_RequestLeaveGame leaveGame = new C_RequestLeaveGame();
leaveGame.ObjectId = Managers.Object.MyPlayer.Id;
Managers.Network.Send(leaveGame);
}
else
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit(); // 어플리케이션 종료
#endif
}
}
}
이렇게 해서 슬라이드에 값이 변화됐을 때 값을 SetAudioVolume함수를 통해 볼륨을 설정해 줄 수 있다.
또한 Play를 통해 오디오를 재생할 때 페이드 인 아웃 효과를 주기위해 함수를 작성한다.
public void BgmSoundChange(AudioSource audioSource, AudioClip newClip, float pitch = 1.0f, float fadeTime = 1.0f)
{
StartCoroutine(FadeInNewBGM(audioSource, newClip, pitch, fadeTime));
}
IEnumerator FadeOutOldBGM(AudioSource audioSource, float fadeTime = 1.0f)
{
float startVolume = audioSource.volume;
for (float t = 0; t < fadeTime; t += Time.deltaTime)
{
audioSource.volume = startVolume * (1 - t / fadeTime);
yield return null;
}
audioSource.Stop();
audioSource.volume = startVolume; // reset volume
}
IEnumerator FadeInNewBGM(AudioSource audioSource, AudioClip newClip, float pitch = 1.0f, float fadeTime = 1.0f)
{
yield return StartCoroutine(FadeOutOldBGM(audioSource, fadeTime));
audioSource.clip = newClip;
audioSource.pitch = pitch;
audioSource.Play();
audioSource.volume = 0;
for (float t = 0; t < fadeTime; t += Time.deltaTime)
{
audioSource.volume = t / fadeTime;
if (audioSource.volume >= data.bgmVolume)
break;
yield return null;
}
audioSource.volume = data.bgmVolume;
}
BGM 볼륨을 0까지 천천히 내리고(1초) bgm을 바꾼 뒤 내가 설정해둔 볼륨의 크기까지 다시 키우는 방식을 통해 fade를 구현했다.
개발은 사운드를 마지막으로 다음 포스팅이 마지막 일 것같다. 지금까지 만든 게임을 소개하는 영상을 업로드할 것이다.
'Unity > 온라인 RPG' 카테고리의 다른 글
[Unity 3D] 퀘스트 DB에 저장하기 (0) | 2024.08.14 |
---|---|
[Unity 3D] 퀘스트 시스템 구현하기 (1) | 2024.08.06 |
[Unity 3D] 보스 드래곤의 패턴 만들기 (0) | 2024.07.29 |
[Unity 3D] 보스 원정대 꾸리기 + 보스 컷신 (3) | 2024.07.11 |
[Unity 3D] 클라이언트, 서버 최적화 (0) | 2024.07.04 |