1편이 ‘왜 만들었는가’에 대한 이야기였다면,
2편은 ‘어떻게 만들었는가’ 에 대한 이야기다.

개인적으로 웹 애플리케이션 제작시 나는 Django, PostgreSQL, Redis, Docker 같은 백엔드 기반 시스템을 다루는 것을 훨씬 편하게 느끼는 사람이다.
그런데 이번 프로젝트는 거의 전부 순수 바닐라 JS + HTML5 Canvas 기반으로 진행됐다.
평소라면 절대 깊게 다루지 않았을 법한 프론트엔드 기술들이 이 게임에서는 핵심이 되었다.

그리고 놀랍게도…
엄청 재미있었다.
이것이 나를 더욱 몰입하게 만든 기술적 요인이었다.

평소에는 프론트엔드 쪽 업무를 "좋아하지 않는다"라는 표현보다 "싫어한다"라는 표현이 적절할 정도인데, 豆柴の大群 에 대한 사랑의 힘이 "싫어하는 일"도 좋아하게 만든 것이 아닐까 싶다.

캐릭터 선택 화면 스크린샷


1. HTML5 Canvas – “그림판 + 애니메이션” 같은 세계



MAME RUN!!의 핵심은 HTML5 Canvas다.
이는 웹페이지 위에 만들어진 ‘그림판’ 같은 영역인데, 여기에 직접 픽셀을 그리는 방식으로 게임을 만든다.

보통 웹 개발은

  • 버튼 만들고(HTML)

  • 색 입히고(CSS)

  • 약간의 행동을 준다(JS)

이런 흐름이지만 Canvas는 완전히 다르다.

Canvas는 30~60fps로 계속 다시 그리는 구조다.
매 프레임마다:

  1. 배경을 지우고

  2. 다시 그리고

  3. 캐릭터를 그리고

  4. 장애물을 그리고

  5. 충돌을 계산하고

  6. 점수를 올리고

  7. 다음 프레임을 예약한다

이걸 매 초 수십 번 반복한다.

요즘 웹 브라우저는 게임 엔진 못지않게 정말 강력 하다고 느껴진다.


2. 점프와 중력: “단 3줄의 코드가 만들어내는 마법”

게임의 기본인 ‘점프’는 사실 단순 물리로 구현되었다.

dino.vy += gravity * dt;  // 중력 적용
dino.y  += dino.vy * dt;  // 위치 변경

if (dino.y >= ground) {
    dino.y = ground;
    dino.vy = 0;
}

이 단순한 로직만으로도
레오나가 부드럽게 뛰고 착지하고 떨어지는 동작이 자연스럽게 만들어진다.

게임 엔진이 없는 상황에서,
“아… 이런 기본 물리만으로도 충분히 쫀쫀한 움직임이 나오는구나”
라는 작은 감동이 있었다.

레오나가 점프하는 플레이


3. 스프라이트 애니메이션 – 레오나가 달리는 순간 생명이 붙었다



레오나의 러닝 모션은 다음 이미지처럼 6 프레임짜리 스프라이트 시트다.

(※ 블로그에 스프라이트 이미지 삽입 추천)

JS에서는 스프라이트를 잘라서 프레임마다 그리면 된다:

const frameWidth = sprite.width / 6;
ctx.drawImage(
  sprite,
  frameIndex * frameWidth, 0, frameWidth, sprite.height,
  dino.x, dino.y, dino.width, dino.height
);

프레임 인덱스만 계속 바꾸면
레오나가 귀엽게 달린다.
이 순간 게임에 ‘생명’이 들어간 느낌이 들었다.

게임 개발에서 애니메이션은 정말 중요하다.


4. 장애물 랜덤 생성 – "단조로움을 없애는 핵심"

장애물은 여러 종류가 있다.

  • 주황색 콘

  • 빨간 콘

  • 노란 콘

  • 오토바이 (2가지 색)

  • 자동차 (2가지 종류)

  • 표지판 (세가지)

그냥 1~2개만 반복되면 금방 질리는데,
여러 타입을 랜덤 간격으로 섞어주면 게임 흐름이 확 살아난다.

const key = OBSTACLE_KEYS[Math.random() * length];
const interval = 0.8 + Math.random() * 1.2;

아주 단순한 랜덤이지만
플레이 감각은 완전히 달라진다.


5. 충돌 판정 – 가장 어려우면서도 가장 재미있던 부분

충돌 판정은 흔히 “AABB 박스 충돌”이라고 부르는 방식이다.

if (dino.x < ob.x + ob.width &&
    dino.x + dino.width > ob.x &&
    dino.y < ob.y + ob.height &&
    dino.y + dino.height > ob.y) {
    // 충돌!
}

여기서 중요한 건

  • 너무 빡빡하게 판정하면 게임이 짜증나고

  • 너무 널널하면 게임 난이도가 떨어진다

그래서 레오나와 장애물 둘 다 히트박스를 실제보다 10~15% 축소해서 “기분 좋은 난이도”를 만들었다.

이 미세 조정이 꽤 재밌었다.
게임 개발자들이 왜 난이도 밸런스에 혼을 담는지 조금 알 것 같았다.


6. 거리 기반 스테이지 시스템 – 50초면 클리어

Stage 1은 약 50초에 클리어되도록 설계했다.
그래서 레오나는 초당 16m를 이동하는 것으로 가정했다.

목표 거리: 800m  
속도: 16 m/s → 약 50초  

이걸 숫자로만 정하는 게 아니라, 실제로 30~40번 플레이하면서
“이 정도면 한 판 즐기고 다시 하고 싶은 길이인가?”
를 계속 시험했다.

작지만 플레이 감각을 좌우하는 중요한 요소였다.


7. 서버 연동 – 점수 랭킹과 기록 저장

백엔드 기반 개발자인 만큼, 서버 파트는 자연스럽게 Django + DRF로 구성했다.

  • POST로 점수 제출

  • Serializer에서 유효성 검사

  • 비로그인 유저는 guest_id로 기록

  • 유저별 최고점은 UserScore 테이블에서 관리

  • TOP 5는 HTML 조각으로 반환해 JS가 교체

그래서 게임오버 시 바로 랭킹 TOP5가 실시간 갱신된다.

재밌는 건, Canvas 게임 안에서도
백엔드의 안정성과 구조화된 데이터 흐름이 엄청 큰 힘이 된다는 사실이었다.

백엔드에서 체계적으로 캐릭터의 이미지과 대사 데이터를 정리해두고 이를 조합해서 브라우저로 보내주는 방식이다.


8. 모바일 대응 – 예상보다 훨씬 어려웠다

웹 게임을 만드는 데 있어서 가장 예상 밖의 난관은 모바일 환경이었다.

  • 해상도 대응

  • 터치 동작

  • 캔버스 비율

  • 렌더링 성능

  • 배경 스크롤 속도 보정

데스크탑에서는 매우 부드럽게 돌아가지만
모바일에서는 자잘한 프레임 드랍 · 터치 반응성 · canvas 크기 계산 같은 문제들이 있었다.

결국 해결했지만,
이 과정에서 “프론트엔드 개발자들 존경합니다…” 라는 말이 절로 나왔다.


9. 2편 – 기술편을 마치며

백엔드 쪽을 좋아해서 주로 API를 위주로 개발하는 내가 디자인이 메인인 게임을 JS로 웹으로 만드는 일은 처음이었다. 평소에도 프론트는 해야하니까 어쩔 수 없이 하는 정도였고, 특히 HTML5 Canvas를 이렇게 깊게 써본 적은 거의 없었다.

그런데 이번 프로젝트를 통해,

  • 웹은 생각보다 강력하고

  • 지저분하다고 한 때 싫어했던 자바스크립트는 나름 재미있는 언어이며

  • 게임 개발은 ‘작은 기술들의 조합’이라는 것

을 배울 수 있었다.

팬심 하나로 시작한 프로젝트였지만,
개발자로서도 꽤 많은 배움과 성장을 얻은 작업이었다.


다음 편 예고 (3편: 스토리 편)

다음 글에서는
“왜 시부야에서 시작하는가?”, “왜 도쿄돔인가?”, “각 멤버별 캐릭터 설정은 어떻게 정했는가?”
같은 세계관/스토리 제작 비하인드를 정리해볼 생각이다.