248 lines
7.8 KiB
C#
248 lines
7.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
using UnityEngine.Audio;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace BlueWater.Audios
|
|
{
|
|
[Serializable]
|
|
public class SfxPitch
|
|
{
|
|
[field: SerializeField]
|
|
public bool IsIgnoreTimeScale { get; set; }
|
|
|
|
[field: SerializeField]
|
|
public float PitchValue { get; set; }
|
|
|
|
public SfxPitch(bool isIgnoreTimeScale, float pitchValue)
|
|
{
|
|
IsIgnoreTimeScale = isIgnoreTimeScale;
|
|
PitchValue = pitchValue;
|
|
}
|
|
}
|
|
|
|
public class AudioManager : Singleton<AudioManager>
|
|
{
|
|
[Title("오디오 데이터")]
|
|
[SerializeField]
|
|
private BgmDataSo _bgmDataSo;
|
|
|
|
[SerializeField]
|
|
private SfxDataSo _sfxDataSo;
|
|
|
|
[SerializeField]
|
|
private int _sfxChannelCount = 32;
|
|
|
|
[Title("오디오 믹서")]
|
|
[SerializeField]
|
|
private AudioMixer _audioMixer;
|
|
|
|
[SerializeField]
|
|
private AudioMixerGroup _masterMixerGroup;
|
|
|
|
[SerializeField]
|
|
private AudioMixerGroup _bgmMixerGroup;
|
|
|
|
[SerializeField]
|
|
private AudioMixerGroup _sfxMixerGroup;
|
|
|
|
[Title("중복 사운드 처리")]
|
|
[SerializeField, Range(0f, 1f)]
|
|
private float _sfxMinInterval = 0.09f;
|
|
|
|
private Dictionary<string, AudioClip> _bgmDictionary;
|
|
private Dictionary<string, AudioClip> _sfxDictionary;
|
|
private Dictionary<AudioSource, SfxPitch> _sfxPitchDictionary;
|
|
private Dictionary<string, float> _sfxPlayTimeDictionary;
|
|
|
|
private AudioSource _bgmAudioSource;
|
|
|
|
protected override void OnAwake()
|
|
{
|
|
InitializeDictionary();
|
|
InitializeComponents();
|
|
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
}
|
|
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
StopBgm();
|
|
StopSfxAll();
|
|
}
|
|
|
|
private void InitializeDictionary()
|
|
{
|
|
_bgmDictionary = new Dictionary<string, AudioClip>(_bgmDataSo.BgmDataList.Count);
|
|
foreach (var element in _bgmDataSo.BgmDataList)
|
|
{
|
|
_bgmDictionary.Add(element.BgmName, element.Clip);
|
|
}
|
|
|
|
_sfxDictionary = new Dictionary<string, AudioClip>(_sfxDataSo.SfxDataList.Count);
|
|
foreach (var element in _sfxDataSo.SfxDataList)
|
|
{
|
|
_sfxDictionary.Add(element.SfxName, element.Clip);
|
|
}
|
|
|
|
_sfxPlayTimeDictionary = new Dictionary<string, float>(_sfxDataSo.SfxDataList.Count);
|
|
}
|
|
|
|
private void InitializeComponents()
|
|
{
|
|
_bgmAudioSource = gameObject.AddComponent<AudioSource>();
|
|
_bgmAudioSource.outputAudioMixerGroup = _bgmMixerGroup;
|
|
_bgmAudioSource.loop = true;
|
|
|
|
_sfxPitchDictionary = new Dictionary<AudioSource, SfxPitch>(_sfxChannelCount);
|
|
for (var i = 0; i < _sfxChannelCount; i++)
|
|
{
|
|
var sfxAudioSource = gameObject.AddComponent<AudioSource>();
|
|
sfxAudioSource.outputAudioMixerGroup = _sfxMixerGroup;
|
|
sfxAudioSource.loop = false;
|
|
_sfxPitchDictionary[sfxAudioSource] = new SfxPitch(false, sfxAudioSource.pitch);
|
|
}
|
|
}
|
|
|
|
public void PlayBgm(string bgmName)
|
|
{
|
|
if (_bgmDictionary.TryGetValue(bgmName, out var value))
|
|
{
|
|
_bgmAudioSource.clip = value;
|
|
_bgmAudioSource.Play();
|
|
}
|
|
else
|
|
{
|
|
print("Bgm not found: " + bgmName);
|
|
}
|
|
}
|
|
|
|
public void StopBgm()
|
|
{
|
|
_bgmAudioSource.Stop();
|
|
}
|
|
|
|
public void PlaySfx(string sfxName, bool loop = false, bool ignoreTimeScale = false, float? duration = null)
|
|
{
|
|
if (_sfxDictionary.TryGetValue(sfxName, out var value))
|
|
{
|
|
float currentTime = ignoreTimeScale ? Time.unscaledTime : Time.time;
|
|
|
|
if (_sfxPlayTimeDictionary.TryGetValue(sfxName, out var lastPlayTime))
|
|
{
|
|
if (currentTime - lastPlayTime < _sfxMinInterval)
|
|
{
|
|
return; // 최소 간격 조건 미충족 시 재생하지 않음
|
|
}
|
|
}
|
|
var availableSfxAudioSource = GetAvailableSfxAudioSource();
|
|
availableSfxAudioSource.clip = value;
|
|
availableSfxAudioSource.loop = loop;
|
|
|
|
if (ignoreTimeScale)
|
|
{
|
|
_sfxPitchDictionary[availableSfxAudioSource] = new SfxPitch(true, 1f);
|
|
availableSfxAudioSource.pitch = 1f;
|
|
}
|
|
else
|
|
{
|
|
float pitch = duration.HasValue ? value.length / duration.Value : 1f;
|
|
_sfxPitchDictionary[availableSfxAudioSource] = new SfxPitch(false, pitch);
|
|
availableSfxAudioSource.pitch = _sfxPitchDictionary[availableSfxAudioSource].PitchValue * Time.timeScale;
|
|
}
|
|
|
|
availableSfxAudioSource.Play();
|
|
|
|
_sfxPlayTimeDictionary[sfxName] = currentTime;
|
|
}
|
|
else
|
|
{
|
|
print("Sfx not found: " + sfxName);
|
|
}
|
|
}
|
|
|
|
public void StopSfx(string sfxName)
|
|
{
|
|
if (_sfxDictionary.TryGetValue(sfxName, out var clip))
|
|
{
|
|
foreach (var element in _sfxPitchDictionary.Keys)
|
|
{
|
|
if (element.clip == clip && element.isPlaying)
|
|
{
|
|
element.Stop();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("Sfx not found: " + sfxName);
|
|
}
|
|
}
|
|
|
|
public void StopSfxAll()
|
|
{
|
|
foreach (var element in _sfxPitchDictionary.Keys)
|
|
{
|
|
if (element.isPlaying)
|
|
{
|
|
element.Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
private AudioSource GetAvailableSfxAudioSource()
|
|
{
|
|
foreach (var element in _sfxPitchDictionary.Keys)
|
|
{
|
|
if (!element.isPlaying)
|
|
{
|
|
return element;
|
|
}
|
|
}
|
|
|
|
// 모든 AudioSource가 사용 중이면 첫 번째 AudioSource를 재사용
|
|
using var enumerator = _sfxPitchDictionary.Keys.GetEnumerator();
|
|
enumerator.MoveNext();
|
|
return enumerator.Current;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 0.0001 ~ 1값
|
|
/// </summary>
|
|
/// <param name="volume"></param>
|
|
public void SetMasterVolume(float volume)
|
|
{
|
|
var newVolume = Mathf.Log10(volume) * 20f;
|
|
_audioMixer.SetFloat("Master", newVolume);
|
|
}
|
|
|
|
public void SetBgmVolume(float volume)
|
|
{
|
|
var newVolume = Mathf.Log10(volume) * 20f;
|
|
_audioMixer.SetFloat("Bgm", newVolume);
|
|
}
|
|
|
|
public void SetSfxVolume(float volume)
|
|
{
|
|
var newVolume = Mathf.Log10(volume) * 20f;
|
|
_audioMixer.SetFloat("Sfx", newVolume);
|
|
}
|
|
|
|
public void SetPitchSfxAll(float pitch)
|
|
{
|
|
foreach (var element in _sfxPitchDictionary)
|
|
{
|
|
if (element.Value.IsIgnoreTimeScale) continue;
|
|
|
|
element.Key.pitch = element.Value.PitchValue * pitch;
|
|
}
|
|
}
|
|
}
|
|
} |