From 64f3408d4eb1077cf2a51fa848155ad1a9ba5d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=86=E1=85=B5=E1=86=AB?= =?UTF-8?q?=E1=84=80=E1=85=AE?= Date: Wed, 8 Jul 2026 05:42:29 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B0=B0=EA=B2=BD=EC=9D=8C=EC=95=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- unity-client/Assets/Scenes/Lobby.unity | 3 + unity-client/Assets/Scripts/SoundManager.cs | 110 ++++++++++++++++---- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/unity-client/Assets/Scenes/Lobby.unity b/unity-client/Assets/Scenes/Lobby.unity index 1682b4e..63a92f7 100644 --- a/unity-client/Assets/Scenes/Lobby.unity +++ b/unity-client/Assets/Scenes/Lobby.unity @@ -59571,6 +59571,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 99e32a34df5b44d91a7f5280093cd383, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::SoundManager + lobbyBgmClip: {fileID: 8300000, guid: 39f24fa81a3ca4c43bed61b4ff4a7228, type: 3} + battleBgmClip: {fileID: 8300000, guid: 39f24fa81a3ca4c43bed61b4ff4a7228, type: 3} buttonClickClip: {fileID: 8300000, guid: a9aa2b3b2a44a784bba46038b467f994, type: 3} attackSwingClip: {fileID: 8300000, guid: 04b0b69230019a140ad1818f9970a743, type: 3} punchHitClip: {fileID: 8300000, guid: 64cf1790cb30f874590e1c96c4141691, type: 3} @@ -59578,6 +59580,7 @@ MonoBehaviour: playerDeathClip: {fileID: 8300000, guid: 291608b8d286e2a4580ff36324455ce2, type: 3} transitionClip: {fileID: 8300000, guid: 64795b87003c2324fa0814fbc336066c, type: 3} typingClip: {fileID: 8300000, guid: a4559fb7c4092fb479860fa337317ac3, type: 3} + typingPoolSize: 5 --- !u!82 &2016794388 AudioSource: m_ObjectHideFlags: 0 diff --git a/unity-client/Assets/Scripts/SoundManager.cs b/unity-client/Assets/Scripts/SoundManager.cs index 7d0a850..4bf2bfb 100644 --- a/unity-client/Assets/Scripts/SoundManager.cs +++ b/unity-client/Assets/Scripts/SoundManager.cs @@ -1,26 +1,32 @@ 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; // 버튼 클릭음 (button sound 1/2) + public AudioClip buttonClickClip; // 버튼 클릭음 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 등) + public AudioClip punchHitClip; // 피격 타격음 + public AudioClip enemyDeathClip; // 적 사망음 + public AudioClip playerDeathClip; // 플레이어 사망음 + public AudioClip transitionClip; // 성벽 통과 씬 전환음 + public AudioClip typingClip; // 대화창 글자 타이핑음 [Header("타자기 효과음 풀링 설정")] - [SerializeField] private int typingPoolSize = 5; // 동시 중첩 재생 가능한 최대 채널 수 + [SerializeField] private int typingPoolSize = 5; private AudioSource[] typingAudioPool; private int nextPoolIndex = 0; - private AudioSource globalAudioSource; + private AudioSource globalSFXSource; // 효과음 전용 소스 + private AudioSource bgmAudioSource; // BGM 전용 루프 소스 private void Awake() { @@ -34,24 +40,83 @@ public class SoundManager : MonoBehaviour Instance = this; DontDestroyOnLoad(gameObject); - globalAudioSource = GetComponent(); - if (globalAudioSource == null) + // 1. 효과음 전용 오디오 소스 설정 + globalSFXSource = GetComponent(); + if (globalSFXSource == null) { - globalAudioSource = gameObject.AddComponent(); + globalSFXSource = gameObject.AddComponent(); } - // 시작 시 타자기 효과음용 오디오 풀 컴포넌트를 자식으로 미리 생성해 둡니다. (영구 재사용) + // 2. BGM 전용 독립 오디오 소스 생성 및 셋팅 (루프 기능 및 독립 볼륨) + bgmAudioSource = gameObject.AddComponent(); + 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); // SoundManager 자식으로 묶어 구조 정돈 + child.transform.SetParent(this.transform); AudioSource source = child.AddComponent(); source.playOnAwake = false; @@ -60,16 +125,16 @@ public class SoundManager : MonoBehaviour } } - // 기본 단발성 효과음 재생 메서드 + // 기본 단발성 효과음 재생 public void PlayOneShot(AudioClip clip, float volume = 1f) { - if (clip != null && globalAudioSource != null) + if (clip != null && globalSFXSource != null) { - globalAudioSource.PlayOneShot(clip, volume); + 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); @@ -77,20 +142,19 @@ public class SoundManager : MonoBehaviour 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.volume = 0.22f; + channel.pitch = Random.Range(0.85f, 1.15f); channel.Play(); } }