본문 바로가기
Development/Internship

[멋사 로켓단 인턴쉽] 6일차 - Audio Manager 제작

by Mobics 2025. 8. 14.

목차


    멋쟁이사자처럼 로켓단 인턴쉽

    25.08.13

    회의록

    >> 안건

    • 업무 업데이트
      • 질문 혹은 피드백 요청
    • 주요 안건
      • 1회차 완전플레이 마일스톤 및 작업카드 작성 및 분담
      • 목표 개발 진행
    • 향후 마일스톤
      • 1회차 완전플레이
      • 완전 완성

     

    >> 회의 내용

    • 프로토타입 개발 완료 확인.
    • 1회차 완전플레이 개발을 위한 마일스톤 재정립
    • 일감 작업카드 생성 및 분배 후 작업시작
    • 익일 작업 분배건 내일까지 완성 목표
    • 이후부터 일감을 유동적으로 생성 및 작업하여 마일스톤 도달을 위한 개발기간 단축을 도모.

     

    새로 만들어진 UI Manager

    : 어제 일과시간이 끝난 후에 팀장 분께서 UI Manager를 새로 만드셨고, 오늘 가져오셨다. 내가 만든 UI Manager가 초라할 정도로 구조가 잘 짜여져있었다.

    --> 내가 만든 걸 부정 당한 것 같아서 많이 속상했지만 이건 공부가 아니라 일을 하는 인턴쉽이고, 정해진 시간 안에 게임을 만들어야 하는 입장에서 나의 속도와 실력으로는 해낼 수 없기에 받아들였다. 내가 더 잘하는 수 밖에 없다.

     

    >> 코드

    1. UIManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class UIManager : Singleton<UIManager>
    {
        [SerializeField] public TitleUIController titleUIController;
        [SerializeField] public InGameUIController inGameUIController;
        [SerializeField] public PopupUIController popupUIController;
        
        [SerializeField] private PanelController panelController;
        [SerializeField] private PopupController popupController;
    
        protected override void Initialize()
        {
            base.Initialize();
            
            // 초기화 로직 필요 시, 추가
        }
    
        public void SetTitleUIController(TitleUIController titleUIController)
        {
            this.titleUIController = titleUIController;
        }
        
        public void SetInGameUIController(InGameUIController inGameUIController)
        {
            this.inGameUIController = inGameUIController;
        }
        
        public void SetPopupUIController(PopupUIController popupUIController)
        {
            this.popupUIController = popupUIController;
        }
        
        public void ReleasePopupUIController()
        {
            popupUIController = null;
        }
        
        public void ShowBackgroundImage(bool isShow)
        {
            popupUIController.backgroundImage.gameObject.SetActive(isShow);
        }
    
        // Panel 관련
        public void OpenPanel(GameObject panel) => panelController.OpenPanel(panel);
        
        // Popup 관련
        public void ShowPopup(GameObject popup) => popupController.ShowPopup(popup);
        public void ClosePopup(GameObject popup) => popupController.ClosePopup(popup);
    }

     

    2. TitleUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    using UnityEngine.Serialization;
    
    public class TitleUIController : Singleton<TitleUIController>
    {
        [SerializeField] private RectTransform _titleUI;
        
        //새로 추가한 TitleUI는 이곳 아래에 추가해주시고, 프리팹의 자식개체로 넣은 뒤 인스펙터에서 할당해주세요.
        //이 싱글톤 객체를 통해 UI 접근을 용이하게 관리합니다.
        //ex. public TimerUIController timerUIController;
        public MainMenuUIController mainMenuUIController;
        public SubMenuUIController subMenuUIController;
        
        //여기까지
        
        protected override void Initialize()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            UIManager.Instance.SetTitleUIController(this);
        }
        
        public void ShowTitleUI()
        {
            _titleUI.gameObject.SetActive(true);
        }
    
        public void HideTitleUI()
        {
            _titleUI.gameObject.SetActive(false);
        }
        
        #region MainMenuUI
        public void ShowMainMenuUI()
        {
            mainMenuUIController.gameObject.SetActive(true);
        }
    
        public void HideMainMenuUI()
        {
            mainMenuUIController.gameObject.SetActive(false);
        }
        #endregion
    
        #region SubMenuUI
        public void ShowSubMenuUI()
        {
            subMenuUIController.gameObject.SetActive(true);
        }
    
        public void HideSubMenuUI()
        {
            subMenuUIController.gameObject.SetActive(false);
        }
        #endregion
        
    }

     

    3. InGameUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class InGameUIController : Singleton<InGameUIController>
    {
        [SerializeField] private RectTransform _inGameUI;
        
        //새로 추가한 InGameUI는 이곳 아래에 추가해주시고, 프리팹의 자식개체로 넣은 뒤 인스펙터에서 할당해주세요.
        //이 싱글톤 객체를 통해 UI 접근을 용이하게 관리합니다.
        //ex. public TimerUIController timerUIController;
        public TimeUIController timeUIController;
        public InteractionUIController interactionUIController;
        
        
        //여기까지
        
        protected override void Initialize()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            UIManager.Instance.SetInGameUIController(this);
        }
        
        public void ShowInGameUI()
        {
            _inGameUI.gameObject.SetActive(true);
        }
    
        public void HideInGameUI()
        {
            _inGameUI.gameObject.SetActive(false);
        }
    
        #region TimeUI
        public void ShowTimeUI()
        {
            timeUIController.gameObject.SetActive(true);
        }
    
        public void HideTimeUI()
        {
            timeUIController.gameObject.SetActive(false);
        }
        #endregion
    
        #region InteractionUI
        public void ShowInteractionUI()
        {
            interactionUIController.gameObject.SetActive(true);
        }
    
        public void HideInteractionUI()
        {
            interactionUIController.gameObject.SetActive(false);
        }
        #endregion
    }

     

    4. PopupUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    using UnityEngine.UI;
    
    public class PopupUIController : Singleton<PopupUIController>
    {
        [SerializeField] public Image backgroundImage;
        
        //새로 추가한 InGameUI는 이곳 아래에 추가해주시고, 프리팹의 자식개체로 넣은 뒤 인스펙터에서 할당해주세요.
        //이 싱글톤 객체를 통해 UI 접근을 용이하게 관리합니다.
        //ex. public PauseUIController pauseUIController;
        public PauseUIController pauseUIController;
        
        
        //여기까지
        
        protected override void Initialize()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            UIManager.Instance.SetPopupUIController(this);
        }
    
        #region PauseUI
        public void ShowPauseUI()
        {
            pauseUIController.ShowPopup();
        }
        
        public void HidePauseUI()
        {
            pauseUIController.ClosePopup();
        }
        #endregion
        
    }

     

    5. MainMenuUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class MainMenuUIController : MonoBehaviour
    {
        [SerializeField] private RectTransform _rectTransform;
        [SerializeField] private CanvasGroup _canvasGroup;
        [SerializeField] private Button _gameStartButton;
        [SerializeField] private Button _gameTutorialButton;
    
        private void Awake()
        {
            //버튼 클릭이벤트 등록
            _gameStartButton.onClick.AddListener(OnClickGameStartButton);
            _gameTutorialButton.onClick.AddListener(OnClickGameTutotialButton);
        }
        
        public void OnClickGameStartButton()
        {
            GameManager.Instance.GoToInGame();
        }
    
        public void OnClickGameTutotialButton()
        {
            Debug.Log("OnClickGameTutotialButton");
        }
    }

     

    6. SubMenuUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class SubMenuUIController : MonoBehaviour
    {
        [SerializeField] private RectTransform _rectTransform;
        [SerializeField] private CanvasGroup _canvasGroup;
        [SerializeField] private Button _gameSettingButton;
        [SerializeField] private Button _leaderBoardButton;
    
        private void Awake()
        {
            //버튼 클릭이벤트 등록
            _gameSettingButton.onClick.AddListener(OnClickGameSettingButton);
            _leaderBoardButton.onClick.AddListener(OnClickLeaderBoardButton);
        }
        
        public void OnClickGameSettingButton()
        {
            Debug.Log("OnClickGameSettingButton");
        }
    
        public void OnClickLeaderBoardButton()
        {
            Debug.Log("OnClickLeaderBoardButton");
        }
    }

     

    7. PauseUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class PauseUIController : PopupController
    {
        [SerializeField] private RectTransform _rectTransform;
        [SerializeField] private CanvasGroup _canvasGroup;
        [SerializeField] private Button _resumeButton;
        [SerializeField] private Button _retryButton;
        [SerializeField] private Button _quitButton;
        [SerializeField] private Button _audioToggleButton;
        [SerializeField] private Button _vibrateToggleButton;
    
        void Awake()
        {
            _resumeButton.onClick.AddListener(OnClickResumeButton);
            _retryButton.onClick.AddListener(OnClickRetryButton);
            _quitButton.onClick.AddListener(OnClickQuitButton);
            _audioToggleButton.onClick.AddListener(OnClickAudioToggleButton);
            _vibrateToggleButton.onClick.AddListener(OnClickVibrateToggleButton);
        }
        
        
        
        public void ShowPopup()
        {
            base.ShowPopup(gameObject);
        }
        
        public void ClosePopup()
        {
            base.ClosePopup(gameObject);
        }
        
        public void OnClickResumeButton()
        {
            ClosePopup();
            GameManager.Instance.ResumeGame();
        }
        
        public void OnClickRetryButton()
        {
            
        }
        
        public void OnClickQuitButton()
        {
            ClosePopup();
            GameManager.Instance.ResumeGame();
            GameManager.Instance.inGameController.Dispose();
            GameManager.Instance.inGameController.QuitGame();
        }
        
        public void OnClickAudioToggleButton()
        {
            
        }
        
        public void OnClickVibrateToggleButton()
        {
            
        }
    }

     

    8. InteractionUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class InteractionUIController : MonoBehaviour
    {
        [SerializeField] private RectTransform _rectTransform;
        [SerializeField] private CanvasGroup _canvasGroup;
        [SerializeField] private Button _acceptButton;
        [SerializeField] private Button _negativeButton;
        [SerializeField] private Button _pauseButton;
        
        private void Awake()
        {
            //버튼 클릭이벤트 등록
            _acceptButton.onClick.AddListener(OnClickAcceptButton);
            _negativeButton.onClick.AddListener(OnClickNegativeButton);
            _pauseButton.onClick.AddListener(OnClickPauseButton);
        }
        
        public void OnClickAcceptButton()
        {
            
        }
    
        public void OnClickNegativeButton()
        {
    
        }
        
        public void OnClickPauseButton()
        {
            GameManager.Instance.PauseGame();
            UIManager.Instance.popupUIController.ShowPauseUI();
        }
    }

     

    9. TimeUIController.cs

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    
    public class TimeUIController : MonoBehaviour
    {
        public TMP_Text workTimeText;
        public TMP_Text dayText;
    }

     

    Audio Manager 제작

    : 아주 간단한 기능만 존재하는 프로토타입은 얼추 만들어졌고, 이제 어느정도 1차 플레이는 가능할 정도의 게임을 만들기 위해 새로운 일감이 생겼다.

    --> 그 중 나는 Audio Manager를 만드는 일감을 받았다.

     

    ▶ 팀장 분께서 새로 만드신 UI Manager의 구조처럼 짜기를 원하셨다.

    --> 뭔가 비슷하게 짜긴 했는데, 아직 내가 이 구조를 정확히 이해하지 못한 것 같다.

     

    >> 만드려고 하는 Manager 구조

    • 어디서든 AudioManager를 Instance로 호출해서 BGM 또는 SFX를 재생할 수 있도록
    • Audio Toggle을 눌렀을 때 BGM과 SFX 모두 꺼지도록
    • AudioManager는 각 Controller를 조율만 하고, 재생이나 정지 등의 세부 로직은 각 Controller들이 담당하도록
    • 새로운 BGM와 SFX를 추가할 때 간단히 추가할 수 있도록 --> 추가할 Audio를 Inspector에서 바인딩, Controller에서 재생 함수 추가, 사용할 곳에서 AudioManager를 호출하여 바로 사용

     

    >> 작성 중인 코드

    1. AudioManager.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class AudioManager : Singleton<AudioManager>
    {
        [SerializeField] private BGMController bgmController;
        [SerializeField] private SFXController sfxController;
        
        public BGMController BGM => bgmController;
        public SFXController SFX => sfxController;
    
        private bool _isAudioOn = true;
    
        protected override void Initialize()
        {
            base.Initialize();
            _isAudioOn = true;   // 기본값은 켜짐으로 설정
            
            // 필요 시, 초기화    ex) 저장된 설정 로드
        }
        
        public void SetBGMController(BGMController bgmController)
        {
            this.bgmController = bgmController;
        }
        
        public void SetSFXController(SFXController sfxController)
        {
            this.sfxController = sfxController;
        }
    
        // Audio 토글을 통해 BGM 및 SFX On/Off
        public void ToggleAudio()
        {
            _isAudioOn = !_isAudioOn;
            bgmController.SetBGMOn(_isAudioOn);
            sfxController.SetSFXOn(_isAudioOn);
        }
    }

     

    2. BGMController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class BGMController : Singleton<BGMController>
    {
        [SerializeField] private AudioSource bgmSource;
        
        // BGM을 추가하실 때, 여기에 추가해주세요.
        [SerializeField] private AudioClip titleBGM;
        [SerializeField] private AudioClip gameBGM;
        
        // 여기까지
    
        private AudioClip _currentBGM;     // 현재 재생해야 할 BGM 저장
        private bool _isBGMOn = true;       // BGM이 켜져있는지 여부
        public bool IsBGMOn() => _isBGMOn;
    
        protected override void Initialize()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            AudioManager.Instance.SetBGMController(this);
        }
    
        // BGM를 추가하신 뒤, 아래 함수 모음에 재생 함수를 작성해주세요.
        #region PlayBGM 함수 모음 
    
        public void PlayTitleBGM() => PlayBGM(titleBGM);
    
        public void PlayGameBGM() => PlayBGM(gameBGM);
    
        #endregion
    
        // BGM 재생 (반복 O)
        private void PlayBGM(AudioClip clip)
        {
            if (!_isBGMOn || clip == null) return;
    
            bgmSource.clip = clip;
            bgmSource.loop = true;
            bgmSource.Play();
        }
    
        // BGM 중지
        private void StopBGM()
        {
            bgmSource.Stop();
        }
    
        // _isBGMOn값을 조정하고 그에 따라 BGM 재생 및 중지
        public void SetBGMOn(bool isBGMOn)
        {
            _isBGMOn = isBGMOn;
            if (!_isBGMOn)
                StopBGM();
        }
    }

     

    3. SFXController.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class SFXController : Singleton<SFXController>
    {
        [SerializeField] private AudioSource sfxSource;
        
        // SFX를 추가하실 때 여기에 추가해주세요.
        [SerializeField] private AudioClip buttonClick;
        [SerializeField] private AudioClip stamp;
        
        // 여기까지
    
        private bool _isSFXOn = true;       // SFX가 켜져있는지 여부
        public bool IsSFXOn() => _isSFXOn;
        
        protected override void Initialize()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
    
        private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            AudioManager.Instance.SetSFXController(this);
        }
    
        // SFX를 추가하신 뒤, 아래 함수 모음에 재생 함수를 작성해주세요.
        #region PlaySFX 함수 모음
    
        public void PlayButtonClick() => PlaySFX(buttonClick);
    
        public void PlayStamp() => PlaySFX(stamp);
    
        #endregion
    
        // SFX 1번 재생
        private void PlaySFX(AudioClip clip)
        {
            if (!_isSFXOn || clip == null) return;
            sfxSource.PlayOneShot(clip);
        }
    
        // _isSFXOn 조정
        public void SetSFXOn(bool isSFXOn)
        {
            _isSFXOn = isSFXOn;
        }
    }