목차
멋쟁이사자처럼 로켓단 인턴쉽
25.08.22
회의록
>> 안건
- 업무 업데이트
- 질문 혹은 피드백 요청
- 주요 안건
- SFX, BGM 선별 및 할당
- 튜토리얼 이미지 적용
- 향후 마일스톤
- 디테일 보강
>> 회의 내용
- SFX 및 BGM 선별 및 선정 후 게임에 적용
- 튜토리얼 완성
- Itch.io 업로드 완료
필요한 BGM 및 SFX 고민
: 이전 시간에 고민했던 BGM 및 SFX 목록이다.
- 게임에 필요한 BGM 및 SFX 목록 정리
- BGM
- Title BGM
- InGame BGM
- SFX
- Click SFX
- UI Close SFX
- InGame Count SFX
- (InGame Start SFX)
- Stamp SFX
- (InGame NewRecord SFX)
- Obstacle SFX - 종류별로
- Result NewRecord SFX
--> 근데 다른 팀원 분께서 근무시간 외에 SFX를 다 찾아오셨다.
- 팀원 분이 찾아오신 SFX 목록
- Common
- Click SFX
- InGame
- Stamp SFX
- Document SFX
- Document Classfication SFX
- 성공
- 실패
- Obstacle SFX - 종류별로
- 벌레, 포스트잇 타격소리
- 손 쳐내는 소리
- 파일철 / 서류봉투에서 꺼내는 소리
- Speed Up SFX
버그 수정
: 게임 시작을 눌렀을 때, InGameBGM이 재생되고 3초 센 뒤에 InGameBGM이 다시 재생되는 버그
--> InGameState.cs의 OnEnter()과 InGameController.cs의 StartGame()에서 둘다 BGM을 호출했기 때문에 발생한 버그, InGameState.cs의 OnEnter()의 BGM 재생 코드를 삭제
SFXController 리팩토링
: SFX를 동시에 재생할 수 있고 필요하면 SFX를 반복재생 할 수 있도록 리팩토링
--> 오브젝트 풀링 방식으로 구현하여 AudioSource를 필요한 만큼 만들고 사용하도록
>> 작성한 코드
<hide/>
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SFXController : Singleton<SFXController>
{
// SFX를 추가하실 때 여기에 추가해주세요.
[Header("Common")]
[SerializeField] private AudioClip buttonClick;
[Header("InGame")]
[SerializeField] private AudioClip stamp;
[SerializeField] private AudioClip docSuccess;
[SerializeField] private AudioClip docFail;
[SerializeField] private AudioClip docSwap;
[SerializeField] private AudioClip obsBugPostHit;
[SerializeField] private AudioClip obsProcessTry;
[SerializeField] private AudioClip obsHandHit;
[SerializeField] private AudioClip obsFileEnvelopeOut;
[SerializeField] private AudioClip speedUp;
[SerializeField] private AudioClip timeOutAlert;
// 여기까지
private List<AudioSource> _sfxSources = new List<AudioSource>();
private const int INITIAL_POOL_SIZE = 11;
private bool _isSFXOn = true;
public bool IsSFXOn() => _isSFXOn;
protected override void Initialize()
{
SceneManager.sceneLoaded += OnSceneLoaded;
// 초기 풀 생성
for (int i = 0; i < INITIAL_POOL_SIZE; i++)
{
var src = gameObject.AddComponent<AudioSource>();
src.playOnAwake = false;
src.loop = false;
_sfxSources.Add(src);
}
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
//SceneManager.sceneLoaded -= OnSceneLoaded;
AudioManager.Instance.SetSFXController(this);
}
// SFX를 추가하신 뒤, 아래 함수 모음에 재생 함수를 작성해주세요. 그리고 작성하신 함수를 통해 사용하시면 됩니다.
#region PlaySFX 함수 모음
public void PlayButtonClick() => PlaySFX(buttonClick);
public void PlayStamp() => PlaySFX(stamp);
public void PlayDocSuccess() => PlaySFX(docSuccess);
public void PlayDocFail() => PlaySFX(docFail);
public void PlayDocSwap() => PlaySFX(docSwap);
public void PlayObsBugPostHit() => PlaySFX(obsBugPostHit);
public void PlayObsProcessTry() => PlaySFX(obsProcessTry);
public void PlayObsHandHit() => PlaySFX(obsHandHit);
public void PlayObsFileEnvelopeOut() => PlaySFX(obsFileEnvelopeOut);
public void PlaySpeedUp() => PlaySFX(speedUp);
public AudioSource PlayTimeOutAlert() => PlayLoopSFX(timeOutAlert);
#endregion
private void PlaySFX(AudioClip clip)
{
if (!_isSFXOn || clip == null) return;
var src = GetFreeAudioSource();
src.PlayOneShot(clip);
}
public AudioSource PlayLoopSFX(AudioClip clip)
{
if (!_isSFXOn || clip == null) return null;
var src = GetFreeAudioSource();
src.loop = true;
src.clip = clip;
src.Play();
return src;
}
public void StopLoopSFX(AudioSource src)
{
if (src != null && src.isPlaying)
{
src.Stop();
src.loop = false;
src.clip = null;
}
}
private AudioSource GetFreeAudioSource()
{
foreach (var src in _sfxSources)
{
if (!src.isPlaying) return src;
}
// 다 쓰고 있으면 하나 더 추가
var newSrc = gameObject.AddComponent<AudioSource>();
newSrc.playOnAwake = false;
_sfxSources.Add(newSrc);
return newSrc;
}
public void SetSFXOn(bool isSFXOn)
{
_isSFXOn = isSFXOn;
if (!_isSFXOn)
{
// 꺼지면 모든 SFX 정지
foreach (var src in _sfxSources)
src.Stop();
}
}
}
>> 테스트
: 코드를 작성하고 테스트해보니, 문제가 여러 개 있었다.
- 막상 AudioSource를 만든 만큼 활용하지 않았다.
- SFX를 반복재생하려고 할 때 PlayLoopSFX()에서 AudioSource를 반환하고 SFX의 반복재생을 중지할 때는 그 AudioSource를 받아서 멈추려고 했으나 그렇게 되면 SFX를 반복재생하려고 하는 곳마다 Audio 관련 코드가 많이 늘어난다.
--> 알고보니 AudioSource.PlayOneShot()을 사용하면 따로 AudioSource의 Clip에 영향을 주지 않을 뿐더러 PlayOneShot()으로 재생한 다른 AudioClip과도 상관없이 재생이 가능했다. 즉, 오브젝트 풀링 방식으로 구현할 필요가 없다.
▶ 다시 SFXController를 오브젝트 풀링 방식에서 원래대로 복구하고 반복 재생이 필요한 SFX 따로 관리할 Dictionary를 만들어서 Audio 관련 코드는 오직 SFXController에서만 작성되도록 구현
※ AudioSource.PlayOneShot()의 공식 레퍼런스
https://docs.unity3d.com/ScriptReference/AudioSource.PlayOneShot.html
└ 최종 코드
- SFXController.cs
<hide/>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SFXController : Singleton<SFXController>
{
// SFX를 추가하실 때 여기에 추가해주세요.
[Header("Common")]
[SerializeField] private AudioClip buttonClick;
[Header("InGame")]
[SerializeField] private AudioClip stamp;
[SerializeField] private AudioClip docSuccess;
[SerializeField] private AudioClip docFail;
[SerializeField] private AudioClip docSwap;
[SerializeField] private AudioClip obsBugPostHit;
[SerializeField] private AudioClip obsProcessTry;
[SerializeField] private AudioClip obsHandHit;
[SerializeField] private AudioClip obsFileEnvelopeOut;
[SerializeField] private AudioClip newRecordResult;
[SerializeField] private AudioClip newRecordScoreBar;
[SerializeField] private AudioClip speedUp;
[SerializeField] private AudioClip fever;
[SerializeField] private AudioClip timeOutAlert;
// 여기까지
private AudioSource _sfxSource; // 단발성 AudioSource
private Dictionary<AudioClip, AudioSource> _loopSources; // 반복용 AudioSource
private bool _isSFXOn = true; // SFX가 켜져있는지 여부
public bool GetIsSFXOn() => _isSFXOn;
protected override void Initialize()
{
SceneManager.sceneLoaded += OnSceneLoaded;
// AudioSource 초기화
_sfxSource = gameObject.AddComponent<AudioSource>();
_sfxSource.playOnAwake = false;
_loopSources = new Dictionary<AudioClip, AudioSource>();
}
private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
{
AudioManager.Instance.SetSFXController(this);
}
// SFX를 추가하신 뒤, 아래 함수 모음에 재생 함수를 작성해주세요. 그리고 작성하신 함수를 통해 사용하시면 됩니다.
// 1번 재생 : PlaySFX()
// 반복 재생 : PlayLoopSFX()
// 반복 재생 중지 : StopLoopSFX()
#region PlaySFX 함수 모음
public void PlayButtonClick() => PlaySFX(buttonClick);
public void PlayStamp() => PlaySFX(stamp);
public void PlayDocSuccess() => PlaySFX(docSuccess);
public void PlayDocFail() => PlaySFX(docFail);
public void PlayDocSwap() => PlaySFX(docSwap);
public void PlayObsBugPostHit() => PlaySFX(obsBugPostHit);
public void PlayObsProcessTry() => PlaySFX(obsProcessTry);
public void PlayObsHandHit() => PlaySFX(obsHandHit);
public void PlayObsFileEnvelopeOut() => PlaySFX(obsFileEnvelopeOut);
public void PlaySpeedUp() => PlaySFX(speedUp);
public void PlayFever() => PlaySFX(fever);
public void PlayTimeOutAlert() => PlayLoopSFX(timeOutAlert);
public void StopTimeOutAlert() => StopLoopSFX(timeOutAlert);
public void PlayNewRecordResult() => PlaySFX(newRecordResult);
public void PlayNewRecordScoreBar() => PlaySFX(newRecordScoreBar);
#endregion
// SFX 1번 재생
private void PlaySFX(AudioClip clip)
{
if (!_isSFXOn || clip == null) return;
_sfxSource.PlayOneShot(clip);
}
// SFX 반복 재생
private void PlayLoopSFX(AudioClip clip)
{
if (!_isSFXOn || clip == null) return;
if (_loopSources.ContainsKey(clip)) return; // 이미 재생 중이면 패스
var src = gameObject.AddComponent<AudioSource>();
src.playOnAwake = false;
src.loop = true;
src.clip = clip;
src.Play();
_loopSources[clip] = src;
}
// SFX 반복 재생 중지
private void StopLoopSFX(AudioClip clip)
{
if (clip == null || !_loopSources.ContainsKey(clip)) return;
var src = _loopSources[clip];
if (src != null && src.isPlaying)
{
src.Stop();
Destroy(src);
}
_loopSources.Remove(clip);
}
// _isSFXOn 조정
public void SetSFXOn(bool isSFXOn)
{
_isSFXOn = isSFXOn;
if (!_isSFXOn) // 모든 SFX 정지
{
_sfxSource.Stop();
foreach (var key in _loopSources)
if (key.Value != null) key.Value.Stop();
_loopSources.Clear();
}
}
}
└ 발견한 버그
- LoopSFX 재생 중에 일시중지한 다음 소리를 껐다가 키면 LoopSFX 재생이 안되는 버그
- 결과창으로 넘어가서도 계속 LoopSFX가 재생되는 버그 --> 타이틀로 가서도 계속 재생 / 다시 게임을 시작해서 카운팅이 끝나고 게임이 시작해야 풀림
▶ 계속 LoopSFX가 재생되는 버그는 다른 팀원 분께서 InGameController.cs의 EndGame() 함수에서 게임이 끝나고 결과창UI가 나오기 전에 LoopSFX를 꺼주는 함수를 호출해서 해결하셨다.
--> 남은 버그는 추후 수정 예정
New Record시, SFX가 재생되도록 적용
: New Record SFX가 추가되어 New Record시, SFX가 재생되도록 적용해주었다. (게임 중 + 결과창)
--> 결과창에서 New Record시, DOTween 효과 도중에 Audio를 재생하기 위해 Seqeunce 안에 콜백(Action)을 삽입.
└ 최종 코드
1. ResultUIController.cs
<hide/>
// New Record 시, 효과
private void ShowNewRecordEffect()
{
newRecordImage.gameObject.SetActive(true);
// 초기화 (작고 안 보이는 상태)
newRecordImage.color = new Color(1f, 1f, 1f, 0f);
newRecordImage.rectTransform.localScale = Vector3.zero * 0.8f;
Sequence seq = DOTween.Sequence();
// Fade In + Scale Up
seq.Append(newRecordImage.DOFade(1f, 0.5f));
seq.Join(newRecordImage.rectTransform.DOScale(1.2f, 0.3f).SetEase(Ease.OutBack));
// SFX 재생
seq.AppendCallback(() =>
{
AudioManager.Instance.SFX.PlayNewRecordResult();
});
// 살짝 튕기면서 원래 크기로
seq.Append(newRecordImage.rectTransform.DOScale(1f, 0.2f).SetEase(Ease.OutBack));
// 착! 강조
seq.Append(newRecordImage.rectTransform.DOScale(0.95f, 0.1f).SetEase(Ease.InQuad));
seq.Append(newRecordImage.rectTransform.DOScale(1f, 0.15f).SetEase(Ease.OutQuad));
}
2. InGameState.cs
<hide/>
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class InGameState : IGameState
{
public void OnEnter()
{
GameManager.Instance.StartCoroutine(StartGame());
UIManager.Instance.inGameUIController.ShowInGameUI();
}
public void OnUpdate()
{
// TODO: 유저의 최고기록 불러오기 (임시: 'K'를 누르면 연출 재생)
// New Record시, 점수판에 New Record Image 연출 재생
if (Input.GetKeyDown(KeyCode.K))
{
AudioManager.Instance.SFX.PlayNewRecordScoreBar();
InGameUIController.Instance.scoreUIController.ShowNewRecordImage();
}
}
public void OnExit()
{
}
IEnumerator StartGame()
{
yield return GameManager.Instance.inGameController.SetInitGame();
yield return GameManager.Instance.inGameController.RunSequence();
}
}
'Development > Internship' 카테고리의 다른 글
[멋사 로켓단 인턴쉽] 14일차 - 예비군으로 인해 불참 (0) | 2025.08.26 |
---|---|
[멋사 로켓단 인턴쉽] 13일차 - Audio 관련 기능 추가 및 버그 수정 (1) | 2025.08.25 |
[멋사 로켓단 인턴쉽] 11일차 - 점수판 완성 및 버그 해결 (3) | 2025.08.21 |
[멋사 로켓단 인턴쉽] 10일차 - Result UI & Effect (0) | 2025.08.20 |
[멋사 로켓단 인턴쉽] 9일차 - Result Effect 및 BGM Bug Fix (0) | 2025.08.19 |