using UnityEngine; [RequireComponent(typeof(AudioSource))] public class SoundManager : MonoBehaviour { // 전역 어디서나 효과음을 호출할 수 있는 싱글톤 인스턴스 public static SoundManager Instance { get; private set; } [Header("효과음 오디오 클립 등록 슬롯")] public AudioClip buttonClickClip; // 버튼 클릭음 (button sound 1/2) public AudioClip attackSwingClip; // 플레이어/적 공격 휘두르기 소리 public AudioClip punchHitClip; // 피격 타격음 (Punch Medium Big) public AudioClip enemyDeathClip; // 적 사망음 (monster dead) public AudioClip playerDeathClip; // 플레이어 사망음 (Dead) public AudioClip transitionClip; // 성벽 통과 씬 전환음 (go to castle background1) public AudioClip typingClip; // 대화창 글자 타이핑음 (button sound 1 등) [Header("타자기 효과음 풀링 설정")] [SerializeField] private int typingPoolSize = 5; // 동시 중첩 재생 가능한 최대 채널 수 private AudioSource[] typingAudioPool; private int nextPoolIndex = 0; private AudioSource globalAudioSource; private void Awake() { // 싱글톤 보장 if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); globalAudioSource = GetComponent(); if (globalAudioSource == null) { globalAudioSource = gameObject.AddComponent(); } // 시작 시 타자기 효과음용 오디오 풀 컴포넌트를 자식으로 미리 생성해 둡니다. (영구 재사용) InitializeTypingPool(); } // 오브젝트 가비지를 줄이기 위한 오디오 풀링 초기화 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); // SoundManager 자식으로 묶어 구조 정돈 AudioSource source = child.AddComponent(); source.playOnAwake = false; source.loop = false; typingAudioPool[i] = source; } } // 기본 단발성 효과음 재생 메서드 public void PlayOneShot(AudioClip clip, float volume = 1f) { if (clip != null && globalAudioSource != null) { globalAudioSource.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(); } } }