ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 총알 피하기 게임 [항해 플러스 코육대]
    개발/사이드 프로젝트 2023. 9. 28. 17:17

    SAILOR - MR - MOON

    게임 해보기 (링크)

    GitHub Repository (링크)

    항해 플러스: 제1회 코육대 홈페이지 (링크)

    소감 및 기능 구현 설명 보기 (링크)


    추석이 다가왔다.

    이번 추석 명절은 길다.

    열심히 일하던 회사를 떠나 6일간의 자유를 얻은 것이다.

    하지만 나는 개발자... 회사가 주는 일이 없어져 버린다면 머리 한 구석이 허전하다.

    idle 프로세스가 자꾸 심심하다고... 일 좀 주라고......

     

    마침 재밌어보이는 코딩 대회가 열린다는 소식을 접했다.

    이름하야 코육대.. 코딩 육상 대회다.

    아육대를 예전엔 참 즐겨봤었는데 요즘엔 하는지 안 하는지도 잘 모르겠다.

    암튼 코딩을 육상 달리기 하듯 하는 대회라니 얼마나 설레는가?

    더군다나 상품으로 27인치 DELL UHD 모니터가 주어진다고 하니, 피가 끓어오른다...

    내 27인치 LG QHD 모니터에게 친구를 선물해주면 참 좋아할 것 같다.

    물론 둘이 제조사는 다르지만..... 오히려 좋다.

    제조사가 달라야 유전적으로 건강하지 않을까? ㅋㅋㅋ

     

    신청해놓고 며칠이 지났을까

    추석 연휴 첫날이 되고 오후 2시에 문제가 공개되었다.

    나는 가장 재밌어보이는 총알 피하기 게임을 만들어보기로 결심했다.

     

    기술 스택은 자유라고 한다.

    나는 상남자니까 바닐라 자바스크립트로 웹게임을 만들어야지.

    게임의 생명은 성능.. 쓸데없는 것은 다 떼어내고 경량화해야

    손맛이 좋은 겜이 나올 것이다.

    근데 바닐라 자바스크립트는 왜 바닐라일까?

    밍밍하고 베이직한 맛이 자바스크립트 본연의 그것과 잘 맞아떨어져서 그렇게 이름 붙여진 것일까

    하지만 나는 초코맛을 더 좋아한다.

    나중에 JS 커스텀을 하게 된다면 초코 자바스크립트로 명명해야겠다.

     

    재밌는 게임 만들기.....

    요구사항은 다음과 같다...

     

    요구사항에 맞추어 대략적인 기획을 해 보았다.

    Essentials

    • 선박
      • 상하좌우 이동
      • 대각선까지
      • 누른 시간 비례 가속도?
    • 총알
      • 생성
        • 인스턴스 생성 방식
        • 사방에서 생성
      • 이동
      • 소멸
    • 선박과 총알의 충돌 처리
    • 생존 시간 기록

    Additional Features…

    • 리플레이
    • 다양한 이동 방식
      • Shift + 방향키를 하면 해당 방향으로 대쉬
      • Z X C 에 스킬 넣기
    • 스테이지
      • 단계별 난이도 조정
      • 보스전 (패턴)
    • BGM & SFX
      • 심플한 BGM 무한재생 가능하도록
      • 대쉬기와 아이템에 SFX

     

    그리고 우리 ChatGPT(이하 `똑똑이`)에게 코드를 짜 달라고 의뢰했다.

    역시 똑똑하다.. 바로 실행해 봤는데 잘 작동한다.

    이제는 이렇게 똑똑이에게 맡길 수 있는 부분들은 다 맡기고

    개발자는 세부적인 디테일을 잡거나

    어려운 기능을 만들어내는 것에 집중하는 role을 수행하는 것이 맞다고 생각한다....

     

    똑똑이가 짜 준 코드에는 몇 가지 문제가 있었다.

    1. 선박 위치가 화면 밖을 벗어날 수 있음.
    2. 총알이 사라지고 나서 (splice이후) 해당 인덱스를 참조하려고 하기 때문에 잘못된 충돌 계산을 하거나.. 심할 경우 없는 index 참조 오류가 나서 게임이 멈춤.
    3. 게임이 종료되고 나서도 계속 requestAnimationFrame이 호출되기 때문에 종료코드가 제대로 실행되지 못하고 alert가 무한히 호출됨.

    일단 이런 버그성 동작들을 먼저 고쳤다.. 똑똑이의 헛똑똑이 모먼트.

    이런 인간미까지 있으니 똑똑이를 사랑하지 않을 수가 없다.

     

    그 다음엔 원하는 대로 구현되지 않은 부분들을 수정해 보았다.

    1. 총알은 위에서 아래로만 떨어지고.. 선박은 좌우로만 움직임. (예전에 플래시게임으로 있던 똥피하기 게임이 연상되었다....)
    2. 시간이 안 나온다.
    3. 선박이랑 총알 크기가 좀 크다. 줄이고 싶다.
    4. 대쉬기를 쓸 수 있게 하고 싶다.

    1번 2번 3번은 무난하게 구현할 수 있었다... 하지만 4번은 조금 쉽지 않았다...

    나는 쉬프트를 누른 상태에서 방향키를 더블탭 해야 대쉬가 나가게 하고 싶었다.

    똑똑이가 내 설명을 제대로 알아먹지를 못해서 (내가 설명이 부족한 탓이겠지...) 직접 구현했다.

     

    대쉬 기능 구현하기

     

    쉬프트가 눌려있음을 표시하는 shiftPressed 변수와,

    쉬프트와 함께 눌렀던 키를 기억해두는 pressedKeyWithShift 변수를 만들었다.

    그리고 대쉬를 무한히 쓸 수 있으면 안되므로 dashCooldown 변수도 만들었다.

     

    그 다음 EventListener에 조건을 추가해 주었다.

    • shift keydown시 shiftPressed를 true로 바꾼다.
    • shift keyup시 shiftPressed를 false로 바꾼다.
    • shiftPressed인 상태에서 방향키가 keyup되면 해당 키를 pressedKeyWithShift에 기억해둔다.
    • 여전히 shiftPressed 상태에서 저장해둔 키와 같은 키가 keydown되면 대쉬가 나가고 쿨이 돌기 시작한다.

    자잘하게 신경써줘야 하는 부분들은 플레이 테스트 해 보면서 수정했다.

     

    잘 된다.

    (gif가 왜캐 슬로모션으로 나오지.. 대충 웹사이트에서 변환했더니 그런 것 같다)

     

    구현하고 보니 shiftPressed를 쓰지 않아도 작동하더라...

    쉬프트를 누른채로 keydown 이나 keyup 이벤트가 발생하면 e.shiftKey가 true로 들어온다.

    고것을 이용하여 구현하니 굳이 shiftPressed 변수가 필요 없었다.

    과감히 지워주었다 ^-^

     

    여기까지 만들고 보니 git을 안 만들었다는 사실이 떠올랐다.

    다 날려먹기 전에 얼른 git init하고 커밋했다.

     

    버그를 또 발견했다.....

    왼쪽을 누르다가.. 왼쪽을 떼지 않고 오른쪽을 눌렀다 떼면 선박이 멈춰버린다.

    선박의 이동 방식과 keyup keydown 이벤트 발생 시점에 문제가 있었다...

     

    선박의 이동 방식은 다음과 같다.

    아.. 이동 방식을 설명하려니까 전반적인 구동 방식을 먼저 설명해야겠다는 생각이 든다....

    중구난방으로 글을 쓰다가 이제서야 기능 설명을 하는군... ㅋㅋㅋㅋ

     

    이 게임은 기본적으로 HTML canvas와 window객체의 requestAnimationFrame을 사용해서

    1초에 60번.. canvas에 그림을 그려주는 방식이다.

    requestAnimationFrame 함수에 업데이트 함수를 파라미터로 넘겨주면

    화면 리프레시 주기에 맞춰서 업데이트 함수가 실행된다.

    1프레임, 즉 1/60초 마다 상태가 업데이트 되는 것이다.

    function update() {
        
        // ... other codes ...
        
        requestAnimationFrame(update);
    }

    그러니까 요런식으로 재귀적으로 requestAnimationFrame이 실행될 수 있게끔 해주면

    무한으로 애니메이션을 즐길 수가 있다... ㅋㅋㅋ

     

    근데 키 입력은 1프레임마다 받는 것이 아니라,

    자바스크립트의 EventListener에서 keyup과 keydown 이벤트를 실시간으로 listen한다..

    그러면 내가 누르고 있는 키를 인식해서

    다음 프레임에 어느 방향으로 이동할지를 정해야 한다...

    그것을 player 객체의 dx와 dy에 저장하여

    프레임 업데이트 때 x += dx; y += dy 와 같이 translate 해주는 것이다.

     

    문제는 여기서 발생한다

    우리들의 운영체제는 너무나 똑똑해서

    키보드를 살짝만 눌러도 무한히 입력되는 것을 방지하기 위해

    키 입력에 딜레이를 준다.

    일정시간 누르고 있어야만 반복적으로 입력이 되는 것이다ㅏㅏㅏㅏㅏㅏㅏ 이렇게.

    하지만 이러면 구현에 따라 특정 상황에서 키 입력이 씹혀버리는.. 문제가 발생하는 것이다.


    오케이.. 그러면 조금 지저분한 구현이 필요하다...

    좀 지저분하면 어떤가? 원래 이런 세세한 디테일은 지저분한 법.....ㅋㅋㅋ

    백조는 물 속에서 다리를 바쁘게 움직이고 있기에 우아한 것이다.

    (하지만 이것은 사실 거짓말이다.. 백조나 오리는 물 위에 뜨기 위해 그렇게 많은 노력을 하지 않는다고 한다 ㅋㅋㅋ)

    아무튼 기존에 눌려있는 키를 기억해두고,

    keyup 이벤트 발생 시에 여전히 반대 키가 눌려있다면 해당 이동값을 dx,dy에 제공하는 방법으로 해결했다.

     

    자... 근데 여기서 코드가 점점 커지는 느낌이 들었고

    일단 지금 타이밍에 모듈화를 해야 겠다는 생각이 들었다.

    그래서 Player class와 Bullet class를 만들어서 인스턴스를 따올 수 있도록 분리 작업을 했다.

    이 작업을 제때 하지 않으면.. 코드가 명란 바질 파스타가 되는 건 시간 문제니까 ㅋㅋㅋ

     

    클래스 분리 같은 모듈화 작업을 똑똑이에게 시키면 되지 않느냐고?

    아니다.... 똑똑이는 훌륭한 조수의 역할을 수행할 뿐...

    머지않은 미래에는 똑똑이 혼자서 코드를 다 짤지도 모르지만

    지금은 프로젝트 사이즈가 조금만 커져도 중요한 부분을 놓치기 일쑤다.

    오류가 발생하는 지점을 지적하면 잘못했다고 용서를 구하기 바쁜 녀석이다 ㅋㅋㅋ

     

    그리고 나도 이런 리팩토링을 수행하면서 비로소 코드를 상세하게 살펴보게 되고

    우리 똑똑이가 짜준 코드를 100% 이해하게 되는 것이다...

     

    똑똑이를 쓰는 것은 책을 읽는 것과 비슷하다

    선조들의 지혜가 책에 담겨 있듯이...

    똑똑이의 어깨 위에 올라서서 더 높은 코드를 바라볼 줄 알아야 한다..

     

    어쨌든 이제 기본적인 구현 요구사항은 다 만족했고

    아이템을 만들고 스테이지별 난이도 조정을 하려고 한다.

    이제는 모듈화가 되어 있기 때문에 비교적 수월하게 기능을 붙일 수 있을 것이다.

     


    아.. 개발하다보니 글쓰는 것이 힘들어져 버렸다.

    원래는 개발하면서 중간중간 쓰려고 했는데, 아무래도 왔다갔다 하면 집중이 잘 안된다...

    집중력을 잃지 않으면서 포스팅 하시는 분들은 정말 존경스럽다.

     

    아무튼 글도 길어진 김에, 소회 겸 총정리는 다음 포스팅에 이어서 해야겠다.

    댓글

Copyright 2022. ProdYou All rights reserved.