Unity

[Unity] Project_ShootingRun

송만덕 2021. 11. 1. 03:38

개요

드래곤플라이트의 슈팅 템플런의 러닝, 그리고 궁수의 전설과 같은 무한 스테이지 방식을 채용한 러닝슈팅게임.

총 4명으로 구성된 팀으로 게임 기획 및 쉐이더 담당, UI 담당자, 맵 디자인 담당자, 기능 구현 담당자가

있었으며 그 중 기능 구현을 담당하여 대부분의 스크립트를 작성하였다.

게임 엔진은 유니티 엔진을 사용하였으며 언어는 C#언어로 제작되었다.

 

https://youtu.be/vJiEvaxrG8U


주요 기능 설명

01
로그인 화면

우선 서버는 뒤끝이라는 서버 관리 시스템을 사용했는데, 뒤끝의 기능인 게스트 계정을 사용해 기기 내부에 게스트 계정 정보를 저장하는 방식으로 로그인을 구현하였다.


로그인 버튼을 누르면 우선 닉네임의 여부에 따라 신규회원과 기존회원을 판별하게 되고
신규 회원의 경우엔 닉네임을 입력함과 동시에 서버에 존재하는 유저 정보 테이블을 초기화 해주게 된다.


게임에서 서버 정보를 얻는 방식은 로그인을 하게되면 데이터매니저라는 스크립트를 통해 서버에서 각종 테이블을 읽어옴으로써 데이터를 불러오는 방식으로 구현하였다.


01
플레이어 상태정보

게임을 실행하게 되면 맨 처음 보이는 메인메뉴이다.

 

상단엔 뒤끝 서버를 이용하여 저장된 자신의 계정 정보와 획득한 코인의 갯수를 나타내고

우측엔 장착한 무기와 게임시작버튼이 있다.

 

하단엔 메뉴를 선택하는 탭이 있는데, 탭을 선택하거나 화면을 스와이프 하면 탭을 이동할 수 있다.

스와이프 UI는 슬라이더를 통해 하단의 탭을 클릭하거나 조건에 맞는 화면을 슬라이드 하게되면 탭이 넘어가게 되며 하단 UI의 판넬 크기와 위치 같은 부분을 조정함으로써 구현하였다.


012345
상점 구매기능

상점과 장비 시스템 또한 서버를 통해서 구현하였다.

 

우선 뒤끝에서 제공하는 차트 기능을 활용하여 상점 아이템 품목에 대한 차트를 생산하였고, 인게임에서 아이템을 선택하고 구매버튼을 눌렀을 때 구매한 아이템의 가격과 소유한 코인을 비교하게 되고


조건이 만족한다면 구매한 아이템의 ID를 유저의 인벤토리 테이블에 전달하여 인벤토리 목록에서 아이템을 활성화하고, 상점에서의 해당 아이템을 비활성화 하는 방식으로 구현하였다.

 


0123
아이템 장비기능

구매한 아이템을 선택하면, 선택한 아이템에 따라 선택 이미지가 보이게 되고

 

그 상태에서 장착 버튼을 누르면 선택한 장비를 장착하게 된다.

 

이 또한 어떠한 무기를 장착하였는지, 또 무기마다 어떤 총알 스킨을 장착하였는지 아이템에 대한 장착정보를 유저 정보 테이블에 저장하게 되고 서버에 저장하게되며

위의 메인메뉴의 캐릭터 상태와 장비 상태가 변하고, 스테이지 씬에 진입하게 됐을 때 유저 정보 테이블을 읽어 장착한 아이템에 대한 오브젝트를 활성화 하는 방식으로 구현하였습니다.


01
랭킹과 기록

플레이 한 기록에 따라 최고 기록을 서버에 저장하고, 그 서버에서 점수가 높은 순으로 랭킹을 보여주는 기능이다.

1~3등까지의 아이콘을 달리하고, 가장 아래엔 자신의 순위가 나타난다.


01234567

게임 시작 버튼을 누르면 스테이지 씬으로 넘어가며 (낮은 버전의 캡처가 포함되어 있어 쉐이더 문제로 총알이 허공에 있는 사진이 있다.)

 

스테이지 씬은 URP를 활용하여 서브웨이서퍼와 같은 curved 맵을 구현하는 Animal crossing이라는 셰이더 강의를 참고하였고, 강의에서 제공하는 셰이더 그래프를 생성한 뒤 맵과 몬스터 등의 Mesh Vertex의 Y좌표에 변환을 줌으로써 커브효과를 주어 진행 방향이 언덕처럼 휘어있는듯 보이게끔 만들었다.

 

기본 동작으로는 캐릭터는 자동으로 z축으로 이동하며 LateUpdate를 통해 카메라가 캐릭터를 따라가는 구조이고, 공격은 각 총기의 공격속도에 따라 자동으로 공격하며 메인 메뉴에서 장비한 아이템들을 캐릭터가 장착하게 되며, 발사하는 총알 또한 적용한 스킨에 따라 다르게 발사된다.

 

플레이어 동작으로는 좌측과 우측으로 스와이프하여 이동하거나, 위나 아래로 스와이프 하여 점프하거나 슬라이딩을 할 수 있다.

 

스와이프 감지로는 플레이어 컨트롤러라는 스크립트에서 gettouch를 사용하여 터치를 감지하였고, 스와이프 거리를 계산하여 조건의 거리 이상 이동하게 되었으면 그에 따른 캐릭터 조작을 수행하는 방식으로 구현하였다.
X좌표 이동의 경우는 Mathf.Lerp를 이용하여 시작점과 도착점 간의 거리를 percent라는 것을 통하여 보간하며 이동하는 식으로 구현하였고
점프와 같은 경우엔 정해진 점프 시간만큼 점프를 진행하게 되고, 시간에 따라 중력과 점프 진행도를 계산하여 y좌표를 변환하는 방법으로 구현되었습니다.

 

스테이지 씬의 기본적인 UI로는 최상단엔 닉네임과 체력 그 아래엔 킬과 점수에 따라 획득하는 코인과 점수, 그리고 몬스터 킬 수

우측 상단엔 스테이지와 업그레이드 횟수가 표시된다.

 

스테이지는 크게 두가지로 몬스터가 나오는 구간과 장애물이 나오는 구간이 있는데, 몬스터는 스테이지가 올라갈수록 더욱 강해진다.

플레이어의 공격은 몬스터에게 충돌판정이 있으며, 몬스터가 플레이어의 총알에 맞게되면 HUD Text를 통해서 받은 데미지를 화면에 표시하게 되는 방식이고

플레이어의 진행방향에 생성되는 대부분의 오브젝트는 플레이어와 충돌하면 플레이어에게 데미지를 입히게 되며, 몬스터와 장애물 모두 종류에 따른 각각의 공격 방식을 지니고있다.

 

또 장애물은 총 세개로 첫번째로 나무가 쓰러지는 장애물,

다음으로 토네이도는 스테이지의 진행도에 따라 나타나게 되는 오브젝트로 랜덤한 라인의 끝에서 현재 플레이어의 위치로 날아 올 것이라는 진행방향을 알려주는 indicator를 생성한 뒤 일정 시간이 지나면 경로의 방향대로 진행하며 충돌시 데미지를 주게되는 오브젝트이다.

 

마지막으로 낙석은 몬스터 스폰과 동일하게 각 세 라인에 따라 일정 확률로 나타나게 되고 빠르게 날아오며 충돌 시 데이지가 강한 오브젝트입니다.

 

무한 스테이지로 구성되어있기 때문에 캐릭터가 사망하거나 유저가 포기하여야 끝이 나며 (우측 상단의 정지 버튼을 통함)

스테이지가 종료되면 마지막 사진과 같이 여러 정보가 담긴 결과창이 출력된다.


업그레이드

앞서 설명한 업그레이드 기능이다.

스테이지를 클리어 할 때마다 업그레이드를 1회씩 할 수 있는데, 랜덤으로 세 종류가 나타나게 된다.

나타나는 업그레이드 버튼은 중복감지를 통해서 중복되게 나타나지 않으며 그 중에서도 성능이 좋다고 생각되는 것들은 조건에 따라만 등장하게 된다.

총 6종류 정도로 구성되어 있고, 최대체력이 증가하는 기능은 최대체력이 증가함과 동시에 증가된 양을 회복시켜준다.

 

구현은 한 스테이지를 클리어 하게되면 teimscale을 0으로 함으로써 잠시 게임을 멈춘 뒤 업그레이드 창을 띄워주고 random 함수를 통해

 

랜덤한 업그레이드 버튼이 출력되게 하였으며, 원하는 업그레이드 버튼을 클릭하게 되면 업그레이드 창이 닫히며 timescale을 되돌리고 다음 스테이지가 진행되는 방식으로 구현하였다.


01234
몬스터 종류

스테이지에 등장하는 몬스터들이다.

우섯 첫번째는 일정 시간마다 돌진하여 공격하는 늑대

두번째는 숨어있다가 플레이어가 근접하여야 나타나는 두더지

세번째는 일정 시간마다 투사체를 발사하는 꽃

네번째는 플레이어가 근접하면 Slerp를 통해 한 칸 만큼 날아가 찌르기 공격을 하는 벌 몬스터가 있다.

마지막으로는 보스 스테이지 구현을 위해 만들어둔 보스 몬스터이다.

 

무한 스테이지로 중간에 프로젝트 컨셉이 변경되어 사용하지 못했지만

다양한 투사체를 발사하거나 땅에서 가시가 솟는 파티클을 통하여 공격을 하거나 Coroutine을 통한 시간차 공격을 하는 등의 공격 방식을 지녔으며

각 공격을 할 때마다 앞서 설명한 토네이도와 같은 방식의 인디케이터를 생성해 공격 범위를 보여주는 등 다양한 기능이 존재하는 보스 몬스터이다.

 

 


첫 번째 피드백 반영

 

어느정도 완성 단계에 돌입하여 테스트를 진행해 본 결과

다음과 같은 피드백 + 여러가지 버그에 대한 피드백이 왔고, 이를 개선하기 위해 추가적인 개발을 진행하였다.

 

1. 남자 캐릭터 구현

012

우선 미구현으로 되어있던 남자캐릭터 선택 기능을 추가하였다.

상점에서 남자캐릭터를 구매하게 되면 잠겨있던 남자 캐릭터가 언락되게 되며,

메인 화면에서 위와 같이 선택된 상태로 게임시작 버튼을 누르게 되면 남자캐릭터로 진행하게 된다.

플레이어 정보와 같은 것들은 오브젝트 Tag를 Player로 하여 검색하기 때문에 어떤 캐릭터를 선택하든 동일하게 작동하게 된다.

또 피드백 중 남자캐릭터 선택 이후 화살표 버튼이 기능하지 않는다는 피드백이 있었는데, 찾아보니 단순히 버튼에 스크립트가 적용이 안되어있었던 사소한 문제였기 때문에 간단히 해결할 수 있었다.

 

2. 스테이지 결과창 버그

012

우리의 게임은 여러가지 씬 내에 데이터를 불러오고 저장하기 위해

첫 번째 사진과 같은 DataManager라는 오브젝트를 사용하는데, 두 번째 사진과 같이 다양한 데이터를 담고있다.

결과창의 HighestScore나 Stage 등 다양한 내용 또한 포함하고 있는데,

이 버그의 문제점은 스테이지를 하나 클리어 한 후 업그레이드를 진행하였을 때 DataManager 오브젝트가 사라진다는 점이었다.

이를 해결하기 위해 Stage Clear에 대한 스크립트를 확인하였고, DataManager가 사라지는 원인을 찾아내어 더이상 사라지지 않게 하였더니 결과창이 정상적으로 출력되었다.

 

3. 난이도 관련 피드백

 

기존엔 업그레이드 되는 수치만큼 몬스터의 체력만 증가하게끔 하였는데,

업그레이드 수치가 이것저것 쌓이다 보면 게임의 난이도가 너무나 쉬워진다는 피드백이 들어왔다.

이를 해결하기 위해 스테이지에 따른 몬스터의 체력 증가 수치와 기본 체력을 증가시켰고, (두더지 몬스터는 유독 존재 이유를 모르겠다는 피드백이 있어 앞길을 막는 역할을 할 수 있게 체력 통을 늘렸다.)

장애물 오브젝트의 경우 스테이지에 따라 충돌시 데미지를 상향시켜 더욱 강하게 만들었다.

(변수에 따라 간략하게 할 수 있었는데 피드백 바로 다음 날이 캡스톤디자인 전시 날이라 급하게 만들었다.)

 

4. 외의 변경사항

 

위에서 언급된 변경사항 이외에도 여러가지 수정 사항이 있었다.

대부분 QA를 하는 도중 발견 된 자잘한 버그들로, 소스 한 줄이나 수치를 변경함으로써 간단히 해결할 수 있었던 문제라 따로 자세히 다루진 않았다.

 

 


 

후기

어찌보면 본격적인 첫 유니티 프로젝트였기 때문에, 시작부터 잘 하지는 못하였고

또 배우는 단계에서 작성한 소스들도 있기 때문에 지저분한 부분이 많았던 점이 너무 아쉬웠다.

물론 그로 인해서 성장한 것을 느낄 수 있었고 유니티 마스터라고 할 순 없지만 어느정도 기본 기능을 사용할 수 있게 만들어준 작품인 것 같다.

또 구현 중 가장 힘들었던 것은 상점과 장비기능이라고 생각된다.

아무래도 서버 구축 자체는 나의 담당업무가 아니었기 때문에 뒤끝 사용을 위한 소스코드를 거의 모르는 상태로 며칠간 서버 스크립트와 뒤끝 문서를 읽어야 했으며, 컨텐츠 적용을 위해 조원분이 작성하신 UI 스크립트마저 전부 읽어야 했기 때문에 머리가 너무 아팠다.

또한 스크립트가 어떻게 동작하는지 이해하여도 내가 필요한 기능을 구현하기 위해선 이것저것 찾아봐야 하는 기능들이 많았고, 앞서 말했듯 UI와 서버 등 다양한게 얽혀있었기 때문에 버그가 한번 생기면 잡기가 상당히 까다로웠다.

하지만 이렇게 스크립트를 전부 읽어버린 까닭에 프로젝트가 동작하는 흐름을 전부 알 수 있었고 그 덕분에 최근에 한 대부분의 구현 업무를 팀원의 도움 없이도 어느정도는 혼자 처리할 수 있었다.

 

아직 미구현된 기능도 많고 이것저것 아쉬운 작품이지만 나름 이런저런 기능 구현에 많은 도움이 되었고

QA를 위해서 수백번의 스테이지를 플레이 하며 애정도 생긴 소중한 첫 작품이다.

다음 Unity를 다룰 일이 생긴다면 캐주얼 RPG를 다뤄보고 싶은 생각이며, 다음엔 여러가지 디자인 패턴과 최적화 기법을 적용하고 여러가지 셰이더와 물리법칙을 통해 다양한 재미를 주는 게임을 만들고 싶다.

 

추가

 

01

캡스톤 디자인 발표 이전에 쓰여진 글인데, 발표 날 상을 받게 되었다!

개인적인 사정으로 발표에 참석하진 못하였지만, 최우수상을 수상하게 되었다고 한다.