2D 1대1 대전 게임이며, 고정된 맵에서 전투를 한다는 특성상 맵 내의 그래픽 요소는 폴리싱 단계에서 꽤 중요한 요소일 것이다. 팀 회의를 통해 정해진 컨셉상 사이버펑크스러운 색깔을 셰이더로 만들어보자는 방향으로 결정되어 아래와 같은 컨셉의 색깔을 구성하기로 했다.
캐릭터에는 동적인 색상 변화를 부여하여 시각적 몰입감을 높일 수 있도록 라이브 그라데이션 셰이더를 제작한다. 1대1 게임 상황에서 플레이어의 색 구분을 위해 플레이어 1의 색깔은 오렌지 ~ 노란색 그라데이션 색으로 하고, 플레이어 2의 색깔은 진파랑 ~ 하늘색 그라데이션 색으로 한다.
플랫폼 오브젝트에 상단-중단-하단 색상을 다르게 적용하여 깊이감과 시각적인 변화를 제공하는 셰이더로 제작한다. 단순 단색보다 게임 환경의 분위기와 레벨 디자인에 맞춘 색상 변형 가능한 형태로 제작하였으며, 에디터에서 색상과 그라데이션 범위를 조절하여 다양한 맵 스타일을 쉽게 구현.
셰이더를 만드는 방법을 알아보기 위해 유니티 공식 메뉴얼의 내용을 참고했다. 하지만 노드 라이브러리만 봐도 엄청나게 많은 기능이 있기 때문에, 이것들 전부를 살펴 보면서 직접 적용해보기에는 시간상으로 부족할 것이 확실했기 때문에, 핵심적으로 사용할 기능 위주로 찾아보면서 셰이더 그래프 활용 방법에 대한 개념을 잡았다.
<플랫폼 셰이더>
플랫폼 셰이더는 Shader Graph를 사용하여 Top / Middle / Bottom의 3가지 색상을 위치 기반으로 부드럽게 보간하는 방식으로 구현되었다.
Position 노드와 Lerp 연산을 활용하여 오브젝트의 월드 좌표(Y축)를 기반으로 색상을 혼합하고, Spread(범위)와 Origin(기준점)을 조절해 다양한 플랫폼 크기와 형태에 대응하도록 했다.
3단 색상 구조로 단순한 그라데이션보다 풍부한 시각 효과 제공할 수 있으며, 색상, 범위, 기준 위치를 에디터에서 실시간 조절이 가능하도록 제작하였다. 또한 플랫폼의 형태가 다양한 상황에서도 안정적으로 적용할 수 있도록 Vertical 그라데이션으로 제작하였다.
<캐릭터 셰이더>
캐릭터 셰이더는 Shader Graph를 사용하여 실시간 회전하는 그라데이션 효과를 생성한다.
Time 노드와 UV 회전(Rotate) 노드를 이용해 텍스처 좌표를 지속적으로 회전시키며, Gradient를 샘플링해 부드러운 색상 변화를 표현했다.
이와 같이 구성한 셰이더 그래프는 Slider로 양/음수 회전을 결정하는 것으로 회전 속도와 방향 조절이 가능하다. 또한 Texture 없이 순수 계산 기반으로 구현되어 메모리 부담이 적으며 다양한 색상 팔레트를 적용할 수 있다.
셰이더 그래프는 게임을 개발하는 데 있어서 시각적인 효과와 몰입감을 향상시키는 요소 중 하나이며, 게임의 완성도를 높이는 요소 중 하나이다. 또한 셰이더 그래프를 사용하여 재질을 직접 개발하는 형태는 리소스 절감을 위한 방법 중 하나가 될 수 있으며, 텍스쳐 애니메이션 없이 UV와 Gradient 계산만으로 구현되기 때문에에 메모리 사용량을 줄일 수 있다. 독자적인 색감을 넣는 데에도 셰이더 그래프는 활용될 수 있으며 유연하게 스타일을 변경할 수 있는 방식으로 구현하여 게임의 깊이감을 줄 수 있는 방식 중 하나가 될 수 있다.
앞선 과정에서 맵의 플랫폼의 형태를 다양하게 만들고 셰이더까지 적용하여 디자인의 완성도를 높였다. 다만 여기에서 그 이상으로 퀄리티를 높일 수 있는 방법으로 빛과 같은 효과를 사용하는 것이 어떤지 방법을 모색해보게 되었다.
2D 프로젝트에서는 3D 프로젝트와 달리 빛 연산이 들어가는 방식이 아니다 보니 3D 프로젝트로 전환하는 방식도 고려했으나, 3D 프로젝트로 전환하면 오브젝트에 그림자가 생기는 부분이 발견되었다. 이는 프로젝트 기획 의도상 적절한 그래픽은 아니라고 판단되어 3D 프로젝트로의 전환은 보류되었다.
따라서 2D 프로젝트 환경에서의 빛 효과를 낼 방법에 대해 찾아봐야 했고 이 중 포스트 프로세싱의 블룸 효과에 대해 주목하게 되었다.
포스트 프로세싱은 기존에 렌디렁된 씬에 렌더링 효과를 더하는 작업으로, 여러가지 다양한 효과가 있다. 이 중에서 블룸은 이미지의 밝은 영역의 경계에서 확장되는 광원을 생성하여 씬을 강조하는 착시효과를 만들어낸다.
<유니티 공식에서 제공하는 자료화면>
이를 활용하기 위해서 하이어라키 상에서 Global Volume을 추가하고, Profile을 추가하여 아래와 같이 블룸 효과를 설정했다.
다만 이와 같이 포스트 프로세싱의 효과를 사용할 시에 생각해 봐야 할 점이 있는데, 유니티 프로젝트 - URP 상에서 포스트 프로세싱을 사용할 시에 레이어를 지정하여 Global Volume을 조정할 수 없고 특정 영역 범위에서 포스트 프로세싱이 적용된다. 즉, 맵 내에 생성되는 플레이어나 탄환, UI 등과 같은 오브젝트에도 블룸이 적용되어 버린다는 것이다.
이와 같이 모든 오브젝트에 블룸이 적용되는 것은 의도하지 않은 방향이기 때문에 블룸이 플랫폼에만 적용될 수 있도록 설정해줄 필요가 있다.
포스트 프로세싱이 최종적으로 적용되는 것은 카메라를 통해서이므로, 이런 블룸의 효과를 오브젝트별로 분리하기 위해서는 카메라의 설정을 통해서 분리해야 한다.
메인 카메라의 자식오브젝트로 카메라를 추가로 배치한 다음, 해당 카메라에는 포스트 프로세싱을 꺼준다.
카메라의 Render Type는 Overlay로 설정한 후 Main Camera의 스택에 등록한다. 그리고 Main Camera의 Rendering 레이어에 포스트 프로세싱을 적용할 레이어를 선택하고, No Post Cam에 포스트 프로세싱을 적용하지 않는 레이어를 선택한다.
이와 같이 분리하면 특정 레이어에만 선택적으로 포스트 프로세싱이 적용된 화면을 적용할 수 있다.
2D 환경과 고정되어 있는 맵이라는 특성에서 다소 밋밋하게 보일 수 있는 맵에 효과를 더하여 한층 더 퀄리티를 높일 수 있는 작업으로, 마치 플랫폼이 빛나는 듯한 효과를 줄 수 있음. 이에 따라 유저의 게임에 대한 몰입감을 높이고 게임의 완성도를 높이는 효과를 기대할 수 있음.
레퍼런스가 된 Rounds에서 역동적인 게임 플레이 요소 중 하나가 되는 것이, 동적인 UI 애니메이션이 들어가 있다는 것이다.
단순히 승 수를 표시하는 것만이 아닌 UI에 애니메이션 요소를 넣음으로서 역동적인 게임 플레이 경험을 만들 필요성이 있었다. 이는 게임 자체에 필수적으로 필요한 기능은 아니지만 폴리싱 단계에서 필요한 요소로서 게임의 완성도를 높일 수 있는 요소가 될 것이다. 따라서 앞서 맵 이동 방식에서 사용했던 닷트윈을 UI 요소에도 반영하여 사용하여 애니메이션 효과를 주는 방식을 구현하고자 하였다.
게임 사이클 플로우를 살펴 보면, 한 라운드에는 최대 3번의 대전을 진행하며, 먼저 2승을 차지한 플레이어 1점을 얻는다. 이와 같은 점수 득점 상황에서 먼저 2점을 얻는 플레이어가 승리하게 되는 구조로, 라운드별 3판 2선승제, 게임 내 3라운드 중 2라운드를 먼저 이기는 플레이어가 최종 승리자가 되는 게임이다.
따라서 인게임에 필요한 UI는 다음과 같다.
그러므로 각각의 UI를 컨트롤하는 3가지 UI 컨트롤러 컴포넌트가 필요하며, 이 UI들의 표시를 총체적으로 관리하는 IngameUIManager가 필요하다.
따라서 구조는 위와 같이 설계하였으며 인게임 매니저에서 게임 상황에 따른 정보를 전달받아 상황에 맞는 UI를 출력하도록 설계하였다.
각 컨트롤러가 하는 역할 및 구성을 위한 애니메이션 작업은 다음과 같다.
RoundOverPanelController
해당 패널의 경우 애니메이션적 요소가 많으므로, 우선 점수를 표시하는 UI를 초기화하는 작업이 우선이 되어야 함.
UI는 초기화와 동시에 IngameManager를 통해 받아온 점수를 표시함. Image의 FillAmount를 통해 표시가 되었으며 0점일 경우 채워지지 않은 원, 1점일 경우 반원 분량만큼 채워진 상태, 2점으로 최종승자가 됐을 경우 완전히 채워진 원이 된다.
게임 상황에 따라 애니메이션은 두 가지 경우로 나뉘어 재생됨.
최종 승리자가 확정되지 않은 상태에서는 점수를 표시하고, DOScale을 통해 UI가 비활성화되기 직전 줄어들어 사라지는 애니메이션을 재생
최종 승리자가 확정되었을 경우 승자의 점수 UI는 DOScale로 사이즈가 줄어들어 자신의 위치에서 StaticScoreUI의 최종 점수 위치로 DOMove로 이동하며 이후 비활성화됨. 패자는 승리자의 애니메이션이 재생된 이후 점수 UI가 중앙 하단으로 DOMove로 이동한 후 DOScale로 지정한 사이즈만큼 커짐. 이후 카드 선택 UI가 활성화되면서 해당 애니메이션으로 캐릭터가 카드를 선택하도록 자리잡는 듯한 연출을 함.
여기서 해당 애니메이션의 경우 순서 및 딜레이에 대한 요소가 중요하므로, 아래와 같이 인스펙터 상으로 조작해야 할 변수 등 복잡성을 줄이기 위해 내부적으로 변수를 묶어서 처리하였음. 이에 따라 인스펙터 변수를 각각 조작하다 애니메이션의 순서가 꼬이는 현상을 미연에 방지하고자 함.
StaticScoreUI
게임 최초 승리 시에는 해당 칸에 점수를 시각적으로 표시해야 하므로, 비활성화되어 있던 점수표시 UI를 활성화함.
최종 승리자가 되었을 경우 DOScale로 해당 활성화된 점수표시UI를 확정 표시로 바꾸고, 최종 패배일 경우 해당 칸의 점수표시 UI를 다시 비활성화함함
GameRestartPanelController
해당 패널에는 애니메이션 효과가 필요하지는 않음. 다만 게임 재시작 여부 판정과 리매치 거부 시 로비로 이동하는 연결 작업을 진행함.
이때, 게임을 재시작하는 경우 게임 시작 전 로딩씬으로 이동한 후 인게임 씬을 재로딩하는 방식으로 씬 자체를 초기화하는 방식으로 구현함. 하나의 씬에서 누적된 정보로 인한 버그 발생 등의 요소를 최소화하고자 함.
<라운드 종료 애니메이션>
<게임세트 종료 애니메이션>
인게임에서의 플레이에서 단순히 게임의 결과만을 표현하는 방식에서 끝나는 것이 아닌 애니메이션 효과를 부여함으로서, 승리 - 패배 상황을 시각적, 극적으로 연출할 수 있다. 또한 유저의 입장에서도 UI란 요소를 통해 게임의 규칙과 진행 현황을 빠르게 파악할 수 있게 하며, 긴장감과 기대감을 부여할 수 있는 역할을 수행한다. 이에 따라 게임의 완성도를 높아 보이게 하며 독창적인 연출로 차별화를 기대할 수 있음.
또한 IngameUIManager, PanelController 등 컴포넌트 기반 구조로 제작되어, 다른 게임 모드나 프로젝트에서도 손쉽게 재활용할 수 있는 구조로 설계되었음. 이에 따라 라운드 수, 세트 수, 애니메이션 스타일 등을 변수로 관리해 다양한 상황에 적용할 수 있음.
Rounds의 캐릭터의 팔과 같은 경우 곡선형의 팔을 가지고 있으며, 팔이 쓰이는 장소는 아래와 같은 카드 선택 씬에서의 카드를 가리킬 때와 인게임에서 마우스 방향으로 총을 겨누는 식으로 팔이 작동한다.
단순히 사람 팔 처럼 관절이 있는 팔을 만드는 경우라면 Character Joint 같은 것을 사용하는 방식을 고려해보겠지만, 캐릭터 특성상 다소 비현실적인 곡선형 팔을 가지고 있다 보니, 이 부분은 일반적인 팔 관절을 만드는 방식 혹은 애니메이션만으로 구성하기에는 다소 어려움이 있어보였다. 따라서 곡선을 그릴 방법에 대해 리서치를 해 보면서, 유니티 레지스트리 기능 중 하나인 Splines라는 기능에 주목하게 되었다.
Rounds의 카드 선택 중의 캐릭터 애니메이션을 보면, 계속해서 몸을 들썩이면서 어깨 부분은 계속 움직이고, 팔은 고정된 카드의 위치를 가리키고 있다는 특징이 있다. 따라서, 캐릭터의 몸체의 애니메이션을 정의하는 컴포넌트와 팔의 애니메이션을 정의하는 컴포넌트로 나누어야 한다.
캐릭터의 Body에는 활성화와 동시에 위와 아래로 들썩이는 애니메이션 효과를 준다. 이를 위해서 닷트윈에서 DOMove 를 이용하여 애니메이션을 구현하고 코루틴으로 위아래의 들썩임을 표현한다.
캐릭터의 오른쪽 팔과 왼쪽팔에 대한 컴포넌트를 각각 생성한다. Spline의 노드는 3개로 구성하며 Spline의 0번째 노드는 플레이어의 Body의 자식 오브젝트로 있는 어깨의 위치, 2번째 노드는 플레이어가 가리켜야 하는 좌표로 둔 후 1번째 노드는 0번째 노드와 2번째 노드를 Lerp하여 설정한 노드를 등록하게 한다.
해당 두 개의 팔을 관리하는 컴포넌트를 두어, 유저가 현재 가리키고 있는 카드가 어떤 것인지 정보를 받아오고, 이에 따라 카드를 향해 손을 뻗는 적절한 애니메이션을 출력할 수 있도록 한다.
팔이 가리키는 좌표는 인스펙터 상에서 빈 오브젝트의 위치로 조작할 수 있게 하였으며, 카드 개수 조정 등의 상황에서도 빈 오브젝트를 추가하거나 삭제하는 방식으로 쉽게 조정이 가능하다.
단순히 유저에게 카드를 보여주고 뽑게 만드는 화면만 보여주는 것이 아닌, 연출적으로 자신이 선택한 카드가 무엇인지 시각적으로 보여주고, 캐릭터가 직접 카드를 뽑는 것 같은 실감나는 연출을 보일 수 있음. 애니메이션을 통해 카드를 뽑는 화면에서의 공간감도 채워주며, 게임의 완성도를 높일 수 있는 요소로서 작용함함.
또한 이와 같이 팔을 만드는 방식을 응용하여 실제 인게임 내 캐릭터에도 팔을 붙일 수 있었으며, 이는 단순히 팔 애니메이션으로 표시하는 방법보다 훨씬 자연스럽게 동작할 수 있게 할 수 있었음.
만들어야 하는 게임 특성상 네트워크 요소를 활용해야 하며, 네트워크 동기화에는 많은 요소에 대한 동기화가 필요하다. 네트워크 동기화라는 작업 자체는 한 사람만이 전담하는 작업은 아니었으며, 이 중 본인이 맡아서 진행해야 하는 업무는 다음과 같았다.
인게임 전반에 관한 네트워크 동기화와 게임 내 프로세스의 연결 - 카드 뽑기 연결 및 게임 재시작 구동 방식 구현 등이 본인이 해결해야 하는 과제였다.
게임 내 네트워크를 동기화하는 방식에는 크게 3가지가 있으며, 변수 동기화, RPC, 커스텀 프로퍼티를 이용한 동기화 등이 있다.
여기서 잦은 변동이 있는 맵 오브젝트 물리와 같은 요소에서는 변수 동기화를 적용하고, 비교적 변동 요소가 적은 맵 생성 로직 및 UI 등의 변화와 같은 요소에서는 RPC를 이용하는 방식이 적절하다고 판단함.
맵 오브젝트의 경우 오브젝트의 개수가 블럭 단위로서 단일 맵에 그 개수가 상당히 많이 배치가 되어 있는 구조이다. 따라서 네트워크 동기화를 하지 않았을 경우 컴퓨터 각자에서 물리 연산이 이루어지고, 컴퓨터의 성능, 컴퓨터가 판단한 Velocity의 차이 및 지연 등 여러 요소 등으로 인해 물리 연산의 결과가 완벽하게 일치하기가 어려워진다.
따라서 아래의 테스트 사진과 같이 똑같이 총알을 쐈음에도 오브젝트의 배치가 달라지는 경우를 볼 수 있다.
<네트워크 동기화가 되지 않은 경우>
이것이 오브젝트의 네트워크 동기화가 필요한 이유이다. 여기서 각 오브젝트가 움직이는 물리에 관한 연산은 실시간으로 이루어지며 인게임 내에서도 잦은 변동이 있는 요소이다. 따라서 해당 오브젝트들을 동기화하는 방식으로 변수 동기화 방식을 택했다.
이때, 오브젝트에는 Rigidbody가 존재하므로 그 움직임에 대한 동기화까지 하기 위해서, 오브젝트에서 동기화해야 할 변수는 총 4가지가 된다. Transform, Rotation, Rigidbody.Velocity, Rigidbody.AngularVelocity 이 네 가지 변수를 동기화하는 방식을 선택했다.
여기서, 오브젝트의 위치와 회전, 물리적 움직임에 대한 동기화에 있어 정확성을 높이기 위해서 핵심이 되는 아이디어는, 물리 변화에 대한 연산은 하나의 컴퓨터에서만 처리하도록 하는 것이 적합하다는 것이었다. 여기에서 해당 물리 변화에 대한 연산을 처리하는 컴퓨터는 포톤 네트워크 상 마스터 클라이언트가 처리하는 것이 좋을 것이다.
이에 따라 앞선 오브젝트의 Joint 작동방식에서, Joint가 파괴되고 물리의 BodyType가 전환되는 과정 자체는 마스터 클라이언트에서만 일어나게 하도록 한다. 마스터가 아닌 클라이언트의 입장에서는 아무리 총을 쏴도 그 자신이 물리 변화를 일으킬 수 없이 Kinematic 상태를 유지하게 되고, 대신 RPC를 통해 그 자신이 발사한 총알로 이 오브젝트를 쳤다는 신호만을 보내도록 한다.
이와 같이 마스터만이 물리와 관련된 연산이 가능한 상황이 되면, 해당 오브젝트의 Transform, Rotation, Rigidbody.Velocity, Rigidbody.AngularVelocity를 OnPhotonSerializeView를 통해 정보를 전달한다. 그러면 정보를 받은 컴퓨터에서는 직접적으로 물리 연산을 하는 것이 아닌 해당 오브젝트의 현재 상태를 받아와서 그대로 반영하므로, 마스터 클라이언트에서 연산한 수치 거의 그대로 오브젝트를 움직이게 된다. 다만 네트워크 상의 지연이 발생하게 되므로, 해당 지연에 대한 보상 처리까지 해야지 완벽하게 같은 화면을 연출할 수 있다.
위와 같은 방법으로 모든 오브젝트를 동기화할 수는 있겠지만, 동기화를 해야 할 오브젝트가 지나치게 많으면 네트워크적으로 지연이 발생하고 게임의 속도감이 떨어지는 등의 문제가 발생할 수 있다. 따라서, 게임적으로 동기화의 필요성이 큰 오브젝트만 동기화를 진행하고, 동기화의 중요성이 떨어지는 오브젝트는 동기화를 하지 않는 것도 방법이다.
이와 같은 이유로 동기화를 진행하지 않은 대표적인 오브젝트가 ‘로프’이다.
로프의 경우 저 설정된 링크의 개수만 25개가 되는 상당히 많은 개수의 오브젝트이며, 이를 실제 동기화 시도를 했을 때에는 시각적으로 보일 정도의 성능 저하가 발생하기도 했다. 여기서 로프 기믹의 작동방식에 있어서 로프가 꼭 동기화가 필요한 대상인지에 대한 고민이 필요하다.
실질적으로 해당 로직이 작동하는 핵심은 로프의 끝에 달린 구, 해머가 주변 지형지물을 부수는 것이 핵심이기 때문에, 해머의 동작은 네트워크 동기화가 필요한 요소가 맞다. 하지만 로프의 경우는 어디까지나 해머를 따라다니는 부차적인 시각 요소 오브젝트이기 때문에, 플레이어에 따라 로프의 움직임이이 다르게 보이는 것은 크게 문제가 되지 않는 부분이다. 따라서 로프는 동기화 대상에서 제외함으로서 과도한 동기화로 인한 성능 저하 문제를 방지하고자 했다.
맵 생성 및 이동, 그리고 게임 결과에 따른 UI 출력 등의 요소는 맵 오브젝트의 변화에 비해서는 비교적 빈번하게 발생하는 변화가 아니다. 따라서 해당 과정은 RPC를 통한 처리가 적합하다고 판단했다.
랜덤 맵 생성 로직의 경우, 동기화를 진행하지 않을 경우 각자의 컴퓨터에서 랜덤하게 맵을 생성하고 배치하기 때문에 각자 생성한 맵이 다른 경우가 발생하게 된다. 따라서 맵 생성이 가능한 사람은 마스터 클라이언트만으로 한정하고, 마스터 클라이언트가 먼저 랜덤으로 맵을 배치한 다음 RPC를 통하여 생성한 맵 정보를 전달하는 방식이 적당하다.
여기서 맵 생성에 있어서도 마스터가 아닌 클라이언트도 동일하게 Round를 기준으로 자식오브젝트로 맵을 생성하도록 하기 위해선 해당 맵 자체가 PhotonView를 가지고 있어야 한다. 또한 자신을 다른 오브젝트의 자식으로 지정할 수 있도록 호출하는 RPC를 가지고 있어야 한다.
또한 맵의 이동의 과정에 있어서는 빈번하게 발생하지는 않으나, 이동 자체가 연속적으로 일어나는 행위이다 보니 해당 동기화를 위해선 변수 동기화 방식을 사용했다. 맵의 이동 자체는 마스터 클라이언트에서만 일어나며, 마스터 클라이언트에서 맵의 Transform만 다른 클라이언트에 전달하여 해당 클라이언트가 움직임을 반영하는 식으로 적용했다.
UI의 경우, 네트워크 상의 동기화를 위해서 DOTween의 대상으로서 적용되는 모든 오브젝트에 대해서 PhotonView를 부여하고, 변화 과정을 전부 RPC로 정리하여 동작을 연동시켰다. 해당 방식으로 UI가 적절히 동작하는 것을 확인할 수 있었으며, UI 동작 로직 자체를 크게 바꾸지 않고 애니메이션 전환 과정을 단순히 RPC로 처리하는 것만으로도 네트워크 동기화가 정상적으로 동작하는 것을 확인했다.
게임의 특성상 1대1 대전 게임이며, 두 명의 플레이어가 모두 동일한 맵을 보고 있어야 하며 각자의 캐릭터가 싸우는 것이 이 게임의 메인 컨셉임. 따라서 무엇보다도 중요한 건 두 플레이어가 같은 화면을 보고 있으며 판정의 정확성이 중요시됨. 이를 위해선 네트워크 동기화의 정확성을 높이기 위한 작업에 많은 노력을 들여야 하며, 위와 같은 방식으로 최대한의 효율성을 들여 네트워크 동기화를 이뤄낼 수 있을 것이라 기대함.
상황 : 권용호님의 작업에 대한 백업으로 카드 씬에서의 애니메이션 작업의 서포트를 위해서 붙음. 여기서 캐릭터 애니메이션과 카드 플립이 같이 잘 동작하는지 확인하기 위해서 권용호님이 만든 카드 프리팹과 스크립트의 수정이 필요했음. 다만 함는지 확인하기 위해서 권용호님이 만든 카드 프리팹과 스크립트의 수정이 필요했음. 다만 충돌 방지를 위해 해당 스크립트를 복사한 다음 테스트 중이라는 네임스페이스를 씌우고 코드를 수정하면서 테스트를 진행함.
원인 : 충돌 방지를 위해 네임스페이스 구분을 통해 스크립트를 복사해 사용했으나, 스크립트 명 자체가 동일한 탓에 원본 파일도 수정되는 문제가 발생함. 이에 따라 용호님의 작업물이 전부 작동하지 않는 치명적인 충돌이 발생함.
해결 방법 : 문제를 파악하고서 테스트가 완료된 스크립트에 한해서는 전부 주석 처리를 하고, 복사한 코드를 삭제 및 용호님의 코드를 원본 상태로 복구함.
상황 : 리매치 거부 시에 로비로 이동하지 않는 현상이 확인되어 원인 분석을 하였고 그 결과 RPC로 리매치가 거부된 사실을 전달하지 않고 있다는 사실을 알아내 해당 부분에서 RPC를 호출함. 하지만 RPC 호출을 했음에도 불구하고 여전히 로비 이동이 불가능한 현상을 발견함. 이 부분에 대해 분석해 본 결과 MonoBehaviourPunCallbacks에서 OnLeftRoom의 과정 자체가 실행되지 않는다는 문제를 발견함.
원인 : 리매치를 거부한 다음 OnLeftRoom 단계에서 씬을 전환하는 과정이 들어 있었는데, 이 OnLeftRoom이 실행되기 전에 외부적으로 UI를 비활성화하는 과정이 있음을 발견함. 이로 인해 OnLeftRoom 과정이 실행되기 이전에 UI가 비활성화가 되어 씬 전환이 이루어지지 않음
해결 방법 : 리매치를 거부한 시점에서 비활성화가 되지 않는 컴포넌트에서 신호를 받고 씬을 전환하는 방식으로 변경경함.