게임을 불러오는 중입니다스크립트를 켜면 게임이 나옵니다
구멍 게임
홀 키우기는 바닥의 구멍을 끌고 다니며 쌓인 물건을 떨어뜨리는 3D 게임입니다. 구멍보다 작은 것은 빠지고, 삼킬수록 구멍이 커져서 아까는 못 넣던 것이 들어갑니다.
모바일 게임 광고를 보다 보면 바닥에 난 구멍이 돌아다니며 자동차와 건물을 집어삼키는 장면이 자주 나옵니다. 2018년 Voodoo가 낸 Hole.io가 이 갈래의 대표 게임입니다. 이 규칙만 가져와 혼자 하는 시간 도전으로 만들었습니다. 다른 플레이어와 겨루지 않고, 정해진 시간 안에 구멍을 얼마나 키우는지를 봅니다.
하는 법
구멍을 잡고 끌어당기면 당긴 쪽으로 움직이고, 구멍에서 멀리 당길수록 빠릅니다. 손가락이나 커서를 구멍 위에 두면 멈춥니다. 마우스와 터치가 같은 방식이고, 키보드에서는 방향키나 WASD를 씁니다.
물건이 구멍 속 깊이 떨어지면 경험치가 들어옵니다. 경험치는 구멍 테두리의 노란 고리로 차오르고, 한 바퀴를 채우면 레벨이 오르면서 구멍이 커집니다. 처음 주어지는 시간은 2분입니다.
지도는 동심원 모양의 구역 10곳으로 나뉩니다. 10레벨마다 바깥 구역의 문이 열리고 시간이 1분 늘어나서, 문을 모두 열면 한 판이 최대 11분이 됩니다. 열린 구역은 경계가 사라져 안쪽 구역과 자유롭게 오갈 수 있습니다. 아직 잠긴 구역 경계에는 보이지 않는 벽이 있어서, 구멍도 무너진 물건도 넘어가지 못합니다.
100레벨에 닿으면 완주입니다. 완주 기록은 그 순간 남은 시간이고, 많이 남길수록 좋은 기록입니다. 완주하지 못한 판은 얻은 경험치로 기록합니다.
자세한 설명구역 · 요령 · 만드는 과정
구역
| 구역 | 이름 | 바닥 | 열리는 때 |
|---|---|---|---|
| 1 | 광장 | 보도블록 | 처음부터 |
| 2 | 방 | 카펫 | 10레벨 |
| 3 | 주차장 | 주차 칸 | 20레벨 |
| 4 | 공원 | 잔디 | 30레벨 |
| 5 | 동네 | 벽돌 길 | 40레벨 |
| 6 | 도심 | 화강암 판 | 50레벨 |
| 7 | 거인 식탁 | 식탁보 | 60레벨 |
| 8 | 거인 방 | 나무 마루 | 70레벨 |
| 9 | 거인 도로 | 차도 | 80레벨 |
| 10 | 마천루 | 도시 지도 | 90레벨 |
바깥 구역일수록 물건이 큽니다. 도심부터는 같은 모델을 몇 배로 키운 물건이 섞이고, 거인 식탁에는 구멍만 한 도넛과 햄버거가 쌓여 있습니다. 물건은 3,800개쯤 놓이고, PC와 휴대폰의 배치가 같습니다.
요령
탑은 밑동부터 노립니다. 맨 아래 물건이 빠지면 위에 얹힌 것들이 무너지면서 한꺼번에 구멍으로 쏟아집니다.
구멍 테두리에 걸친 큰 물건은 무게중심이 구멍 안으로 들어와야 빠집니다. 빠르게 지나가면 걸친 채 버티므로, 큰 물건 위에서는 구멍 한가운데를 맞춰 잠깐 멈춥니다. 그래도 안 빠지면 아직 구멍이 작은 것입니다.
문이 열린 뒤에는 바깥으로 넘어가는 편이 빠릅니다. 레벨 비용은 새 구역 물건의 경험치에 맞춰 오르기 때문에, 안쪽 구역에 남은 작은 물건으로는 고리가 잘 차지 않습니다.
만드는 과정
구멍을 뚫는 방법
화면은 three.js로 그리고, 물리는 Rapier로 계산합니다. 앞서 만든 3D 루빅스 큐브 게임은 3D 엔진 없이 직접 원근 투영으로 그렸지만, 이번에는 물건 수천 개와 그림자, 카툰 음영이 필요해 three.js를 썼습니다. Rapier는 Rust로 만든 물리 엔진을 웹어셈블리로 옮긴 것이라 브라우저에서도 빠르게 돕니다.
가장 먼저 정한 것은 구멍을 물리 엔진 안에서 어떻게 나타낼지였습니다. 바닥은 평평하게 두고 구멍 위에 온 물건만 바닥과 부딪히지 않게 거르는 방법이 가장 간단하지만, 물건이 빠진다와 안 빠진다 둘 중 하나로만 정해집니다. 테두리에 걸려 기우뚱하다가 미끄러져 들어가는 모습이 이 장르의 손맛인데, 그게 나오지 않습니다. 구멍 뚫린 바닥을 움직이는 물체로 끌고 다니는 방법은 엔진이 바닥에 속도가 있다고 보고 위에 놓인 물건까지 마찰로 끌고 갑니다.
그래서 구멍 뚫린 판과 안쪽 벽을 삼각형 그물(트라이메시) 하나로 만들어 고정된 물체에 달고, 구멍이 움직인 스텝마다 판을 새 자리로 순간 이동시켰습니다. 고정된 물체는 속도가 0이라 물건을 끌고 가지 않고, 테두리가 진짜 모서리라 걸침과 미끄러짐이 저절로 나옵니다. 화면의 테두리와 물리 테두리를 같은 정점으로 만들어서, 보이는 것과 부딪히는 것이 어긋나지 않습니다.
안쪽 벽은 윗부분만 부딪힙니다. 구멍 반지름의 1.5배(최소 2 m)보다 깊이 내려간 물건은 충돌을 끄고 떨어뜨리고, 반지름의 4배(최소 5 m) 깊이에서 경험치로 바꿉니다. 내려가는 동안 색을 점점 어둡게 칠해서 구멍 속으로 사라지는 것처럼 보입니다.
중력은 지구보다 센 14 m/s²에서 시작해, 구멍이 커질수록 최대 8배까지 세집니다. 구멍이 커지면 카메라가 뒤로 물러나는데, 중력이 그대로면 화면에서는 물건이 느릿느릿 떨어져 보이기 때문입니다.
수천 개를 한꺼번에
Rapier는 멈춘 물건을 재워서 계산을 건너뜁니다. 그런데 바닥 판을 옮기면 엔진이 그 위의 물건을 모두 깨웁니다. 열린 구역의 물건이 그대로 남다 보니, 7구역에서는 깨어 있는 물건이 3,489개였고 물리 계산 한 번에 18.8 ms가 걸렸습니다(자동 주행으로 잰 느린 쪽 5% 기준). 한 프레임(16.7 ms)을 물리 혼자 넘는 시간입니다.
그래서 구멍에서 먼 물건은 물리 세계에서 잠시 빼 둡니다. 화면에는 그 자리에 그대로 그리고, 구멍이 다가오면 다시 넣습니다. 기준은 화면이 아니라 구멍과의 거리입니다. 후반에는 카메라가 멀리 물러나 화면에 물건이 수천 개 들어오지만, 실제로 움직일 수 있는 것은 구멍 근처뿐입니다. 빼고 넣는 단위도 물건 하나가 아니라 탑 하나, 피라미드 하나 같은 무리입니다. 하나씩 빼면 받침만 빠진 탑 윗부분이 허공에서 떨어집니다. 굴러가던 물건이 빼 둔 무리에 다가가면 그 무리도 다시 넣는데, 이때 물건의 크기와 속도만큼 미리 넣어야 큰 물건이 빼 둔 탑을 뚫고 지나가지 않습니다. 이렇게 바꾼 뒤 같은 7구역에서 실제로 계산하는 물건은 1,400개 남짓이 됐고, 계산 시간은 7.7 ms로 내려왔습니다.
처음에는 무리의 범위를 처음 쌓인 자리로만 잡았습니다. 그러자 쓰러진 탑 꼭대기가 멀리 굴러간 뒤에는, 구멍이 그 위로 가도 떨어지지 않다가 탑 밑동을 삼키고 나서야 떨어지는 일이 생겼습니다. 꼭대기는 이미 멀리 있는데 무리는 아직 밑동 자리에 있다고 계산했기 때문입니다. 지금은 무리를 뺄 때 물건들이 실제로 멈춘 자리로 범위를 다시 잽니다.
그림과 소리
물건 모델은 Kenney가 무료로 공개한 3D 키트 6종(음식, 자동차, 가구, 자연, 교외 주택, 상가)에서 67종을 골랐습니다. 모델 파일을 그대로 불러오지 않고, 빌드 전에 스크립트로 꼭짓점 색과 모양만 남긴 파일 하나(1.1 MB)로 구워 둡니다. 같은 종류의 물건은 한 번에 그립니다.
색은 3단으로 끊어 칠하는 카툰 음영으로, 외곽선은 모델을 조금 부풀려 뒤집어 그리는 방법으로 냈습니다. 바닥 무늬 10종은 캔버스로 그려 한 묶음의 텍스처로 올리고, 바닥은 한 장으로 둔 채 지도 가운데에서 떨어진 거리로 구역을 골라 그 무늬를 칠합니다. 무늬 크기도 구역의 물건 크기를 따라가서, 거인 구역에서는 식탁보 체크와 마루 널빤지가 거인 크기로 깔립니다.
점수와 단추도 HTML을 얹지 않고 게임과 같은 캔버스 안에 그립니다. 서체는 배민 주아체 하나만 쓰고, 화면에 나오는 글자만 남겨 21 KB로 줄였습니다. 화면 읽기 프로그램이 읽을 글자는 보이지 않는 HTML로 따로 둡니다.
효과음과 배경음은 소리 파일 없이 Web Audio로 그 자리에서 합성합니다. 연달아 삼키면 '뽁' 소리의 음이 조금씩 올라가고, 여러 개가 한꺼번에 빠져도 소리가 뭉개지지 않게 0.1초 간격을 두고 차례로 냅니다.
성장 수치
물건 하나의 경험치는 가로, 높이, 세로를 곱한 부피의 2/3제곱에 10을 곱한 값입니다. 2/3제곱은 겉넓이처럼 늘어나는 값이라, 크기가 두 배인 물건은 경험치를 네 배쯤 줍니다. 부피를 그대로 쓰면 큰 물건 하나가 작은 물건 수백 개를 덮어 버립니다.
레벨 비용은 구역마다 정합니다. 한 구역에 놓인 물건의 경험치를 모두 더한 값의 30%가 그 구역 10레벨의 비용이고, 구역 안에서는 뒤 레벨로 갈수록 조금씩 비싸집니다. 한 구역에서 30%쯤만 삼키면 다음 문이 열린다는 뜻입니다. 구멍 반지름은 레벨마다 3.5%씩 커져서, 1 m로 시작해 100레벨에서는 30 m쯤 됩니다.
모델: Kenney(새 탭에서 열림)(CC0), 서체: 배민 주아체(OFL)
COMMENTS
댓글을 불러오고 있습니다.