-
총알 피하기 게임 - 소감 그리고 총정리 [항해 플러스 코육대]개발/사이드 프로젝트 2023. 10. 3. 22:51
게임 해보기 (링크)
이전 글 보러가기 (링크)
GitHub Repository (링크)
항해 플러스: 제1회 코육대 홈페이지 (링크)
드디어! 게임을 완성했다.
생각했던 것보다 프로젝트 볼륨이 컸다.
하지만 정말 재밌게 개발했다.
덕분에 추석 연휴 기간동안 심심하지 않게 지낼 수 있었다 ㅋㅋㅋ
게임 개발은 예전부터 해보고 싶다고 생각했었는데,
제대로 개발하려면 언리얼 엔진이나 유니티를 써야 하지 않을까..? 하는 막연한 생각에
진입장벽이 생겨서 도전도 못 해보고 있었다.
이번 코육대를 계기로 간단한 웹 게임을 만들어보니,
생각보다 어렵지 않구나! 하는 기분이 들어서 뿌듯하다.
느낀 점
느낀 점을 몇 가지로 정리해 보면 이 정도이다.
- 자바스크립트는 정말 대단한 언어다.
- 안 되는게 없다. 정말로.
- 개발 과정에서 느껴지는 자유도는 정말 최고다...
- 그리고 브라우저 window 객체가 제공하는 다양한 메서드들은 웹 개발을 정말 재미있게 만들어준다.
- 게임 에셋 만들어주시는 분들 사랑해요
- 덕분에 짧은 개발 기간에 좋은 퀄리티의 결과물이 나왔다.
- 이 자리를 빌어 https://opengameart.org 의 에셋 제작자 분들께 다시 한 번 감사를 표한다...
- 처음 써보는 새로운 기술로 세팅하는 것(환경설정)은 늘 어렵구나
- 백엔드 API 서버를 ASP.NET Core 7 버전으로 구현했는데, ASP.NET은 써본 적 있지만 구버전이라 최신 버전은 처음 써봤다.
- 역시나 CORS 에러, POST 엔드포인트 parameter가 안 들어오는 문제 등 몇 가지 문제가 발생했는데 레퍼런스가 별로 없어서 해결하는 데 진땀 뺐다. 공식 문서도 상당히 불친절해...
- 하지만 성장했다. 해내고 나면 재밌단 말이지...
코육대 덕분에 새로운 시도도 하고, 좋은 경험치를 쌓을 수 있었던 것 같아서 참 좋았다.
다음에도 이런 기회가 있으면 도전해 보고 싶다.
사용한 기능들과 구현 방식
이 파트를 어떻게 정리할까, 고민을 해 봤는데
그냥 구현한 기능들을 쭉- 읊으면서 중요한 것, 어려웠던 것 위주로 풀어내 보려 한다.
약간 조각 모음이 필요한 디스크 같으니, 알아서 걸러서 봐주시기를 바란다...
# 기술 스택
프론트는 Vanilla JavaScript
백엔드는 ASP.NET Core 7 을 사용했다.
(DB는 Microsoft Azure SQL을 사용했다)
바닐라 자바스크립트를 쓴 이유는.. 게임 개발이니까 최대한 언어의 날 것 그대로를 살려서 개발해보고 싶었다는 느낌? 불필요한 기능 쫙 빼고 꼭 필요한 기능만 넣어서 개발해보고 싶었다.
백엔드는 지금 다니는 회사에서 쓰는 기술 스택을 사용했다. 빨리 구현해야 하다 보니, 아무래도 익숙한 언어를 쓰자고 생각했다.
그래도 너무 쓰던 것만 쓰면 좀 그러니까, 최신 버전을 써보자 해서 최신 버전으로 올렸는데 생각보다 환경설정이 쉽지 않았다 ㅋㅋ
# 화면 렌더링
HTML canvas를 활용했다.
window.requestAnimationFrame 메서드에 콜백함수를 인자로 넣고 재귀호출하면
해당 메서드가 모니터 화면 주사율에 맞게 실행된다.
그러면 모니터 화면 주사율에 맞게 애니메이션이 업데이트된다.
*아쉬운 점: 144hz 모니터에서 이 게임을 실행하면, 강제로 고속 모드로 게임을 플레이하게 될 것이다.
*디바운싱 기법으로 60hz에 맞추는 방식을 쓰면 될텐데, 시간이 넉넉치 않아서 우선 미뤄뒀다.
개발하다보니 캔버스에 렌더링 해야 할 데이터가 많아졌는데, 이를 그때그때마다 화면에 로드하면 렉이 발생할 수 있다고 생각했다.
브라우저는 화면에 그릴 게 많아지면 급격하게 느려진다.. 렌더링이 생각보다 리소스를 많이 잡아먹는 활동이다.
그래서 가상 캔버스를 만들어서 거기에 데이터로 그려야 할 모습을 만들어두고, 마지막에 가상 캔버스에서 실제 캔버스로 옮기는 방식을 사용했다.
그렇게 구현하니 캔버스 크기 리사이징 문제도 자연스럽게 해결할 수 있어서 좋았다. (브라우저 창 크기에 맞게 알아서 게임 화면 크기도 조정될 수 있도록 구현함)
# 플레이어 이동
document.addEventListener 메서드를 통해 방향키의 keyup, keydown 이벤트를 청취했다.
키가 눌리면 해당 방향으로 이동할 값을 dx, dy에 저장해놓고,
프레임이 업데이트 될때마다 dx, dy값을 x, y (실제 위치)에 반영했다.
그 외 디테일한 구현들이 있지만.. 생략
# 총알의 생성과 이동, 소멸
Bullet이라는 클래스를 만들어서, 총알이 생성되어야 할 때마다 인스턴스로 뽑아냈다.
총알들은 배열에 저장된다.
그리고 화면의 끝에 도달했을 때 (범위 밖으로 벗어났을 때) 소멸된다.
단순하게 for 반복문을 역으로 돌면서 splice 하는 방식으로 소멸시켰다.
총알은 랜덤하게(Math.random) 사방에서 생성되는데,
생성 확률은 log함수로 일정 수치에 수렴할 수 있도록 했다.
시간이 지날수록 많이 나오게끔 구현하려다보니
단순 1차함수 비례 그래프로 구현하기에는 뭔가 가오가 살지 않았다.
그래서 log함수로.. 처음에는 적게 나오다가 갑자기 증가폭이 커졌다가
최종적으로 점점 증가하긴 하지만 한 수치로 수렴하게 되는... log함수로 구현했다.
# 쉴드 아이템 사용
총알은 또 쉴드 아이템을 사용하는 방법으로도 없앨 수 있다.
아이템을 사용하면 플레이어 기준으로 일정 거리에 있는 총알들을 없앤다.
이 역시 총알들을 순회하며 일정 거리 안에 플레이어가 있으면 splice 해버렸다.
# 총알을 맞으면 Game Over
마찬가지로 총알을 순회하며 플레이어와 충돌하는 총알이 있으면
폭발 Effect를 발생시키고 isGameOver value를 true로 바꿔준다.
# 함수 간 원시값의 공유
primitive value, 즉 원시값은 JavaScript에서 주소 복사가 안된다.
C언어였으면 포인터를 인자로 전달하면 같은 값을 공유할 수 있었을텐데,
JS에서는 원시값의 주소값에 접근할 수가 없다.
isGameOver같은 true, false 처리를 해야 하는 원시값은 전역적으로 공유가 가능해야 하는데,
이를 객체로 감싸는 방식으로 우회해서 처리했다.
const isGameOver = { value: true }와 같은 형태다.
# 쉴드, 폭발 등 Effect
쉴드를 자세히 본다면 알 수 있겠지만.. 단순히 이미지만 해당 위치에 그려주는 것이 아니라
플레이어를 따라 다녀야 하고,
2프레임마다 투명도도 바뀌는 것을 알 수 있다.
생각보다 처리해야 할 부분이 많다는 얘기다.
이 역시 따로 클래스로 빼서 인스턴스로 사용했고,
생성자에 의존성 주입을 활용하여 상황에 맞는 Effect가 발생할 수 있도록 구현했다.
(내가 구현한 게 정확히 의존성 주입이 맞는지는 잘 모르겠지만.. 일단 개념은 비슷한 것 같다)
# 보스 스테이지
이 게임에는 보스가 있다.
보스는 다루기 쉽지 않다..
하나하나 정해진 패턴대로 왔다갔다 움직이며
알맞은 타이밍에 총알을 쏠 수 있게 만들어야 했다.
따라서 패턴 데이터가 많이 필요했고...
중복을 최대한 막기 위해서 총알 패턴을 따로 만들고 재사용했다.
그리고 원래같으면 프레임마다 움직임을 그려 줘야 하는데
프레임과 프레임 사이가 예측 가능하면 (일직선으로 움직인다든지)
해당 동작의 지속 시간과 경로를 수학적으로 계산하여 움직이게끔 했다.
(그러면 내가 써야 할 코드의 양이 많이 줄어든다!)
아무튼 어떻게 하다보니 꽤 괜찮게 구현이 되어서
다음에 업데이트를 한다면 보스 스테이지를 더 다양하게 꾸며볼 예정이다.
# 대쉬
스포가 될 수도 있겠지만,
대쉬를 사용하지 않는다면 3레벨로 진입할 수가 없다.
확실히 일반 총알 피하기보다는 보스전을 위한 스킬이라고 할 수 있겠다.
대쉬는 쉬프트를 누른 상태에서 같은 방향키를 두 번 누르면 발동하는데,
이렇게 한 이유는 오작동 가능성을 줄이면서도
조작 난이도를 높이려고 한 것이다.
불행히도 구현 난이도도 함께 높아졌는데,
설명하긴 어렵지만 keyup과 keydown 이벤트 시 적절하게 눌린 키를 기억해놓는 방식으로 처리했다.
잘 처리하지 않으면 플레이어가 이상하게 움직여서 애먹었다.
# 인트로 화면과 각 메뉴들
인트로 화면 참 예쁘지 않은가?
개인적으로 만족하는 부분이다.
reddit의 한 유저가 그린 배경을 활용했다.
각 메뉴를 클릭하면 이동하는 것은
단순히 현재 위치를 상태값처럼 기억하는 방식으로 구현했다.
현재 위치가 credits이면
credits에 맞는 컴포넌트들을 그려주는 방식이다.
어차피 애니메이션 프레임이기 때문에,
update와 draw 함수에서 분기 처리 해주면 된다.
좀 급하게 구현하다보니 하드코딩한 면이 있는데,
더 깔끔하게 발전시키고 싶은 아쉬운 코드 중 하나다.
# 리더보드
백엔드 API 서버를 활용했다.
게임이 끝나면 이름을 입력받고,
이름과 점수를 서버로 post 요청 보낸다.
그러면 DB에 저장되고,
이를 리더보드에서 불러오는 방식이다.
리더보드는 Skip() Take() 메서드를 통해 특정 부분만 그때그때 가져올 수 있도록 구현했다.
정리해놓고 보니 별 거 없어 보이네...
아무튼 이번 연휴 보람찼다!
이 게임을 발전시키든지, 다른 게임을 개발하든지 해서
앞으로도 게임 개발은 계속 해보고 싶다.
정말 재미있는 경험이었다. 끝!
'개발 > 사이드 프로젝트' 카테고리의 다른 글
총알 피하기 게임 [항해 플러스 코육대] (2) 2023.09.28 - 자바스크립트는 정말 대단한 언어다.