Graditude Project Wiki %3A VR Rhythm Game - UAVisonline/Portfolio GitHub Wiki

졸업프로젝트 : 가상현실 및 사용자 신체활동을 결합한 리듬게임제작

해당 게임 프로젝트는 필자의 한양대학교 졸업을 위해 진행된 프로젝트이며, 총 3명의 팀으로 프로젝트 기획 및 제작이 진행되었다.
해당 프로젝트는 VR(Oculus Rift 1)으로 플레이 하는것이 가능하며, 게임 내 6가지 음악에 따라 앞에 나오는 물체를 타이밍에 맞춰서 주먹으로 처리하는 방식으로 진행된다. 또한 5개의 음악에 대해서 주변 환경 내 연출이 존재하며 해당 졸업 프로젝트에서는 이러한 연출이 사용자에게 어떠한 영향을 미치는지 파악하는 것을 목표로 한다.

해당 프로젝트는 2022년 3월부터 개발이 진행되었으며 2023년 7월 말 개발이 완료, 8월 사용자 조사 및 보고서 작성을 통해 프로젝트가 완료되었다.

이번 Wiki에서는 해당 프로젝트 내에서 필자가 맡아 개발한 영역에 대해 서술하고자 하며 이를 요약하면 아래와 같다.

  • 게임 내 음악 및 연출, 난이도 선택 기능

  • 음악에 따른 가상현실 연출 효과 제어

1. 게임 내 음악 및 연출, 난이도 선택 기능

1.플레이를 위해서 게임은 어떠한 정보를 담아야 하는가? (Unity - ScriptableObject)

해당 게임은 6가지 음악에 대해 연출, 난이도 밑 멀티플레이 유무를 선택할 수 있으며 이에 따라 실제 게임의 내용이 변화한다. 우선 이때 제작자가 음악 항목에 대해서 고려할 요소는 아래와 같다.

1. 음악 제목 및 작곡가 이름
2. 재킷 이미지
3. 음악 음원
4. 음악 난이도에 따른 패턴 및 해당 난이도 level
5. 음악 연출 설정에 따른 연출감독 오브젝트 (2. 음악에 따른 가상현실 연출 효과 제어에서 설명)

이러한 정보를 담을 수 있는 클래스 Script를 만들 수 있지만, 이를 게임 내 객체로 저장하면 각 음악마다 게임 오브젝트를 만들어야하는 상황이 발생한다. 변경되지 않는 단순한 정보를 저장/불러오는 용도로 게임 오브젝트를 사용하기에는 부하가 된다. 그렇기에 해당 프로젝트에서는 Unity의 ScriptablObject를 사용하여 이 문제를 해결하였다.

프로젝트에는 MusicTemplateObject라는 Script가 있으며 해당 Script는 아래와 같은 코드 내용을 통해 위에서 언급한 정보를 저장 및 관리, 사용이 가능하다.

10     [TabGroup("UI Component")] [SerializeField] private Sprite album_jacket;
       ...... // 데이터 변수
27     [BoxGroup("Director Object")] [SerializeField] private GameObject nothing_director;

29     public Sprite get_album_jacket()
       ...... //데이터 반환 함수
96     public GameObject get_nothing_stage()

또한 게임 내 패턴 데이터(MusicPatternObject Script)도 ScriptableObject형식으로 틀을 작성, 패턴 데이터 내 각 노트에 대한 데이터 배열을 넘겨주는 반환함수를 통해 실제 게임과 상호작용 하도록 하였다. 단 해당 ScriptableObject는 제작편의를 위해 아래와 같은 함수가 추가되어있다.

1. set_temp_position : 노트 데이터 배열의 위치 구조체 배열값을 읽어서 Vector 위치값을 할당하는 함수 (3번 개선 함수로 변경)
2. apply_reference : reference TextAsset을 읽어서 노트 데이터의 시간 및 위치 구조체 값을 읽는 함수
3. amke_pattern_using_reference : 위 2번 함수에서 읽은 시간/위치 구조체 값을 이용해 실제 노트 데이터를 채우는 함수


2. 위 정보를 게임 시스템은 어떻게 처리하는가? (AlbumSelect Script 및 GameStart Script)

실제 게임 내에서는 사용자가 위 음악 정보들을 선택할 수 있어야 하며, 이를 위해 게임 내에서 선택 오브젝트가 필요하다. 해당 선택 오브젝트 내에는 AlbumSelect Script가 있으며, 해당 Script에는 노래에 관한 위 ScriptablObject 배열 및 현재 가리키는 노래 index 변수 및 난이도, 연출모드, 멀티선택모드 열거체 변수가 존재한다.

게임이 시작하면 AlbumSelect Script에서는 위 4가지 변수 4개를 기본값으로 설정하며 function_about_clip_change()라는 함수를 이용해 음악 재생 및 Text UI에 해당 음악에 대한 정보를 반영한다. 그 뒤 앞, 뒤 버튼을 이용해 index값을 변환하고 위 함수를 다시 실행해 정보를 변경, 난이도 및 연출, 멀티플레이 버튼을 누르는 것으로 열거체 변수를 변경한다.

그 뒤 플레이할 음악 및 난이도, 연출유무를 고르고 게임시작을 누르면 GameStart Script에서 AlbumSelect Script 정보를 가져와 GameManager에 집어 넣은후 Play field로 전환한다. Play field에서는 GameManager에서 방금 받아온 정보를 읽어 음악 및 노트, 연출장치를 미리 생성하며, 3가지 모두 생성이 되었을 때 음악을 실행시켜서 노트 및 연출장치를 시간에 맞춰 동작시킨다.

2. 음악에 따른 가상현실 연출 효과 제어

GameManager는 실제 게임 플레이 Scene으로 이동하게 되는 경우, 1-1에서 언급한 5번 연출감독오브젝트를 노래에 따라 불러오게 된다.
해당 연출감독 오브젝트는 노래에 따른 다양한 가상연출을 가지고 있으며 이를 적절한 시점에 맞추어 발동하는 방식으로 작동한다.
아래 2가지 항목을 통해 해당 방식을 어떻게 만들었는지 설명하고자 한다.

1.가상현실 연출의 추상화

노래 내 가상 연출은 불, 새로운 빛, 하늘교체, 오브젝트의 색깔변경 외 다양한 연출들이 존재한다. 이러한 연출을 관리하고자 우선 각 연출을 공통요소로 묶어서 정리하고자 했으며, 아래 3가지로 가상 연출을 정리할 수 있었다.

1. 빛과 같은 광원을 가진 연출오브젝트(LightObject.cs)
2. 파티클 효과를 생성하는 연출오브젝트(ParticleDirectObject.cs)
3. 그 외 효과를 보여주는 연출오브젝트(DirectGameObject.cs)

각 연출 오브젝트는 필수 Component를 요구하며, 이를 이용해 연출을 구현/재생한다.
또한 기본적인 function이 연출 오브젝트 마다 따로 존재하며 이를 정리하면 아래와 같다.

광원연출 오브젝트 (LightObject.cs)

필수Component : Unity Light Component (Unity 내 광원 Component를 이용해 게임 내 광원 연출을 적용)
권장Component : Reference GameObject (위 발광효과를 가진 오브젝트를 만들고 싶으면 해당 GameObject Reference가 필요)

function
- set_color_instant(Color color) : 매개변수 color값으로 광원의 색깔 변경,  
또한 Ref GameObject가 존재하는 경우 해당 Object의 material EmissionColor도 해당 color로 변경
파티클 오브젝트 (ParticleDirectObject.cs)

필수Component : Unity ParticleSystem Component List (파티클효과를 재생하는 Unity Component를 이용해 게임 내 파티클 연출)

function
- play & stop : 위 Component List 내 모든 파티클 효과를 재생 혹은 중단
연출오브젝트 (DirectGameObject.cs)

권장 Component : Unity Animator Component (오브젝트의 애니메이션을 바꿀경우 사용해야하나, 애니메이션을 바꿀 필요없는 오브젝트의 경우에는 사용하지 않음)

function
- play_animation(string name) : name이름의 Animation을 실행해서 적용, 위 Animator Component가 필요
- function0~4 : 가상함수, 상속한 Script에서 오버라이딩해 상세 내용을 구현 

2.연출 관리자를 통한 연출 제어 (DirectorObject)

GameManager가 실제 Game Scene으로 이동하게 되는 경우, 위에서 말한대로 노트 패턴 및 연출장치를 만든다고 하였다. 이 때 연출장치는 1-1에 5번 항목이 GameManager StageObject에 들어가게 되며, 이를 Game Scene 내 GetDirector Object가 GameManager의 making_Stage function을 호출하는 방식으로 생성된다.

making_Stage function은 연출장치를 생성한 뒤 GameManager의 game_director 변수를 true로 만들어 연출장치가 준비되었다는 것을 전달, 노트패턴도 비슷한 방식을 통해 준비가 되었다는 것을 GameManager에게 전달하여 2개의 변수가 모두 true인 경우 실제 음악을 재생하게 된다.

이 때 GameManager는 연출장치 DirectorObject에게 현재 음악의 재생시간을 매 프레임마다 변수로 전달한다. 그리고 DirectorObject는 이를 이용해 자신에게 입력된 연출을 확인, 음악의 시간이 연출이 발생되어야할 시간을 넘었을 경우 해당 연출을 재생하는 방식으로 작동한다. (DirectorObject.cs : Direct_update function)

DirectorObject Script는 direct_information 직렬화 구조체를 포함하고 있으며, 해당 구조체는 명령어(string), 실행지점(float), 정수인자 및 실수인자, 재생용 정수인자 5가지 값을 가지고 있다. 연출제작자는 DirectorObject 내 해당 구조체 배열을 채워 넣는 것으로 어떤 연출을 어떻게 재생할 지 작성이 가능하다.

DirectorObject는 위 3가지 연출오브젝트를 List 형태로 참조하고 있으며 구조체의 정수인자 및 명령어를 이용해 특정 연출오브젝트의 접근 및 함수호출이 가능하다. 이를 이용해 음악의 특정 시점에서 연출오브젝트를 재생/정지/연출변경하는 것이 가능하며, Direct_update function의 내용을 추가하는 것으로 새로운 공통 연출명령을 추가하는 것이 가능하다.

  • DirectObject의 function을 오버라이딩 하는 것으로 개별 연출 오브젝트의 세세한 명령을 내리는 것이 가능, 이는 공통 연출명령 'GameObject_function'으로 처리

  • 노래에 따른 메인광원 및 Skybox가 다를 수 있음, 이는 DirectorObject의 sun_object, first_skybox 변수를 이용해 조정할 수 있으며 이를 위한 공통 연출명령도 존재

  • 추상화된 3가지 연출오브젝트는 각기 다른 공통 연출명령을 사용, 따라서 Particle에 대해 DirectObject 명령을 사용하는 것은 시스템 상 불가능

⚠️ **GitHub.com Fallback** ⚠️