본문 바로가기
유니티3D

유니티3D - BGM(배경음악) 바꾸기 #2. 스크립트 설명편(1)

지난 시간에는 마을 박스에 진입할 때 배경음악을 바꾸는 부분을 살펴보았지요.
스크립트를 여기 저기 채워넣고 설정만을 바꿔보았지 작동원리에 대한 설명은 없었는데요.
오늘은 그 부분을 살펴보겠습니다.

https://itadventure.tistory.com/415

 

유니티3D - BGM(배경음악) 바꾸기 #1

지난 게시글에서는 유니티에서의 배경음악에 대해 다루어 보았었지요. 한가지 아쉬운 점이 있었습니다. 배경음악이 1번만 재생되고 끝나는 문제가 있었지요. https://itadventure.tistory.com/414 유니티3

itadventure.tistory.com

유니티에는 콜라이더라는 충돌을 감지하는 컴포넌트가 있는데요.
이 콜라이더는 2가지 역활중 1가지를 선택해서 사용할 수 있습니다.

지난 번에는 콜라이더의 영역을 설정하여 플레이어가 진입하는 못하게 하는 벽을 만드는 부분을 소개해드렸는데요.
이 콜라이더를 콜리젼(Collision)이라고 합니다.

https://itadventure.tistory.com/409

 

유니티3D 벽 뜷고 지나가? 안돼! 콜라이더(Collider) 만들기.

해리포터라는 영화에서 주인공 해리포터는 벽을 뜷고 호그와트로 가는 것을 보게 됩니다. 현실에서는 일반적으로 있을 수 없는 일이지만 가상 세계도 그럴까요? 지난 시간에는 '에셋스토어로

itadventure.tistory.com

이번에 사용된 것은 다른 종류의 콜라이더입니다.
트리거(Trigger)라고 부르는데요.
플레이어가 물체를 통과할 수 있는 대신 충돌, 충돌해제 등을 감지해서 특정한 액션을 취할 수 있도록 하는 장치입니다.

마을에 진입할 때, 빠져나갈 때 입구에 특정 막대를 장식하는 것으로 이 동작을 구현할 수 있습니다.
마을 입구에 아래와 같은 트리거가 있다고 칩시다. 우선 이 트리거는 보이지 않는다고 가정합니다.

플레이어가 마을 밖에서 마을 안으로 들어올 때는 '마을 밖 트리거'를 먼저 충돌하고
그 다음에는 '마을 입장 트리거'를 충돌합니다.
최종적으로 부딪힌 것은 마을 입장 트리거이기 때문에
마을 밖 트리거의 액션이 실행되었다가도 결국에는 마을 입장 트리거에 심어 놓은 액션이
최종적으로 실행됩니다.

반대의 경우를 생각해 봅시다.
플레이어가 마을 안에서 밖으로 나갈 때는
'마을 입장 트리거'를 먼저 충돌하고 그 다음에 '마을밖 트리거'를 충돌합니다.
최종적으로 부딪힌 마을 밖 트리거의 액션이 마지막으로 실행되는 것이지요.

마을 입장 트리거에 마을 배경음악을 재생하는 기능을,
마을 밖 트리거에 마을밖 배경음악을 재생하는 기능을 심어놓았다면
결국에는 마을 안으로 들어오거나 마을 밖으로 나갈때 배경음악이 상황에 맞게 바뀌는 것입니다.

다만, 이 방법은 마을의 모든 입구에 트리거를 설치해야 한다는 점과,
플레이어가 점프할 수 있는 최대 높이까지 트리거의 높이를 높이는 등의 방법만 가능합니다.
그게 아니라면 다른 방법의 접근이 필요합니다.

마을 입장 오브젝트에 심겨진 스크립트를 살펴봅시다.
각각의 트리거에는 아래와 같은 스크립트가 심어져 있습니다.

이름이 VileageEnter 이지요.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 트리거 배경음악 재생기 : 스크립트#2. 트리거측
// 설명 : 마을에 진입할 때 트리거에 닿으면 배경음악을 재생합니다.
//  플레이어의 tag 속성을 'myplayer'로 지어주거나, 아래의 소스를 변경해 주시면 됩니다.
// 주석을 삭제하지 않는 조건으로 자유롭게 사용하셔도 됩니다.
// 개발 : Cray
// 블로그 : 크레이의 IT 탐구 https://itadventure.tistory.com/415
public class VileageEnter : MonoBehaviour
{
    // Inspector 영역에 표시할 배경음악 이름
    public string bgmName = "";

    private GameObject CamObject;

    void Start()
    {
        CamObject = GameObject.Find("Main Camera");
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "myplayer")
            CamObject.GetComponent<PlayMusicOperator>().PlayBGM(bgmName);
    }
}

이 명칭이 오브젝트를 선택하면 Inspector 창에서 보이는
VileageEnter 스크립트 컴포넌트가 되는 것입니다.

클래스 정의 시작 부분에는 아래와 같은 코드가 있는데요.

public string bgmName = "";

이 항목은 이 컴포넌트를 추가한 오브젝트에 '속성(Property)'이란걸 부여합니다. 바로 이 부분입니다.

트리거에 부딪힐 때 재생할 배경음악을 정의하는 것입니다.
나중에 배경음악 이름마다 배경음악 오디오 음원을 연결할텐데요.
그 때 사용할 배경음악명입니다.

이렇게 입력하는 속성은 오브젝트마다 다르게 부여해 줄 수 있는데요.
언제든지 유니티 에디터에서 바로 수정할 수 있고
스크립트를 수정해도 바뀌지 않습니다.
그러니까 배경음악이 다른 2개의 트리거가 있더라도 기능이 바뀐 경우
스크립트 수정을 2번 할 필요가 없는 장점이 있습니다.

그냥 여기에 직접 배경음악 오디오 음원을 넣는 방법도 있겠지만 글쎄요..
2가지 단점이 있는데 나중에 설명드리도록 하겠습니다.


그러면 한가지 의문을 가질 수도 있을 것입니다
바로 아래 있는 것은 왜 속성으로 나오지 않느냐는 것입니다.

private GameObject CamObject;

그것은 바로 publicprivate 의 차이점입니다.
딱 잘라 말해서 public 만 속성으로 노출됩니다.
만일 아래와 같이 정의한다면 CamObject 도 속성에 노출되겠지요.

public string bgmName = "";

public GameObject CamObject;

그 다음에 보이는 Start() 나, OnTriggerEnter() 는 
기능을 실행하는 함수(function) 또는 메소드(method)라고 부릅니다.
아래와 같이 표현되는 부분을 보통 그렇게 부르며 기능 덩어리라서 속성으로 노출되지 않습니다.

... 함수( ... ) { ... }

이제 Start() 메소드를 살펴봅시다.
Start() 는 일종의 약속같은 것으로, 유니티에서 오브젝트가 씬에 나타나는 순간에 단 한번만 실행됩니다.
이 것을 이벤트(event)라고 하는데요. 이 경우 시작 이벤트가 되는 것입니다.
여기서 하는 일을 살펴볼까요?

현재 씬에서 Main Camera 라는 오브젝트를 찾아서 CamObject 라는 속성(Property)에 넣어주는 역활을 합니다.
참고로 이 동작은 Main Camera 를 복사해서 또 하나가 생겨나는 명령은 아닙니다.
Main Camera 를 참조 가능한 하나의 핸들이 주어지는 것이라 생각하시면 됩니다.

void Start()
{
    CamObject = GameObject.Find("Main Camera");
}

CamObject 라면 어디선가 본 것 같은데?
맞습니다. 바로 위에 있었지요 :)

private GameObject CamObject;

private 로 부여된 속성은 해당 클래스 내부에서만 사용할 수 있습니다.
밖에서는 접근할 수가 없지요.
이렇게 안에서는 필요하지만 밖에서 접근하는 것을 막고자 할 떄 private 를 사용합니다.

근데 왜 이렇게 하냐구요?
그것은 바로 속도 때문입니다.

1) 카메라 오브젝트를 찾는 동작이 0.1초가 소요되고
2) 카메라 오브젝트에 메시지를 전달하는 동작이 0.1초가 소요된다면,

플레이어가 트리거에 충돌할 때마다 1). 2)번 동작을 매번 실행한다면, 0.2초가 소요되겠지요,
하지만 1)번 동작은 처음에 한번만 실행하면 됩니다.
그러니까 Start()가 처음 실행될 때 한번만 실행해서 CamObject 에 넣어버리는 것으로 이 것을 해결하고,
트리거에 충돌하면 1)번은 하지 않고, 2)번 동작만 실행하는 것이지요.

2)번 동작이 바로 아래 스크립트입니다.

private void OnTriggerEnter(Collider other)
{
    if (other.tag == "myplayer")
        CamObject.GetComponent<PlayMusicOperator>().PlayBGM(bgmName);
}

온트리거엔터 ( OnTriggerEnter ) 또한 유니티에서 약속된 메소드 중의 하나인데요.
바로 플레이어 또는 어떤 물리적인 물체이든지 트리거에 충돌이 시작될 때 발생합니다.

괄호 안쪽을 살펴보시면 아래와 같은 내용이 있는데요.

(Collider other)

이는 충돌한 물체를 의미합니다.
Collider 는 유형이 충돌체라는 의미이고, other 는 충돌한 물체를 의미합니다.
플레이어가 마을 입장 막대에 충돌했을 때 이 other 는 마을 입장 막대일까요? 플레이어일까요?

이 스크립트는 지금 마을 입장 막대에 심어져 있습니다.
그러니 마을 입장 막대 편에서 생각해야 합니다. 그렇다면 결국 부딪힌 물체는 플레이어입니다.

그렇다면 이 부분은 무엇일까요?

if (other.tag == "myplayer")

지난번에 잠깐 설명드렸지만 만일 이 컨텐츠가 멀티 플레이어용이라면,
내 플레이어가 마을에 입장할 때만 배경음악이 바뀌어야지 다른 플레이어가 입장할 때 바뀌면 이상할 겁니다.
그러니까 충돌한 물체의 태그(tag)라는 속성명이 "myplayer"일 때만 실행이 되도록 제한하는 것입니다.

태그는 자모 로봇에 이렇게 심어놓았었지요.

그리고 나서 조건이 맞으면 ( 태그가 myplayer 이면 )
아래 코드가 실행됩니다.

CamObject.GetComponent<PlayMusicOperator>().PlayBGM(bgmName);

주의깊게 보실 부분은 이 부분입니다.

오브젝트.GetComponent<컴포넌트>().***
지금 현재 카메라에는 아래와 같은 스크립트 컴포넌트가 심어져 있습니다.

이 컴포넌트의 이름은 PlayMusicOperator 인데요.
바로 이 컴포넌트를 찾아주는 것이 오브젝트.GetComponent<컴포넌트이름>() 입니다.
카메라에는 많은 컴포넌트가 달려 있는데요.

그 중에서 하나를 골라주는 것이 바로 GetComponent<컴포넌트이름>() 입니다.
여기서는 배경음악을 다루는 PlayMusicOperator 를 다루지만,
Audio Listener 나 Camera 등에도 각각 접근하여 제어 할수가 있는 것이지요.

이 컴포넌트에 접근해서 할 수 있는 가능한 일은 2가지가 있습니다.
첫째는 public 으로 정의된 속성을 바꾸는 일과
둘째로 public 으로 정의된 메소드를 실행하는 일인데요.
아래 코드는 PlayBGM() 이라는 메소드를 실행해주는 역활을 합니다.
그러면서 어떤 배경음악을 연주할지 bgmName 값을 넘겨주는 것이지요

CamObject.GetComponent<PlayMusicOperator>().PlayBGM(bgmName);

바로 아래 값 말입니다 :)

카메라의 PlayMuysicOperator 이라는 스크립트 컴포넌트에는 아래와 같은 내용으로 구성되어있습니다.
여기서 public PlayBGM() 보이시지요? 바로 이 메소드를 실행하는 것입니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 트리거 배경음악 재생기 : 스크립트1. 카메라측
// 설명 : 마을에 진입할 때 트리거에 닿으면 배경음악을 재생합니다.
// 주석을 삭제하지 않는 조건으로 자유롭게 사용하셔도 됩니다.
// 개발 : Cray
// 블로그 : 크레이의 IT 탐구 https://itadventure.tistory.com
public class PlayMusicOperator : MonoBehaviour
{
    [System.Serializable]
    public struct BgmType
    {
        public string name;
        public AudioClip audio; 
    }

    // Inspector 에표시할 배경음악 목록
    public BgmType[] BGMList;

    private AudioSource BGM;
    private string NowBGMname="";

    void Start()
    {
        BGM = gameObject.AddComponent<AudioSource>();
        BGM.loop = true;
        if (BGMList.Length > 0) PlayBGM(BGMList[0].name);
    }
    
    public void PlayBGM(string name)
    {
        if (NowBGMname.Equals(name)) return;

        for (int i = 0; i < BGMList.Length; ++i)
            if (BGMList[i].name.Equals(name))
            {
                BGM.clip = BGMList[i].audio;
                BGM.Play();
                NowBGMname = name;
            }
    }
}

카메라에 들어 있는 이 스크립트가 실제적으로 배경음악을 재생하게 되는데요.
내용이 좀 길어졌으니 다음 편을 기약하도록 하겠습니다.

필요하신 분에게 도움이 되셨을지 모르겠군요.
오늘도 여기까지 읽어 주셔서 감사합니다 :)


다음 게시글 : BGM(배경음악) 바꾸기#3 스크립트 설명(2)

https://itadventure.tistory.com/418

 

유니티3D - BGM(배경음악) 바꾸기 #3. 스크립트 설명편(2)

지난 시간에 이어 스크립트 설명을 재개하도록 하겠습니다. https://itadventure.tistory.com/417 유니티3D - BGM(배경음악) 바꾸기 #2. 스크립트 설명편(1) 지난 시간에는 마을 박스에 진입할 때 배경음악을

itadventure.tistory.com