전체화면 게임 링크 (소리 재생 있음, 키보드 & 마우스)
개요
게임명 : CS099 Final Project
게임 장르 : 2.5D 아케이드 슈팅게임
과목명 / 교수 : CS099 / Rudy Castan
언어 / 프레임워크 / 개발도구 : Javascript / p5.js / Sublime Text
소스 컨트롤 솔루션 / 클라이언트 : Bitbucket / Sourcetree
팀 규모 / 개발 기간 : 1인 / 2주
이번에 소개할 프로젝트는 제가 대학교에 다니면서 개발한 첫 개인 게임 프로젝트입니다.
컴퓨터 사이언스 과목을 담당하시던 Rudy Castan 교수님이 주신 1학년 1학기의 마지막 과제로써 자바스크립트와 p5.js 라이브러리를 활용하여 자신이 원하는 게임을 직접 만들어보도록 하는 과제가 나왔고, 저는 이 과제를 위해 어떤 게임을 만들지 고민하다 당시에 관심이 많던 3D 프로젝션을 2D 공간에서 시뮬레이션하는 코드를 직접 연구하고 작성하여, 하나의 2.5D 아케이드 슈팅 게임을 혼자서 만들게 되었습니다.
앞으로 개발할 2D 게임들에서 두고두고 쓰일 2D 벡터 클래스를 직접 만들고, 각종 멤버 함수들을 추가한 다음에 이를 활용하여 시각적 디버깅을 하도록 하는 과제를 수행하던 중, 2D 벡터 객체 여러개를 활용하여 원근감을 조성하는 방법을 알아낸 후, '이거 잘만 만지면 고정된 시점의 입체 슈팅게임 정도는 만들 수 있겠는데?' 라고 생각하게 되어, 먼저 5일간 컨셉 프루프용 프로토타입을 만들어본 후, 당시의 자신의 실력과 지식 수준으로 실현이 가능하다고 판단하여 7일동안 컨텐츠를 채워넣은 후, 마지막 2일 동안의 폴리싱을 통해 2주 내로 게임을 완성했습니다.
하이 컨셉트
"적들의 화면상에서의 위치와 거리를 동시에 신경써야 하는 우주에서의 입체 슈팅게임"
제가 발견한 원근감을 조성하는 방법은 여러 벡터를 하나의 소실점으로 모이도록 하는 방법이였는데, 이 효과를 게임플레이 내내 유지하기 위해서는 시선상의 조준선의 존재가 꼭 필요했습니다. 플레이어가 적에게 총알과 같은 투사체를 쏘게 할까도 생각해보았지만, 만약 가이드라인을 유지한 채로 총알만 사격한다고 하면 조준선의 각도가 바뀔 때마다 거기에 맞춰서 총알을 이동시키기 위해 총알의 궤적을 계속해서 계산해주는 클래스를 동적으로 생성하여 관리해야 했는데, 이를 기한 내에 혼자서 개발해내기에는 시간이 부족할 거라는 생각이 들었습니다. 그러다, '사격 시의 조준선을 고정시킨 다음, 이 조준선을 그대로 레이저의 경로로 활용하면 어떨까?' 라는 생각으로부터 가이드라인을 따라 진행하는 레이저를 공격수단으로 하는 포탑의 운용을 생각해내게 되었고, 레이저와 포탑의 조합으로부터 자연스레 우주를 떠올리게 되었습니다.
모두 우주를 배경으로 하는 작품들인 건담이나 스타워즈의 배경 연출이 그렇듯이 단순한 점과 원, 그리고 선들만으로도 우주에서의 공격과 폭발, 그리고 별 등을 전부 구현 가능한 만큼 웹이라는 저사양의 플랫폼에서도 잘 돌아갈 수 있을 거라고 생각했고, 기왕 원근감 구현에 성공한 만큼 멀리 있는 적, 그리고 가까이 있는 적들이 모두 존재하도록 한 다음에 적들의 X, Y 좌표뿐만 아니라 화면상에서의 거리까지 신경쓰게 하면 재밌겠다고 생각하여, 이러한 주제들에 초점을 맞춘 간단한 슈팅게임의 개발을 결정하게 되었습니다.
게임 플로우
이 게임에는 스플래시 스크린도, 크레딧 씬도 없고, 오직 G를 누르라고만 하는 터미널 하나만 덩그러니 있는 메뉴가 있습니다. 그 외에는 메인 게임 화면과 스코어 표시 씬이 전부인, 정말로 간결한 게임 플로우를 가지고 있습니다.
이렇게까지 구조를 간결하게 한 이유들 중에는 물론 시간 문제도 있었지만, 이 게임은 모든 씬에서의 배경이 완전한 검은색이기에 어느 한 시각적 요소의 크기만 잘 조절하면 우주 공간 어딘가에서 자연스럽게 나타나거나 사라지는 것과 같은 효과를 줄 수 있고, 이를 활용해 메인 게임 루프의 모든 시각적 요소들이 한 점에서 뻗어나와서 한 점으로 빨려들어가도록 구현한 만큼, 하나의 검은 화면 위에서의 씬 전환의 일체감을 게임의 특징으로 삼고 싶었습니다.
게임 목표 & 보상
무한히 생성되는 적을 상대로 살아남는다는 단순하고 검증된 주제를 목표로 삼았습니다.
보스, 혹은 제대로 된 순차적 웨이브 시스템을 구현하기에는 시간이 부족하였기에 엘리트 몹이 중간중간 섞여나오는 무한 디펜스 시스템을 구현하기로 결정하였고, 시스템은 플레이어가 적을 처치할 때마다 해당 적의 강력함에 따라서 플레이어에게 점수를 지급합니다. 플레이어는 게임이 끝날 때까지 자신의 점수를 볼 수 없습니다. (osu!에서 영감을 받은 시스템)
게임을 진행하다 보면 점점 더 강력한 적들이 필드에 축적되게 되고, 적들로부터 날아오는 투사체에 결국 플레이어는 언젠간 게임 오버를 맞이하게 되어 있지만, 적들을 처치하며 얻은 점수를 마지막에 화면을 가득 채워 보여줌으로써 짧고 굵게 플레이어의 노력에 대한 보상을 줍니다.
메인 게임 메카닉
적들의 위치뿐만 아니라 화면상의 거리까지 고려해야 한다는 컨셉을 구현하기 위해서 영점거리 개념의 도입과 함께 조준선의 줌 인 / 줌 아웃 기능을 구현하여 영점거리를 먼저 정확히 맞춘 후, 적의 예상 위치에 사격해야 적에게 데미지를 입힐 수 있도록 코드를 작성했습니다.
현재 위치가 아닌 예상 위치인 이유는, 우선 적이 단순한 고정 표적이 아닌 제대로 된 하나의 AI라는 느낌을 주고 싶었기에 주기적인 공격과 함께 계속해서 이동하면서 이동방향을 바꾸도록 해두었고, 거기에 더해 레이저가 일정 속도를 두고 진행하도록 한 다음, 발사 후 탄착까지 시간이 걸리도록 하여 공격이 빗나갈 확률을 높이고 공격 명중 시의 쾌감을 증대시키기 위함입니다.
현실의 저격수들끼리의 싸움과 같이, 서로 멀리 떨어진 채로 행하는 전투의 경우에는 적의 거리를 먼저 아는 것이 가장 중요합니다. 이 게임에서는 먼저 조준선 끝을 적에게 가까이 함으로써 적을 색적하여 거리와 진행방향을 알아내고, 적의 거리에 맞춰서 영점거리를 조정한 다음에 화면에 표시되는 예상 위치에 조준 후 발사한다는 순서로 플레이어의 행동을 구성했습니다.
다소 복잡한 절차로 되어있고, 제대로 된 튜토리얼 없이는 무엇을 해야 할지 깨닫기가 힘들 수 있지만, 그만큼 플레이어가 취해야 하는 행동들이 최대한 직관적으로 나타나도록 UI 인터페이스 및 사운드 피드백에 신경을 많이 썼습니다. 적을 색적할 시의 레티클의 깜빡이는 인디케이션이나, 적에 대한 락온이 유지되고 있을 동안의 소리 인디케이션, 그리고 조준선이 고정될 시 조준선의 배치 변경, 적 피격 시의 화려한 피드백 등을 예시로 들 수 있습니다.
영점거리 및 예상 위치 표시 시스템은 워 썬더라는 이름의 공중전 시뮬레이터 게임으로부터 영감을 받았고, 해당 게임에서의 완성된 시스템을 경험해본 덕에 이 게임에서도 해당 기능들을 구현하기가 수월했습니다.
적 AI
적들은 이론상으로는 우주선이지만, 화면상에서는 그저 락온이 되는 하얀 점들일 뿐입니다.
스폰되는 위치, 거리, 이동속도는 모두 랜덤으로 지정되어 생성되고, 각각 다른 종류의 투사체를 쏘는 세 종류의 적들이 있습니다.
기본 적 1 : 주황 투사체 : 느리지만 높은 데미지.
기본 적 2 : 파랑 투사체 : 빠르지만 낮은 데미지.
엘리트 적 : 3점사 파랑 투사체 : 매우 빠르지만 낮은 데미지.
게임 디자인 이론을 다룬 한 책 (A Game Design Vocabulary - Anna Anthrophy) 에서 배운 내용을 참고하여 AI가 지닌 서로 같은 파라미터들 (데미지, 속도, 점사) 에 대한 설정값들 사이의 큰 차이를 둠으로써, 게임플레이의 불확실성을 간단하게 증가시켰습니다.
이미 조준 시스템이 충분히 불편한 만큼 처음에 정해진 거리에서 더 가까워지거나, 혹은 멀어지거나 하는 패턴은 없지만, 주기적으로 플레이어에게 공격을 하고, 등속 운동을 하다가 중간중간 감속하며 방향을 전환하며 플레이어의 신경을 긁도록 하였습니다.
방향 전환에 대해 추가적인 설명을 하자면, 이 게임은 웹 게임인 만큼 스탠드얼론 게임과는 달리 마우스 범위가 화면 밖으로 이탈할 수 있다는 치명적인 기술적 한계를 안고 있습니다. 이 때문에 마우스에 조준선이 반응하는 정도를 매우 높게 조정하고, 800x600 정도의 화면으로도 게임을 문제없이 플레이할 수 있도록 적이 움직일 수 있는 공간에도 제약을 두었습니다. 이를 위해 모든 적들이 중 화면 경계를 넘어가기 전에 자동적으로 반대방향으로 가속도가 가해지도록 구현하였으며, 이 때문에 적들은 빠른 속도로 직진하다가도 급격히 감속하여 중간중간 자연스럽게 곡선을 그리며 움직이게 됩니다.
예상 위치를 시각적으로 알려줌에도 불구하고 처치가 까다로운 속도가 빠른 적의 경우에는 적이 방향을 전환할 때에 느려지는 타이밍을 노려 처치를 시도해 볼 수 있고, 플레이어의 스킬 레벨에 따른 직접적인 피드백을 극대화시키기 위해 오차로 값을 나누는 방식의 데미지 계산 공식을 활용하였기에 잠깐이라도 정확히 적의 중간을 플레이어의 빔이 긁는 경우에는 엄청난 양의 데미지가 한번에 들어가며 적이 순식간에 폭발하게 됩니다.
this.life -= -(this.depth / 2) * damage / (zeroDelta.x + zeroDelta.y + depthDelta);
// spaceship.js, line 267
아트 디자인
우주를 배경으로 하는 게임인 만큼 배경을 검은색으로 하고 나서 그 위에 각양각색의 별들을 추가하였습니다. 당연히 별의 크기에도 원근감을 적용시켰고, 위치 분포에 랜덤 함수를 활용하여 가까이서 크게 보이는 별과 멀리서 작게 보이는 별들이 화면에 골고루 분포하도록 하였습니다. 색깔에 한해서는, 랜덤 함수를 활용하면 화면이 너무 어지러워 보이거나, 혹은 운 나쁘게 색이 너무 집중되거나 할 가능성이 있다고 판단, 모든 별들에게 각자의 화면으로부터의 거리와 위치에 의존하여 색깔이 지정되도록 하여 색깔이 불필요하게 겹치는 일이 없도록 하였습니다.
fill(255 * this.starList[i][j][3]/10, 255 * this.starList[i][j][4]/10, 55)
// space.js, line 59
게임의 컨셉에 맞도록 그림 대신 모든 시각적 정보들을 p5.js의 드로잉 함수들만을 활용해 전달하려고 하였고, 간단한 조작법을 표시해주는 터미널처럼 생긴 작은 윈도우를, 언더테일과 같은 게임들에서 쓰이는 순차적 텍스트 디스플레이 기법을 활용하도록 하여 만들어 넣었습니다.
또, 웹 게임인 만큼 다양한 창 크기로 게임을 즐기는 사람이 있을 거라 판단, 700 x 700 이상의 어떠한 창 크기가 주어지더라도 제대로 게임이 플레이될 수 있도록 코드 구조를 구성했습니다. (700 x 700 이하의 창에서 실행될 경우에는 마우스를 움직일 수 있는 범위가 적의 이동범위를 쫓아가지 못할 수 있고, 터미널이 제대로 표시되지 않을 수 있습니다)
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
windowOffset.x = windowWidth / 2;
windowOffset.y = windowHeight / 2;
fill(16)
rect(
windowOffset.x + worldVector.x,
windowOffset.y + worldVector.y,
windowWidth * 3 * playerFCS.zeroDistanceMultiplier,
windowHeight * 3 * playerFCS.zeroDistanceMultiplier);
noFill();
}
// misc.js, line 10
적이 파괴될 시에는 화려한 폭발 이펙트와 함께 적이 사라진 자리에 깜빡이는 인디케이터가 남아 적이 파괴된 위치를 알려줍니다. 또, 파괴된 적으로부터의 거리, 그리고 파괴된 적의 종류에 따라 충격파가 도달하여 카메라 셰이크가 일어날때까지의 시간과 강도에 차이가 있도록 구현하였습니다. 물론, 폭발 이펙트의 크기 또한 거리에 의존하도록 설정하였습니다.
오디오 디자인
FL Studio 라는 이름의 작곡 소프트웨어를 활용하여 음악과 모든 SFX를 직접 만들었습니다. 단순한 도형들이 큰 비율을 차지하는 게임의 분위기와 잘 들어맞도록 레트로 신스와 8비트 드럼을 활용하였고, 우주의 느낌을 살리기 위하여 웅장하고 어두운 분위기의 앰비언트 FX를 오디오 트랙에 깔아두었습니다. (레트로 신스의 파형 디자인에는 Serum을 활용)
드럼 파트에 대해 조금 더 설명을 하자면, 게임의 메인 트랙을 위해서 드럼 파트 또한 직접 구성하였는데, 처음에는 드럼 파트의 해상도가 너무 높아 레트로 신스와의 궁합이 그다지 좋지 않았습니다. 허나 아웃풋을 가상앰프의 인풋으로 리다이렉트한 후, 이를 통해 다시 밖으로 연결함으로써 소리의 해상도를 괜찮은 느낌으로 낮출 수 있었습니다.
개선점들
터미널을 통한 더 나은 스토리텔링
제대로 된 튜토리얼 / 인게임 도움말
적의 중앙을 맞출 시의 추가 점수 및 이펙트 (단간론파 V3의 V논파 시스템)
적의 상태에 따른 적 도트의 색상 변화 (적의 행동, 체력 등)
더 강한 카메라 셰이크와 적 폭발 이펙트 (이브 온라인의 함선 파괴 시의 섬광)
메인 메카닉을 더 잘 활용하도록 강제하는 보스
스크립트 기반 순차적 페이즈 시스템
플레이어 피격 시의 더 직관적인 시각적 피드백
실행되는 창 크기에 따라 적 이동범위 조정
분명 시간이 부족해서 구현하지 못한 점들도 많지만, 제출 이후에야 생각난 점들도 있습니다. 지속적으로 유지보수를 해야 하는 게임이 아닌 데드라인이 있는 과제였던 만큼 파이널 버전을 더 수정할 당위성을 느끼지 못하였고, 이러한 개선점들은 후에 만들 게임에 참고해볼 예정입니다.
소스 코드
소스 코드는 개발자 도구 (f12) 를 활용하면 볼 수 있습니다! 수정 및 디버깅도 가능합니다 :D
작성자명, 프로젝트명, 과목명, 그리고 학기 및 년도 전부를 헤더 커멘트로써 남기는 것이 언제나 Grading Rubric으로써 강제되어 왔기에, 디지펜 커리큘럼의 모든 공식적인 소스 파일에서는 저러한 헤더 커멘트를 볼 수 있습니다.
개발 환경
당시에는 운영체제로 Windows 10을 사용중이였고, Sublime Text 에디터의 Browser Sync 기능을 활용하여 소스 코드가 곧바로 브라우저에서 돌아가도록 설정한 후, 개발자 도구를 활용하여 게임을 계속해서 디버깅해가며 개발을 진행했습니다.
개발과정 내내 Bitbucket과 Sourcetree를 활용하여 소스 파일들을 관리하였고, bitbucket.io의 무료 호스팅 서비스를 활용하여 게임을 인터넷상에 게시하였습니다. 서포트 페이지 링크
코스 사이트에 제출된 아카이브용 문서
게임의 코드를 설명한 문서.
수업에서 가르친 여러 Programming Concepts들에 대한 개인적인 이해와 코드 스니펫들.
개발 후기
초등학생 시절부터 여러 장르의 게임들을 개발해왔고, 자바스크립트가 생애 첫 프로그래밍 언어였던 만큼 (유니티가 아직 자바스크립트를 지원하던 시절에 입문) 완성도 높은 아케이드 슈팅게임을 만들어내기에 충분한 개발경험, 그리고 언어에 대한 익숙함 덕에 어려운 컨셉의 게임을 제한된 시간 내로 잘 개발해낼 수 있었다고 생각합니다.
하지만 아직 대학교에 들어온지 1학기밖에 되지 않아 제대로 된 심화 컴퓨터 사이언스 주제에 대한 이해도가 부족했고, 또 C++의 코어 가이드라인이나 프로그래밍 프랙티스에 대해 배우기 전이였던 만큼 코드 퀄리티가 다소 떨어지는 점이 있었습니다.
하지만, 이러한 부족한 점들은 1학년 2학기에 수강한 Kevin Wright 교수님의 CS100, 그리고 Rudy Castan 교수님의 CS120에서 컴퓨터 사이언스 주제들과 함께 C++ 프로그래밍 언어의 베이스가 되는 요소들부터 제대로 짚고 넘어감으로써 지금은 충분히 개선되었다고 자신합니다.
이번 프로젝트에서는 제대로 된 게임 디자인 다큐먼트를 작성하지 않고 제한된 시간 내에 결과물을 내기 위해 곧바로 개발에 뛰어들게 되었는데, 이는 상황 변화에 따른 유연한 대처, 그리고 시간 관리와 스코핑을 단련할 수 있었던 좋은 훈련이 되었다고 생각합니다.
게임 디자이너를 목표로 하는 이상, 회사에 들어가고 나서부터는 풀타임으로 프로그래밍을 하지는 않겠지만, 그래도 프로그래밍이나 컴퓨터 사이언스에 대한 충분한 이해도가 동반된 채로 기획안을 내는 디자이너가 있다고 한다면 프로그래머들이 디자이너를 더 믿고, 효율적으로 의사소통을 하며 각자의 일에 더 제대로 집중할 수 있을 것이라 생각하기에 계속해서 컴퓨터 사이언스를 공부해나갈 생각입니다.
Comments