162 lines
5.5 KiB
C#
162 lines
5.5 KiB
C#
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
[RequireComponent(typeof(AudioSource))]
|
|
public class SoundManager : MonoBehaviour
|
|
{
|
|
// 전역 어디서나 효과음과 BGM을 호출할 수 있는 싱글톤 인스턴스
|
|
public static SoundManager Instance { get; private set; }
|
|
|
|
[Header("BGM 배경음악 오디오 클립 등록")]
|
|
public AudioClip lobbyBgmClip; // 로비 씬 배경음악
|
|
public AudioClip battleBgmClip; // 배틀 스테이지 배경음악
|
|
|
|
[Header("효과음 오디오 클립 등록 슬롯")]
|
|
public AudioClip buttonClickClip; // 버튼 클릭음
|
|
public AudioClip attackSwingClip; // 플레이어/적 공격 휘두르기 소리
|
|
public AudioClip punchHitClip; // 피격 타격음
|
|
public AudioClip enemyDeathClip; // 적 사망음
|
|
public AudioClip playerDeathClip; // 플레이어 사망음
|
|
public AudioClip transitionClip; // 성벽 통과 씬 전환음
|
|
public AudioClip typingClip; // 대화창 글자 타이핑음
|
|
|
|
[Header("타자기 효과음 풀링 설정")]
|
|
[SerializeField] private int typingPoolSize = 5;
|
|
private AudioSource[] typingAudioPool;
|
|
private int nextPoolIndex = 0;
|
|
|
|
private AudioSource globalSFXSource; // 효과음 전용 소스
|
|
private AudioSource bgmAudioSource; // BGM 전용 루프 소스
|
|
|
|
private void Awake()
|
|
{
|
|
// 싱글톤 보장
|
|
if (Instance != null && Instance != this)
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
|
|
// 1. 효과음 전용 오디오 소스 설정
|
|
globalSFXSource = GetComponent<AudioSource>();
|
|
if (globalSFXSource == null)
|
|
{
|
|
globalSFXSource = gameObject.AddComponent<AudioSource>();
|
|
}
|
|
|
|
// 2. BGM 전용 독립 오디오 소스 생성 및 셋팅 (루프 기능 및 독립 볼륨)
|
|
bgmAudioSource = gameObject.AddComponent<AudioSource>();
|
|
bgmAudioSource.playOnAwake = false;
|
|
bgmAudioSource.loop = true;
|
|
|
|
// 3. 효과음 풀링 초기화
|
|
InitializeTypingPool();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
// 유니티 씬 로드 완료 이벤트를 리스닝합니다. (씬 전환 시 BGM 자동 매핑용)
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
// 이벤트 해제
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
}
|
|
|
|
// 씬이 완벽하게 로드되었을 때 실행되는 콜백 함수
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
PlayBgmForScene(scene.name);
|
|
}
|
|
|
|
// 씬 이름 기반 배경음악 교체 메서드
|
|
public void PlayBgmForScene(string sceneName)
|
|
{
|
|
AudioClip targetBgm = null;
|
|
|
|
// 씬 이름과 매치되는 배경음악 선별
|
|
if (sceneName == "Lobby")
|
|
{
|
|
targetBgm = lobbyBgmClip;
|
|
}
|
|
else if (sceneName == "battleStage")
|
|
{
|
|
targetBgm = battleBgmClip;
|
|
}
|
|
|
|
if (bgmAudioSource == null) return;
|
|
|
|
// 현재 이미 똑같은 배경음악이 돌고 있다면 끊김 없이 연주 유지
|
|
if (bgmAudioSource.clip == targetBgm && bgmAudioSource.isPlaying) return;
|
|
|
|
// 이전 배경음악 정지 및 새 음악 매핑 후 루프 재생
|
|
bgmAudioSource.Stop();
|
|
bgmAudioSource.clip = targetBgm;
|
|
|
|
if (targetBgm != null)
|
|
{
|
|
bgmAudioSource.volume = 0.45f; // 효과음에 비해 잔잔하도록 볼륨 기본 설정
|
|
bgmAudioSource.Play();
|
|
Debug.Log($"[SoundManager] '{sceneName}' 씬 진입 감지 -> BGM 자동 재생: {targetBgm.name}");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"[SoundManager] '{sceneName}' 씬에 등록된 BGM 오디오 클립이 없습니다.");
|
|
}
|
|
}
|
|
|
|
private void InitializeTypingPool()
|
|
{
|
|
typingAudioPool = new AudioSource[typingPoolSize];
|
|
for (int i = 0; i < typingPoolSize; i++)
|
|
{
|
|
GameObject child = new GameObject($"TypingAudioChannel_{i}");
|
|
child.transform.SetParent(this.transform);
|
|
|
|
AudioSource source = child.AddComponent<AudioSource>();
|
|
source.playOnAwake = false;
|
|
source.loop = false;
|
|
typingAudioPool[i] = source;
|
|
}
|
|
}
|
|
|
|
// 기본 단발성 효과음 재생
|
|
public void PlayOneShot(AudioClip clip, float volume = 1f)
|
|
{
|
|
if (clip != null && globalSFXSource != null)
|
|
{
|
|
globalSFXSource.PlayOneShot(clip, volume);
|
|
}
|
|
}
|
|
|
|
// 효과음 바인딩 메서드들
|
|
public void PlayButtonClick() => PlayOneShot(buttonClickClip, 0.7f);
|
|
public void PlayAttackSwing() => PlayOneShot(attackSwingClip, 0.85f);
|
|
public void PlayPunchHit() => PlayOneShot(punchHitClip, 0.95f);
|
|
public void PlayEnemyDeath() => PlayOneShot(enemyDeathClip, 1.0f);
|
|
public void PlayPlayerDeath() => PlayOneShot(playerDeathClip, 1.0f);
|
|
public void PlayTransition() => PlayOneShot(transitionClip, 0.9f);
|
|
|
|
// 타자기 효과음
|
|
public void PlayTyping()
|
|
{
|
|
if (typingClip == null || typingAudioPool == null || typingAudioPool.Length == 0) return;
|
|
|
|
AudioSource channel = typingAudioPool[nextPoolIndex];
|
|
nextPoolIndex = (nextPoolIndex + 1) % typingPoolSize;
|
|
|
|
if (channel != null)
|
|
{
|
|
channel.clip = typingClip;
|
|
channel.volume = 0.22f;
|
|
channel.pitch = Random.Range(0.85f, 1.15f);
|
|
channel.Play();
|
|
}
|
|
}
|
|
}
|