쯔꾸르식 유니티 게임 공부

08.오디오 메니져 만들기

ruripanda 2025. 1. 3. 14:03

본 강좌는 케이디님의 쯔꾸르풍 유니티 게임 만들기 강좌를 참고하여 만들었음을 알려드립니다

using UnityEngine;

[System.Serializable]
public class Sound
{
    public string name;//사운드 이름

    public AudioClip clip;//사운드 파일
    private AudioSource source;//사운드 플레이어

    public float volumn;
    public bool loop;

    public void SetSource(AudioSource _source)
    {
        source = _source;
        source.clip = clip;
        source.loop = loop;
    }

    public void SetVolumn()
    {
        source.volume = volumn;
    }

    public void Play()
    {
        source.Play();
    }

    public void Stop()
    {
        source.Stop();
    }

    public void SetLoop()
    {
        source.loop = true;
    }

    public void SetLoopCancel()
    {
        source.loop = false;
    }
}
public class AudioManager : MonoBehaviour
{
    static public AudioManager instance;

    [SerializeField]
    public Sound[] sounds;

    private void Awake()
    {
        if (instance == null)
        {
            DontDestroyOnLoad(this.gameObject);
            instance = this;
        }
        else
        {
            Destroy(this.gameObject);
        }
    }

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        for (int i = 0; i < sounds.Length; i++) 
        {
            GameObject soundObjcet = new GameObject("사운드 파일 이름 : " + i + " = " + sounds[i].name);
            sounds[i].SetSource(soundObjcet.AddComponent<AudioSource>());
            soundObjcet.transform.SetParent(this.transform);
        }
    }

    public void Play(string _name)
    {
        for(int i = 0; i < sounds.Length; i++)
        {
            if(_name == sounds[i].name)
            {
                sounds[i].Play();
                return;
            }
        }
    }

    public void Stop(string _name)
    {
        for (int i = 0; i < sounds.Length; i++)
        {
            if (_name == sounds[i].name)
            {
                sounds[i].Stop();
                return;
            }
        }
    }

    public void SetLoop(string _name)
    {
        for (int i = 0; i < sounds.Length; i++)
        {
            if (_name == sounds[i].name)
            {
                sounds[i].SetLoop();
                return;
            }
        }
    }

    public void SetLoopCancel(string _name)
    {
        for (int i = 0; i < sounds.Length; i++)
        {
            if (_name == sounds[i].name)
            {
                sounds[i].SetLoopCancel();
                return;
            }
        }
    }

    public void SetVolumn(string _name, float _Volumn)
    {
        for (int i = 0; i < sounds.Length; i++)
        {
            if (_name == sounds[i].name)
            {
                sounds[i].volumn =  _Volumn;
                sounds[i].SetVolumn();
                return;
            }
        }
    }
}
using System.Collections;
using UnityEngine;

public class MovingObjcet : MonoBehaviour
{
    static public MovingObjcet instance;

    public string currentMapName;//transferMap 스크립트에 있는 transferMapName변수의 값을 저장

    private BoxCollider2D boxCollider;//박스콜라이더
    public LayerMask layerMask;//통과불가 에이리어 설정

    public string walkSound_1;
    public string walkSound_2;
    public string walkSound_3;
    public string walkSound_4;

    private AudioManager theAudio;

    //public AudioClip walkSound_1;//사운드 파일
    //public AudioClip walkSound_2;

    private AudioSource audioSource;//사운드 플레이어

    //케릭터 이동속도
    public float speed;
    //벡터(케릭터의 이동 방향)
    private Vector3 vector;

    //뛰기,평범하게 걷기
    public float runSpeed;
    private float applyRunSpeed;
    private bool applyRunFlag = false;

    //픽셀 단위로 움직이기 구현
    public int walkCount;
    private int currentWalkCount;

    //한칸한칸 이동할때 플레그 형식으로 구현
    private bool canMove = true;

    private Animator animator;
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        if(instance == null)
        {
            DontDestroyOnLoad(this.gameObject);
            boxCollider = GetComponent<BoxCollider2D>();
            //audioSource = GetComponent<AudioSource>();
            animator = GetComponent<Animator>();
            theAudio = FindAnyObjectByType<AudioManager>();
            instance = this;
        }
        else
        {
            Destroy(this.gameObject);
        }     
    }

    //코루틴으로 이동제어 한칸 한칸 이동하게
    IEnumerator MoveCoroutine()
    {
        while(Input.GetAxisRaw("Vertical") !=0 || Input.GetAxisRaw("Horizontal") != 0)
        {
            //시프트 키 입력 여부에 따라 뛰기 걷기 구분
            if (Input.GetKey(KeyCode.LeftShift))
            {
                applyRunSpeed = runSpeed;
                applyRunFlag = true;
            }

            else
            {
                applyRunSpeed = 0;
                applyRunFlag = false;
            }

            //이동방향 벡터 움직임 구현
            vector.Set(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"), transform.position.z);

            //좌상,좌하 등 대각선 방향이동 장금
            if(vector.x !=0)
                vector.y = 0;

            //에니메이터 불러오기
            animator.SetFloat("DirX", vector.x);
            animator.SetFloat("DirY", vector.y);

            RaycastHit2D hit;//레이케스트

            Vector2 start = transform.position; //캐릭터의 현제 위치 값
            Vector2 end = start + new Vector2(vector.x * speed * walkCount, vector.y * speed * walkCount);
            //캐릭터가 이동하고자하는 위치 값 그리고 사이즈

            boxCollider.enabled = false;
            hit = Physics2D.Linecast(start, end, layerMask);
            boxCollider.enabled = true;

            if (hit.transform != null)
                break;

            animator.SetBool("Walking", true);

            int temp = Random.Range(1, 4);
            switch (temp)
            {
                case 1:
                    theAudio.Play(walkSound_1);
                    break;
                case 2:
                    theAudio.Play(walkSound_2);
                    break;
                case 3:
                    theAudio.Play(walkSound_3);
                    break;
                case 4:
                    theAudio.Play(walkSound_4);
                    break;
            }
            theAudio.SetVolumn(walkSound_2, 0.5f);

            //캐릭터 이동구현 코드
            while (currentWalkCount < walkCount)
            {
                if (vector.x != 0)
                {
                    //Translate는 현제 값에서 +를 해주는 코드
                    transform.Translate(vector.x * (speed + applyRunSpeed), 0, 0);
                }
                else if (vector.y != 0)
                {
                    transform.Translate(0, vector.y * (speed + applyRunSpeed), 0);
                }
                if (applyRunFlag)
                    currentWalkCount++;//위의 applyRunFlas가 되면 추가되는 카운트 만약 아니라면 더해지지 않음
                currentWalkCount++;//위의 카운트와는 별개의 카운트로 구분 됨
                yield return new WaitForSeconds(0.01f);//0.01초마다 대기

                /*if(currentWalkCount % 9 == 2)
                {
                    int temp = Random.Range(1, 2);
                    switch (temp)
                    {
                        case 1:
                            //audioSource.clip = walkSound_1;
                            //audioSource.Play();
                            break;
                        case 2:
                            //audioSource.clip = walkSound_2;
                            //audioSource.Play();
                            break;
                    }
                }*/

            }
            currentWalkCount = 0;//0을 넣어서 반복문 초기화

        }

        animator.SetBool("Walking", false);
        //한칸한칸 이동할때 bool값
        canMove = true;
    }

    //매 프레임마다 이동명령을 받음
    void Update()
    {
        if (canMove)
        {
            //이동 방향키 입력을 할때 쯔구르 처럼 상하좌우 구분하게 하는 코드
            if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0)
            {
                //한칸 한칸 이동할때 bool값으로 canMove를 이용함
                canMove = false;
                StartCoroutine(MoveCoroutine());
            }
        }
    }
}

 

오늘 시간에는 게임효과를 내기 위해 오디오매니져를 만들어보겠다

 

각각의 객체가 사운드를 관리하는 법을 알아본다

 

일단 MovingObject 스크립트에 들어간다

 

이렇게 작성해준다

그러면 인스팩터에 변수로 뜨게되는데 여기서 Audio칩을 추가해 줄 수 있다

 

그리고 사운드를 관리해주기 위해서는 

 

오디오 소드 컴포넌트를 추가해줘야한다

컴포넌트를 추가해줬으니 당연히 오디오 소스를 GetComponent해준다

 

그뒤 움직일때 재생이 되게해준다

 

맨위에 if문에 있는 공식은 currentWalkCount를 9로 나눌때 2가 나온다면 이라는 조건인데

인스팩터에 WalkCount가 20이고 이걸 9로 나누면 18을 빼고 2가 남으므로 소리가 재생된다

int temp로 변수를 만들고 랜덤함수 2개를 1,2만들어 넣고 

그리고 스위치 문으로 소리를 2가지로 만들어 놓는다 그럼 랜덤하게 발자국 소리가 들린다

이제 마지막으로 사운드 재생인

'

코드를 적어 넣는다

 

이렇게 하면 소리가 난다

단 아직은 씬을 이동하면 오디오소스가 같이 안온다

 

다음은 오디오 매니져로 통합 관리하는 법을 알아본다

오디오 매니져(AudioManager)스크립트를 만든다

일단 사운드를 배열로 넣고

이렇게 작성해준다

볼륨(volum)과 반복(loop)도 작성해준다

 

source에 _source를 넣어주고 소스 칩(source.clip)을 설정해주면서 반복(loop)를 넣어주는 함수를 만들어 준다

 

여기서 주의할 점이 있는데 커스텀 클레스는 public로 만들어도 인스펙터에 나오지 않는다

따라서 강제로 인스팩터에 나오기하기 위해

위 두가지 함수를 이용해서 인스펙터에 나오게 한다

 

이제 이 소스를 오디오 메니져에서 이용하게 코드를 작성해준다

1째 줄

게임 오브젝트 변수를 만들어주고 뉴게임 오브젝트 이름은 이름+숫자+사운드 숫자 이름으로 만들어준다

2째줄

사운드[숫자].세팅 사운드(SetSource) (사운드 오브젝트.AddComponent<AudioSource>());로 만들어서

겟컴포넌트를 잡아준다

여기까지 작성하면 

오디오 메니져에 이렇게 나올탠데 발자국 소리 4개를 전부 넣어주자

볼륨크기는 1이 최대이다

 

그런데 이렇게 작성해주면 하이러키 창에 이렇게 지저분하게 한꺼번에 뜨게 된다

스크립트 상으로 다른 오브젝트의 하위에 넣기 위해

 SetParent(this.transform)을 넣어주자

그러면 이렇게 이동된다

 

이제 플레이어가 이동할때마다 사운드가 들리게 만들어 준다

 

MovingObject에 string로 코드를 아래처럼 작성해준다

코드를 문자열(string)로 작성한 이유는 문자열로 받아오기 위함이다

 

void start에 오디오 매니저를 찾아와 장착한다

if문을 작성해주고

아래같이 작성해준다

 

그리고 오디오 매니져로 들어가서

 

Play를 작성해준다

함수 이름은 Play이고 (문자열로 값을 받아와서)

반복문으로 잡아오고 이름이 인스팩터에 해당하는 이름을 바아와서

사운드를 플레이 해준다

찾와왔으니 return으로 함수를 끝내준다


위쪽 클레스에는 플레이 함수가 없으므로

작성해준다

 

MovingObject에 마저 작성해준다

인스팩터에 들어와서 이렇게 순서대로 똑같이 적어준다

이렇게 하면 걷는 소리가 잘 들린다

 

이제 나머지 기능들도 작성해준다

재생,중지,반복,반복중지

 

 

그리고 호출될 함수들이다

재생,정지,반복,반복정지, 볼륨

 

그리고 MovingObject에서 이렇게 코드를 넣어주면 소리를 줄일 수 있다

 

그리고 오디오메니져에도 씬을 이동할때 파괴됨을 방지하기 위해 void Awake에 인스턴스를 작성해준다