← 블로그로 돌아가기

Orbit Hop 개발 일지: 탭 한 번, 언제나 공정하게

· 7분 분량 · Orbit Hop

Orbit Hop(오빗 홉)은 PC와 모바일 브라우저에서 즐기는 버튼 하나짜리 아케이드 게임이에요. 우주선은 알아서 행성 둘레를 돌아요. 탭하면 궤도에서 풀려나 일직선으로 날아가요. 그 길이 다음 행성의 궤도 링을 지나면 우주선이 붙잡히고, 아니면 우주 미아가 돼요. 이렇게 섹터 3개를 올라가며 섹터마다 꼭대기의 등대에 불을 밝히는 게임이에요.

이 글에서는 이 게임 뒤에 있는 결정들을 이야기해요. 왜 버튼이 하나인지, 자동 생성한 레벨을 어떻게 공정하게 유지했는지, 이미지 파일도 오디오 파일도 없이 그래픽과 음악을 어떻게 만들었는지, 그리고 모든 자동 테스트가 놓친 버그까지요. 스크린샷은 전부 실제 게임 빌드에서 찍었어요.

태블릿으로 본 Orbit Hop 섹터 2: 점수 180 아래 금색 “COMBO x5”, 가운데의 줄무늬 분홍 행성, 왼쪽 위의 분화구 있는 주황 행성, 초록 행성을 지나 위로 날아가는 우주선, 오른쪽 가장자리의 진행 트랙

태블릿에서 ×5 콤보를 이어 가는 섹터 2. 오른쪽 트랙은 섹터를 얼마나 올라왔는지 보여 줘요.

일부러 버튼 하나

제대로 다듬을 수 있을 만큼 작은 게임을 만들고 싶었어요. 메커니즘 하나, 입력 하나면 물리 엔진도, 길 찾기도, 레벨 에디터도 필요 없어요. 그래서 ‘일단 돌아가는’ 수준에서 멈추지 않고, 시간을 전부 도약의 손맛을 다듬는 데 쓸 수 있었어요.

입력이 하나라서 어디서나 똑같이 플레이돼요. 탭, 클릭, 스페이스 키가 전부 같은 일을 해요. 게임 화면은 휴대폰을 세워서 든 것처럼 세로로 길어서 한 손으로도 할 수 있고요. 가상 조이스틱도, 따로 배울 것도 없어요. 타이틀 화면에서는 게임이 혼자 궤도를 돌고, 풀려나고, 붙잡히는 걸 반복해요. 글을 읽기 전에 이미 뭘 해야 하는지 알게 돼요.

도약 하나하나가 작은 루프예요. 궤도를 돌고, 다음 링이 어디 있는지 읽고, 탭해서 풀려나요. 짧은 점선 조준선이 우주선이 어느 쪽을 보는지 알려 주지만 퍼즐을 대신 풀어 주진 않아요. 붙잡힐 때마다 카메라가 부드럽게 따라 올라가서, 보이지 않는 것을 겨냥하는 일은 없어요.

실패도 빨라요. 놓치면 파티클이 터지고 “LOST IN SPACE” 메시지가 뜨고, 1초도 안 돼서 섹터가 다시 시작돼요. “다시 하시겠습니까?” 버튼은 없어요. 한 번에 몇 초 안 걸리는 게임에서는 다음 시도까지 탭 한 번만 더 거쳐도 그만둘 이유가 되거든요.

큰 프레임워크도 쓰지 않았어요. 게임은 평범한 TypeScript와 브라우저 캔버스로 돌아가고, 완성본은 약 70KB짜리 스크립트 하나예요.

시드로 만든 레벨, 봇이 검증

레벨은 손으로 그리지 않아요. 섹터마다 시드라는 숫자로 생성하는데, 같은 시드는 언제나 같은 배치를 만들어요. 섹터 1은 가만히 있는 행성 사이를 8번 도약해요. 섹터 2는 흔들리는 행성과 소행성 길이 있는 10번 도약이에요. 섹터 3은 더 작은 링과 더 많은 소행성이 있는 12번 도약이고요. 배치가 절대 안 바뀌니까 재시도는 같은 퍼즐을 다시 푸는 거예요. 성공하면 운이 좋았던 게 아니라 실력이 늘었다는 기분이 들어요.

자동 생성 레벨에는 큰 위험이 하나 있어요. 시드 하나가 클리어할 수 없는 섹터나, 타이밍이 말도 안 되게 빡빡한 섹터를 만들 수 있다는 거예요. 그래서 게임의 규칙을 화면도 스피커도 모르는 코드로 따로 뒀어요. 덕분에 스크립트가 화면 없이 진짜 게임 로직을 돌릴 수 있어요. 봇은 궤도의 모든 순간에 한 번씩 이탈해 보고, 어느 순간에 이탈해야 붙잡히는지 기록해요. 어떤 도약이든 성공 구간이 8프레임(약 130밀리초)보다 짧으면 검사는 실패예요.

PC 브라우저의 섹터 3: 더 작아진 링, 보라색 행성에서 위로 날아가는 우주선, 주변의 소행성 여럿, 12번 중 9번째 도약에서 점수 95

섹터 3은 링이 눈에 띄게 작고 소행성이 많아요. 그래도 봇은 모든 도약이 가능하다는 걸 증명해요.

봇은 뭘 고쳐야 하는지도 알려 줬어요. 섹터 1은 튜토리얼치고 너무 빡빡해서 우주선을 늦췄어요. 섹터 3은 너무 심술궂어서 링을 조금 키우고 흔들림을 늦췄고요. 별가루 중에는 링에 너무 가까워서 궤도만 돌아도 저절로 먹게 되는 것들이 있어서, 지금은 간격을 두고 놓아요. 솔직한 아쉬움도 하나 있어요. 섹터 3에는 서로 도는 행성을 넣으려고 했는데, 공정하게 맞추기 더 쉬운 작은 링과 빠른 흔들림으로 바꿨어요.

코드로 그린 그래픽, 쌓여 가는 사운드트랙

Orbit Hop에는 이미지 파일이 하나도 없어요. 행성, 링, 별, 소행성, 우주선까지 전부 게임이 실행되는 동안 코드로 그려요. 그래서 용량이 작고 어떤 화면에서도 선명해요. 분위기는 네온 벡터 우주예요. 가스 띠, 분화구, 대륙이 있는 빛나는 행성, 점선 청록색 링, 깊이감을 위해 별보다 천천히 흘러가는 성운이 있어요. 외부에서 가져온 건 제목과 숫자에 쓰는, 자유 라이선스의 무료 서체 Orbitron뿐이에요.

그래픽 작업 효과를 보여 주는 가장 쉬운 방법은 두 빌드에서 같은 순간을 비교하는 거예요. 섹터 2, 네 번째 도약 직후, 모든 탭 타이밍이 똑같아요.

휴대폰에서 날아가는 중인 프로토타입 섹터 2: 매끈한 주황, 분홍, 초록 행성, 밋밋한 파란 궤적, 회색 “+10”, 점수 45

전: 프로토타입. 행성도 비행 경로도 같고 점수는 45.

같은 순간의 프로덕션 빌드: 줄무늬 주황 행성, 분화구 있는 초록 행성, 분화구 있는 소행성, 우주선 뒤의 금색 궤적, 금색 “+30"과 “COMBO x3”, 점수 84, 오른쪽 진행 트랙, 아래쪽 “COMBO!” 팁

후: 같은 순간인데 이제 84점. 금색 궤적과 “COMBO x3"은 도약이 충분히 빨라서 연속 기록이 쌓였다는 뜻이에요.

여기서 가장 큰 변화는 그래픽이 아니라 콤보예요. 링을 한 바퀴 다 돌기 전에 다시 도약하면 연속 기록이 늘고, 점수 배수는 최대 ×5까지 올라가요. 금색 호는 한 바퀴가 얼마나 남았는지 보여 줘요. 신중한 플레이어는 행성 하나씩 천천히 건너도 되고, 점수를 노리는 플레이어에게는 모험할 이유가 생겨요. 소행성을 건드리지 않고 아슬아슬하게 스치면 보너스 점수와 짧은 슬로 모션이 붙어요.

사운드도 그래픽과 같은 방식이에요. 브라우저의 오디오 시스템이 실시간으로 만들어요. 음악은 차분한 4코드 루프예요. 플레이를 시작하면 아르페지오가 합류하고, 콤보가 ×3에 닿으면 부드러운 킥 드럼과 하이햇이 서서히 들어와요. 잘할수록 음악이 쌓이는 거예요. 붙잡히는 소리는 도약할 때마다 한 음씩 올라가요. 충돌 연출에는 제일 공을 들였어요. 가장 자주 듣게 될 소리거든요. 우주선이 파편으로 부서지고, 화면이 붉게 번쩍이고, 게임이 80밀리초 동안 멈춰요. 그 멈춤은 거의 티가 안 나지만 충돌에 묵직함을 더해 줘요. 화면 흔들림과 진동은 설정에서 끌 수 있어요.

브라우저 셋, 점수는 같다

테스트를 위해 진짜 브라우저에서 게임을 열고 사람처럼 플레이하는 봇을 만들었어요. 모든 화면, 모든 메뉴, 세 섹터를 처음부터 끝까지 플레이하고, 플레이 도중에 화면을 돌리고 브라우저 탭도 바꿔요. Chrome, Edge, Firefox에서 PC, 휴대폰, 가로 휴대폰, 태블릿 크기로, 웹 주소로 열었을 때와 디스크에서 바로 열었을 때 모두 돌렸어요. 최종 빌드는 90개 검사를 전부 통과해요.

PC 브라우저의 승리 화면: 금색 “ALL BEACONS LIT”, 빛나는 등대, 섹터 1~3의 점수 251, 494, 577, 총점 1322, “best combo x5”, PLAY AGAIN과 MENU 버튼

승리 화면에 찍힌 봇의 점수: 251, 494, 577. 어느 브라우저에서든, 몇 번을 해도 정확히 이 숫자가 나왔어요.

저희가 제일 좋아하는 결과예요. Firefox는 Chrome이나 Edge와 완전히 다른 엔진을 쓰는데도 점수가 한 점도 틀리지 않고 맞았어요. 규칙과 그리기를 분리하고 시드를 고정한 덕분이에요. 게임은 어디서 돌려도 똑같이 흘러가요.

그래도 테스트에서 진짜 문제들이 나왔어요. 오래된 Safari와 Firefox에서는 버튼을 그리다가 게임이 멈춰서, 빠져 있는 그리기 기능을 대신하는 코드를 넣었어요. 흐릿한 성운은 성능이 낮은 기기에서 느려서, 지금은 절반 크기로 그려서 늘려요. 아무도 눈치채지 못해요. 그리고 가로로 돌린 휴대폰에서는 이제 세우라고 알려 줘요.

가로로 든 휴대폰의 Orbit Hop: 가운데 행성과 우주선이 있는 세로로 긴 게임 화면, 왼쪽 어두운 막대에는 기울어진 휴대폰 윤곽 아래 “Turn your phone upright for a bigger view”

가로로 해도 잘 돼요. 이제 빈 공간이 더 크게 보는 방법을 알려 줘요.

로봇이 못 본 버그

봇은 모든 휴대폰 크기에서 모든 사운드 검사를 통과했어요. 그런데 진짜 휴대폰에서는 게임이 무음이었을 거예요.

휴대폰은 화면을 터치하기 전에는 페이지가 소리를 내지 못하게 막고, 손가락을 뗄 때, 즉 탭이 끝날 때만 허락으로 쳐요. Orbit Hop은 탭이 시작될 때 소리를 요청했어요. 휴대폰은 거절했고, 게임은 다시 묻지 않았어요. 봇의 가짜 탭은 곧바로 허락으로 인정돼서 이걸 잡을 수 없었어요. 코드를 브라우저가 따르는 규칙과 대조해 읽다가 찾았고, 고치는 건 간단했어요. 이제 소리가 실제로 켜질 때까지 탭, 클릭, 키 입력마다 다시 요청해요.

다음에는 이렇게 할 거예요

규칙과 그리기를 분리한 게 이 프로젝트 최고의 결정이었어요. 레벨 봇, 공정한 타이밍, 똑같은 점수가 전부 거기서 나왔고, 처음에 조금만 원칙을 지키면 되는 일이었어요.

바꾸고 싶은 건 진짜 휴대폰 테스트를 더 일찍 하는 거예요. PC 브라우저는 휴대폰 흉내를 아주 잘 내지만 완벽하진 않고, 가장 심각했던 버그가 바로 그 틈에 숨어 있었어요. 자동 테스트는 저희가 미리 생각해 둔 질문에만 답해 줘요. 코드는 여전히 누군가 읽어야 해요.

Orbit Hop은 최신 브라우저라면 어디서든, 설치 없이 돌아가요. 와서 등대 세 개를 모두 밝혀 보세요. 봇의 1,322점을 넘기면 꼭 알려 주세요.