목차
-
강의
Trigger 안으로 들어갈 때 State를 설정하면 플레이어가 이동하고자 하는 곳에 따라 음악을 동적으로 변경할 수 있습니다. 이 방법은 같은 곳에서 겹치지 않고 따로 떨어진 지역에 적합하지만 지역이 서로 겹칠 경우 한 가지 문제가 생깁니다. 아래는 Village와 Woodlands Trigger의 개념을 그린 이미지입니다.
여기 Woodlands Trigger가 Village Trigger와 살짝 겹치는 것이 보이실 것입니다. 플레이어가 Village에서 나와서 Woodlands로 향하여 Woodlands Trigger 안으로 들어가면 State가 Woodlands로 설정됩니다. 하지만 이전 섹션에서 우리가 구현한 이 논리를 사용하면 플레이어가 Woodlands 안으로 깊숙히 들어가 Village를 떠나면 OnTriggerExitState() 함수가 전역적 Music_Regions State를 Nowhere로 설정하게 되어버립니다. 이 경우 플레이어가 Woodlands 지역 안에 있을 때 Ambient Music를 듣게 되는 상황이 생깁니다.
이 문제를 해결하는 한 가지 방법은 State 목록을 만들어 음악 테마의 우선 순위를 종합적으로 관리해서 플레이어가 들어가는 각 Trigger를 추적하는 시스템을 만드는 것입니다.
이 시스템을 사용하면 플레이어가 Trigger 안으로 들어갈 때마다 State의 목록을 합산하여 플레이어가 얼마나 많은 Trigger 안에 있는지 정확히 알 수 있습니다. 플레이어가 Trigger 밖으로 나가면 시스템이 플레이어가 어떤 다른 Trigger 안에 있는지를 확인합니다. 아무 Trigger 안에도 있지 않을 경우 State가 Nowhere로 다시 설정되죠. 다음 단계에서는 이전 섹션에서 만든 SetMusicState 스크립트를 사용하여 서로 겹치는 Trigger를 처리할 수 있도록 연장해봅시다. 그러기 위해서는 State의 목록을 만들고 Player가 지역 Trigger 안팎으로 드나들 때마다 목록을 동적으로 업데이트해야 합니다.
-
Unity 메뉴에서 Audiokinetic > Certification > 301 > Lesson 8로 가서 Creating a System for Intersecting State Areas를 선택하세요.
이 실습에서는 Village와 Woodlands 음악 지역을 사용하여 작업하겠습니다. 이 두 지역을 이전 실습에서 만든 SetMusicState 스크립트로 설정해야 합니다.
-
Hierarchy에서 Village Music Trigger와 Woodlands Music Trigger 게임 오브젝트를 모두 선택하세요.
Shift, Ctrl(Windows) 또는 CMD(Mac)를 누른 채 두 게임 오브젝트를 클릭하면 모두 선택할 수 있습니다.
여러 게임 오브젝트를 선택한 상태로 Inspector에서 컴포넌트를 추가하면 두 게임 오브젝트에 모두 추가됩니다.
-
Inspector에서 Add Component를 클릭한 후 SetMusicState를 찾아서 선택하세요.
스크립트를 열기 전에 먼저 Wwise-Type State를 지정합시다. 여전히 두 Music Trigger가 선택되었는지 확인하세요.
-
Village와 Woodlands Music Trigger 게임 오브젝트를 모두 선택한 상태로 On Trigger Exit State프로퍼티를 열어서 MusicStates > Music_Regions로 간 후 Nowhere를 선택하세요.
또한 Trigger에 서로 다른 State가 설정되어 있기 때문에 Trigger를 각각 편집해야 합니다.
-
Hierarchy에서 Village Music Trigger만 선택하세요.
-
Inspector에서 OnTriggerEnterState를 열어서 MusicStates > Music_Regions로 간 후 Village를 선택하세요.
-
Hierarchy에서 Woodlands Music Trigger만 선택하세요.
-
Inspector에서 OnTriggerEnterState를 열고 MusicStates > Music_Regions로 가서 Woodlands를 여세요.
이제 스크립트를 열어서 필요한 내용으로 변경해봅시다.
-
Inspector에서 SetMusicState 스크립트를 더블 클릭하세요.
다음으로 플레이 중에 Player-Trigger 상호 작용으로 실행될 State 변화를 추적할 목록을 만들어야 합니다. 목록을 작성하기 위해서는 다음 내용을 입력해야 합니다.
List< >
꺾쇠 괄호 안에서는 이 목록에 저장할 프로퍼티 타입을 선언해야 합니다. 여기에 프로퍼티를 지정할 때처럼 AK.Wwise.State라는 Wwise 클래스 타입을 입력해야 합니다.
-
SetMusicState 스크립트를 열어서 SetMusicState 클래스의 가장 윗 부분에 새로운 줄을 추가하고 List<AK.Wwise.State>를 입력하세요.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SetMusicState : MonoBehaviour { List<AK.Wwise.State> public AK.Wwise.State OnTriggerEnterState; public AK.Wwise.State OnTriggerExitState; private void OnTriggerEnter(Collider other){ if(other.CompareTag("Player")){ OnTriggerEnterState.SetValue(); } } private void OnTriggerExit(Collider other){ if(other.CompareTag("Player")){ OnTriggerExitState.SetValue(); } }
이제 Wwise-Type State 목록을 선언했지만 여기에 이름을 지어주어야 합니다.
-
List<AK.Wwise.State> 뒤에 ListOfStates라고 이름을 지어주세요.
public class SetMusicState : MonoBehaviour { List<AK.Wwise.State> ListOfStates
이제 List가 생겼지만 다른 스크립트에서 이 목록을 확인하고 State를 추가하도록 하려면 몇 가지를 변경해야 합니다. 먼저 public 접근 한정자를 추가해서 이 클래스 밖에서 목록을 볼 수 있게 해주어야 합니다.
-
List 앞에 public을 입력하세요.
public class SetMusicState : MonoBehaviour { public List<AK.Wwise.State> ListOfStates
다음으로 여러분이 쇼핑 목록을 작성하듯이 List를 인스턴스화하고 내용을 추가해야 합니다. List를 인스턴스화하기 위해서는 'new'라는 한정자를 사용합니다.
-
public List<AK.Wwise.State> ListOfStates 뒤에 = new List<AK.Wwise.State>();를 추가하세요.
public class SetMusicState : MonoBehaviour { public List<AK.Wwise.State> ListOfStates = new List<AK.Wwise.State>();
이전 실습에서는 OnTriggerEnter()와 OnTriggerExit()이라는 두 가지 함수를 만들었습니다. Village와 Woodlands Trigger는 모두 동일한 스크립트를 사용하지만 함수가 private 범위로 설정되어 있기 때문에 각 Trigger가 각자 OnTriggerEnter()와 OnTriggerExit() 함수를 자체적으로 호출하게 됩니다. 이 경우 SetMusicState 스크립트가 있는 각 Music Trigger가 앞으로 설정될 State 목록을 독립적으로 추가할 수 있습니다. 하지만 이 스크립트는 각 게임 오브젝트에서 인스턴스화되는데 어떻게 하면 모든 스크립트가 동일한 목록을 참조하도록 할 수 있을까요? 그러기 위해서는 'static' 한정자를 사용해야 합니다. 목록을 static으로 만들면 전체를 통틀어 한 개의 목록만 존재하게 할 수 있습니다. 그렇기 때문에 이 목록을 참조할 경우 모든 스크립트가 동일한 목록을 참조하게 됩니다.
-
public 한정자 다음에 static을 입력하세요.
public class SetMusicState : MonoBehaviour { public static List<AK.Wwise.State> ListOfStates = new List<AK.Wwise.State>();
다음으로 OnTriggerEnter()와 OnTriggerExit() 함수를 변경해서 목록에서 State를 추가하거나 제거할 수 있게 해봅시다. 작업을 시작하기 전에 이전에 Trigger를 설명한 부분으로 다시 돌아가봅시다. 플레이어가 Village에서 시작해 Woodlands로 이동한다고 가정해봅시다. 플레이어가 Woodlands Trigger 안으로 들어가면 Woodlands State가 목록의 가장 앞에 추가됩니다.
플레이어가 계속해서 Woodlands 안으로 들어가면 Village State가 목록에서 제거되며 목록의 가장 앞에 있는 State(Woodlands)로 계속해서 설정됩니다. 하지만 Player가 다시 Village로 돌아가서 Village Trigger 밖으로 나가기 전에 Woodlands를 나갈 경우 Woodlands State가 제거되어 목록의 가장 앞에 있는 새로운 State(Village)로 설정됩니다.
이런 시스템을 만들기 위해서는 List에 Insert() 함수를 사용해서 목록의 특정 위치에 State를 삽입해주어야 합니다. Insert 함수의 괄호 안에 State 프로퍼티를 입력한 후 State를 삽입할 위치를 제공해주는 것입니다. States를 목록의 가장 앞부분에만 삽입하고자 하므로 목록의 인덱스(위치)를 0으로 선언하면 됩니다. (모든 목록은 0으로 시작하여 1, 2, 3...순으로 진행됩니다)
-
OnTriggerEnter() 함수에서 OnTriggerEnterState.SetValue();를 ListOfStates.Insert(0, OnTriggerEnterState);로 바꿔주세요.
public static List<AK.Wwise.State> ListOfStates = new List<AK.Wwise.State>(); public AK.Wwise.State OnTriggerEnterState; public AK.Wwise.State OnTriggerExitState; private void OnTriggerEnter(Collider other){ if(other.CompareTag("Player")){ ListOfStates.Insert(0, OnTriggerEnterState); } }
Insert() 함수는 대상 위치를 절대 덮어쓰지 않으며 대신 나머지 요소(element)를 목록에서 뒤로 옮깁니다.
그런 다음, 목록에서 첫 번째 요소에 접근하려면 접근하고자 하는 요소의 위치를 [ ]로 감싸주어야 합니다. 목록의 가장 앞에 있는 요소가 가장 최근에 입력된 지역이기 때문에 위치를 0으로 지정하면 됩니다. 그렇기 때문에 가장 앞에 있는 State를 찾기 위해서는 먼저 목록의 이름을 선언하고 대괄호를 열어서 숫자 0을 입력한 다음 괄호를 닫아야 합니다. State를 찾은 후에는 제 5강에서 설명해드린 SetValue() 함수를 호출할 수 있습니다.
-
ListOfStates.Insert(0, OnTriggerEnterState); 뒤에 새로운 줄을 추가하고 ListOfStates[0].SetValue();를 입력하세요.
public static List<AK.Wwise.State> ListOfStates = new List<AK.Wwise.State>(); public AK.Wwise.State OnTriggerEnterState; public AK.Wwise.State OnTriggerExitState; private void OnTriggerEnter(Collider other){ if(other.CompareTag("Player")){ ListOfStates.Insert(0, OnTriggerEnterState); ListOfStates[0].SetValue(); } }
이제 목록에 State를 추가하고 값을 설정하게 되었지만 아직 Trigger 밖으로 나갈 때 State가 목록에서 제거되지 않습니다. 예를 들어 플레이어가 Village Trigger 밖으로 나가면 Village Trigger의 State가 목록에서 제거되어 목록의 가장 앞에 있는 새로운 State로 설정되어야 합니다.
목록에서 요소를 제거하기 위해서는 목록에 Remove() 함수를 사용합니다. 그러면 이 함수가 위치에 상관 없이 여러분이 지정한 State를 찾아 제거합니다.
-
OnTriggerExit() 함수에서 OnTriggerExitState.SetValue();를 ListOfStates.Remove(OnTriggerEnterState);로 바꾼 후 스크립트를 저장하세요.
private void OnTriggerExit(Collider other){ if(other.CompareTag("Player")){ ListOfStates.Remove(OnTriggerEnterState); } }
게임을 플레이해서 구현 결과를 시험해봅시다.
-
Unity에서 Play를 클릭한 후 Village Trigger 안으로 뛰어가세요. 그런 다음 Village Trigger 밖으로 나가지 않도록 다리 위로 간 후에 다시 Village로 돌아오세요.
Woodlands와 Village Trigger가 겹치는 부분 밖으로 나갈 때 음악이 다시 Village 테마로 돌아가지 않는 것이 들리실 것입니다. 그 이유는 요소가 제거될 때 State가 다시 설정되지 않기 때문입니다. 목록에서 요소 하나가 제거되면 가장 앞에 있는 새로운 State로 설정되어야 합니다. 이제 게임을 중단하세요.
-
ListOfStates.Remove(OnTriggerEnterState); 뒤에 새로운 줄을 추가하고 ListOfStates[0].SetValue();를 입력하세요.
private void OnTriggerExit(Collider other){ if(other.CompareTag("Player")){ ListOfStates.Remove(OnTriggerEnterState); ListOfStates[0].SetValue(); } }
작업이 거의 끝났습니다! 아직 한 가지 문제가 남아있습니다. 플레이어가 아무 Music State Trigger 안에도 있지 않을 경우 Nowhere State로 설정되어야 합니다. 현재 구현 작업만으로는 목록이 비워져서 Null Reference Exception이 생기게 됩니다. 이 문제를 해결하기 위해서는 if 문을 추가해서 목록에 요소가 있는지를 평가하고 아무 것도 없을 경우 Nowhere State로 설정하도록 해야합니다. 목록에 요소가 있는지 확인하기 위해서는 목록의 요소 개수를 담은 .count 프로퍼티를 사용합니다. 이 숫자가 0보다 많을 경우 목록이 비어 있지 않다는 것을 알 수 있죠.
-
ListOfStates.Remove(OnTriggerEnterState); 후에 새로운 줄을 추가하고 if(ListOfStates.Count > 0){을 입력하세요.
if(other.CompareTag("Player")){ ListOfStates.Remove(OnTriggerEnterState); if(ListOfStates.Count > 0){ ListOfStates[0].SetValue(); }
이 줄에서 여는 중괄호 ' {'만 입력하고 닫는 중괄호 ' }'는 입력하지 않는다는 것에 주의하세요. 그 이유는 'ListOfStates[0].SetValue();'가 조건이 true일 경우에만 실행되도록 해야 하기 때문에 'ListOfStates[0].SetValue();' 후에 '}'를 입력해서 if 문이 true일 경우에만 실행할 코드 부분을 정의해주어야 하기 때문입니다.
-
ListOfStates[0].SetValue(); 후에 새로운 줄을 추가하고 닫는 중괄호 ' }'를 입력하세요.
if(other.CompareTag("Player")){ ListOfStates.Remove(OnTriggerEnterState); if(ListOfStates.Count > 0){ ListOfStates[0].SetValue(); } }
이제 목록이 비어있지 않을 경우에만 값을 설정할 것입니다. 하지만 Music Trigger가 없는 모든 영역에서 사용되는 Nowhere State의 경우 어떻게 해야 할까요? 목록 내용의 개수가 0일 경우 Nowhere State가 설정되어야 합니다. 목록이 비어있는지의 여부를 평가하기 위해서 if 문과 else{} 조건을 사용해봅시다.
else 문을 사용하면 조건이 false인 모든 호출을 else 문의 중괄호 안에 있는 코드로 보낼 수 있습니다.
-
if 문의 닫는 중괄호 후에 else{를 입력하세요.
if(other.CompareTag("Player")){ ListOfStates.Remove(OnTriggerEnterState); if(ListOfStates.Count > 0){ ListOfStates[0].SetValue(); }else{ }
여러분의 코드 편집기가 닫는 중괄호를 써서 else 문을 자동 완성한 경우 다음 단계를 건너 뛰셔도 됩니다.
-
Enter를 두번 누른 후 닫는 중괄호 '}'를 입력하세요.
if(ListOfStates.Count > 0){ ListOfStates[0].SetValue(); } else{ }
이제 else 코드가 호출될 때마다 State가 Nowhere로 설정됩니다. 이렇게 하면 플레이어가 아무 Music Region Trigger 안에도 있지 않을 경우 전역적 State가 Nowhere로 설정되어 음악이 Ambient 테마를 재생하게 되죠.
-
else 문의 중괄호 안에 OnTriggerExitState.SetValue();를 입력하세요.
public class SetMusicState : MonoBehaviour { public static List<AK.Wwise.State> ListOfStates = new List<AK.Wwise.State>(); public AK.Wwise.State OnTriggerEnterState; public AK.Wwise.State OnTriggerExitState; private void OnTriggerEnter(Collider other){ if(other.CompareTag("Player")){ ListOfStates.Insert(0, OnTriggerEnterState); ListOfStates[0].SetValue(); } } private void OnTriggerExit(Collider other){ if(other.CompareTag("Player")){ ListOfStates.Remove(OnTriggerEnterState); if(ListOfStates.Count > 0){ ListOfStates[0].SetValue(); }else{ OnTriggerExitState.SetValue(); } } }
이제 결과물을 시험해봅시다!
-
Unity에서 Play를 클릭하고 Village Trigger 안으로 가서 Village Trigger 밖으로 나가지 않도록 다리 위로 간 후에 다시 Village로 돌아오세요.
플레이어가 Village 안으로 들어가면서 Village 테마가 먼저 재생될 것입니다. 다리 위에서 Trigger가 겹치는 곳으로 들어가면 테마가 Woodlands로 전환됩니다. 하지만 다시 Village로 돌아가면 Village 테마가 다시 재생되죠.
-
Woodlands Trigger 안으로 계속 들어가서 Village Trigger 밖으로 나와보세요.
Woodlands 안으로 들어가서 Village 밖으로 나가면 Village 테마가 Woodlands 테마로 바뀌며 Village Trigger로 다시 들어가기 전까지 계속해서 재생됩니다. 플레이어가 어떤 음악 Trigger 안에도 있지 않을 경우 우리가 스크립트에서 추가한 조건을 통해 Nowhere State가 설정됩니다. 이제 게임을 중단하셔도 됩니다.
이렇게 여러 State Trigger를 처리하는 시스템을 만드는 데에 필요한 모든 작업을 끝냈습니다. 만약 이 자격증 과정에서 처음으로 코드를 작성하신 것이라면 정말 자랑스럽게 생각하셔도 됩니다! 여러분은 프로그래머가 아닐지라도 이제 수많은 프로그래머가 매일 사용하는 다양한 기본 기술을 익히셨습니다. 목록 함수(Remove나 Insert)를 현명하게 사용하면 거의 대부분의 프로퍼티나 변수를 제어할 수 있습니다. 프로그래머는 오디오 통합 뿐만이 아닌 다른 개발 작업 중에도 이 기술들을 사용합니다. 여러분도 if 문이나 list와 같은 기술에 대한 몇 가지 지식을 통해 일반적인 프로그래밍 방법을 배우셨습니다.