본문 바로가기
🌐 유니티 (Unity)

Day 39 - SoundManager(오브젝트 풀에서 사운드 소스를 가져와 재생)

by shin0707 2024. 6. 12.
728x90
반응형

  • 주제 : 사운드 관리 매니저 생성 및 활용

>> 오브젝트 풀[ObjectPool]

(오브젝트를 미리 생성하여 큐에 넣어 놓고 필요할 때마다 가져다 사용)


>> 게임매니저[GameManager]

(오브젝트 풀 초기화 및 관리)


>> 사운드매니저[SoundManager]

(배경음악 및 효과음 관리)

 

>> 사운드소스[SoundSource]

(효과음을 재생하는 오디오 소스 관리)

 

>> 특정 상황 시 효과음 재생


  • 공부내용

 

1. 오브젝트 풀

: Object Pooling은 몬스터나 총알, 효과음 등 게임화면에서 나타났다가 사라졌다를 반복하는 요소들을

비활성화상태(안 보이는 상태)로 미리 생성해 놓고, 필요할 때마다 활성화시키는 기법이다.

이 기법을 사용하면, 생성과 삭제를 반복하지 않기 때문에 메모리를 아낄 수 있다.

 

[System.Serializable]

:클래스별 카테고리화

(다양한 종류의 데이터들을 인스펙터 창에서 추가 혹은 삭제 가능)

 

ObjectPool.cs

using System.Collections.Generic;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
    // 오브젝트 풀 데이터를 정의할 데이터 모음 정의
    [System.Serializable]
    public class Pool
    {
        public string tag; // 태그
        public GameObject prefab; // 프리팹
        public int size; // 크기
    }

    public List<Pool> Pools; // 풀 목록
    public Dictionary<string, Queue<GameObject>> PoolDictionary; // 풀 딕셔너리

    private void Awake()
    {
        // 오브젝트 풀 생성
        PoolDictionary = new Dictionary<string, Queue<GameObject>>();
        foreach (var pool in Pools)
        {
            Queue<GameObject> objectPool = new Queue<GameObject>();
            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.prefab);
                obj.SetActive(false); // 비활성화
                objectPool.Enqueue(obj); // 큐에 추가
            }
            PoolDictionary.Add(pool.tag, objectPool); // 딕셔너리에 추가
        }
    }

    public GameObject SpawnFromPool(string tag)
    {
        // 해당 태그의 풀 존재 확인
        if (!PoolDictionary.ContainsKey(tag))
            return null;

        // 오브젝트 풀에서 오브젝트 가져오기
        GameObject obj = PoolDictionary[tag].Dequeue();
        PoolDictionary[tag].Enqueue(obj);
        obj.SetActive(true); // 활성화
        return obj;
    }
}

2. 게임매니저

: 게임의 전반적인 상태를 관리하고, 스크립트 간 필요한 요소들을 이어주는 역할을 한다.

 

GameManager 오브젝트 생성 -> GameManager 스크립트 할당 -> ObjectPool 스크립트 할당

 

GameManager.cs

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance; // 싱글톤 인스턴스

    public ObjectPool ObjectPool { get; private set; } // 오브젝트 풀

    private void Awake()
    {
        Instance = this; // 싱글톤 설정
        ObjectPool = GetComponent<ObjectPool>(); // 오브젝트 풀 가져오기
    }
}

3. 사운드매니저

음악을 재생하는 메서드를 작성할 때는, 가장 먼저 음악을 정지해주고, 이후 음악을 재생하는 코드를 넣는 것이 좋다.

// 배경음악 변경 메서드
public static void ChangeBackGroundMusic(AudioClip music)
    {
        instance.musicAudioSource.Stop(); // 음악 정지
        instance.musicAudioSource.clip = music; // 새로운 음악 클립 설정
        instance.musicAudioSource.Play(); // 음악 재생
    }

(사운드들이 서로 꼬이는 것을 방지)

 

SoundManager.cs

using UnityEngine;

public class SoundManager : MonoBehaviour
{
    public static SoundManager instance; // 싱글톤 인스턴스

    [SerializeField][Range(0f, 1f)] private float soundEffectVolume; // 효과음 볼륨
    [SerializeField][Range(0f, 1f)] private float soundEffectPitchVariance; // 효과음 피치 변동
    [SerializeField][Range(0f, 1f)] private float musicVolume; // 음악 볼륨

    private AudioSource musicAudioSource; // 음악 오디오 소스
    public AudioClip musicClip; // 음악 클립

    private void Awake()
    {
        instance = this; // 싱글톤 설정
        musicAudioSource = GetComponent<AudioSource>(); // 오디오 소스 가져오기
        musicAudioSource.volume = musicVolume; // 볼륨 설정
        musicAudioSource.loop = true; // 반복 재생 설정
    }

    private void Start()
    {
        ChangeBackGroundMusic(musicClip); // 배경음악 변경
    }

    public static void ChangeBackGroundMusic(AudioClip music)
    {
        instance.musicAudioSource.Stop(); // 음악 정지
        instance.musicAudioSource.clip = music; // 새로운 음악 클립 설정
        instance.musicAudioSource.Play(); // 음악 재생
    }

    public static void PlayClip(AudioClip clip)
    {
        // 오브젝트 풀에서 사운드 소스 가져오기
        GameObject obj = GameManager.Instance.ObjectPool.SpawnFromPool("SoundSource");
        obj.SetActive(true); // 활성화
        SoundSource soundSource = obj.GetComponent<SoundSource>();
        soundSource.Play(clip, instance.soundEffectVolume, instance.soundEffectPitchVariance); // 재생
    }
}

4. 사운드소스

SoundSource 오브젝트 생성 -> AudioSource 컴포넌트 추가 -> SoundSource 스크립트 추가 -> 오브젝트 프리팹화

 

SoundSource.cs

using UnityEngine;

public class SoundSource : MonoBehaviour
{
    private AudioSource audioSource; // 오디오 소스

    public void Play(AudioClip clip, float soundEffectVolume, float soundEffectPitchVariance)
    {
        if (audioSource == null)
            audioSource = GetComponent<AudioSource>();

        CancelInvoke(); // 기존 호출 취소
        audioSource.clip = clip; // 클립 설정
        audioSource.volume = soundEffectVolume; // 볼륨 설정
        audioSource.Play(); // 재생
        audioSource.pitch = 1f + Random.Range(-soundEffectPitchVariance, soundEffectPitchVariance); // 피치 설정

        Invoke("Disable", clip.length + 2); // 재생 종료 후 비활성화 호출 예약
    }

    public void Disable()
    {
        audioSource.Stop(); // 재생 중지
        gameObject.SetActive(false); // 비활성화
    }
}

5. 특정 상황 시 효과음 재생

클립이 존재하면 효과음을 재생하는 코드를 추가

(클립이 없더라도 null의 상황이 발생하지 않음)

[SerializeField] private AudioClip ShootingClip; // 발사 클립

...

    if (ShootingClip) SoundManager.PlayClip(ShootingClip); // 효과음 재생

...
728x90
반응형

loading