어떤 게임이냐면
Hole Punch는 개인 개발자(notoriousbfg)가 공개한 웹 게임이에요. 한 줄로 설명하면 “중력장을 이용해서 우주선을 새총처럼 날려 보내는 게임”이에요. 우주선은 주변 천체의 중력에 끌려가는데, 이 끌림을 잘 이용하면 궤도를 휘어서 목표 지점까지 날아갈 수 있어요. 웹사이트에서 바로 해볼 수 있으니 한 판 해보고 오셔도 좋아요.
이 글은 게임 리뷰보다는 이런 게임을 직접 만든다면 어떤 기술이 필요한지 개발자 눈으로 뜯어보는 글이에요. 생각보다 배울 게 많거든요.
중력 새총(스윙바이)이 뭐냐면
실제 우주 탐사에서도 쓰는 기법이에요. 1977년에 발사된 보이저 호는 목성과 토성 곁을 스치듯 지나가면서 행성의 중력으로 속도와 방향을 바꿨어요. 이걸 중력 도움(gravity assist) 또는 스윙바이라고 불러요. 연료를 거의 쓰지 않고도 궤도를 크게 바꿀 수 있는 방법이에요. 롤러코스터가 내리막에서 속도를 얻어 다음 오르막을 넘는 장면을 떠올리면 비슷해요.
엄밀히 말하면 행성도 움직이고 있어서, 탐사선이 행성의 운동 에너지를 조금 ‘빌려오는’ 원리예요. 그래도 게임에서는 멈춰 있는 천체 주위를 휘어 도는 것만으로도 그 손맛을 충분히 낼 수 있어요.
핵심 구현: 중력 계산과 수치 적분
뉴턴의 만유인력 법칙은 “두 물체 사이의 힘은 질량에 비례하고 거리의 제곱에 반비례한다”는 내용이에요. 게임에서는 매 프레임마다 우주선과 모든 천체 사이의 거리를 재고, 거기서 나온 가속도를 더해 주면 돼요.
~~~js
const G = 1;
const SOFTENING = 25; // 거리가 0에 가까워질 때 힘이 무한대로 튀는 걸 막아줌
function gravityAt(ship, bodies) {
let ax = 0, ay = 0;
for (const b of bodies) {
const dx = b.x - ship.x;
const dy = b.y - ship.y;
const distSq = dx dx + dy dy + SOFTENING;
const f = (G b.mass) / (distSq Math.sqrt(distSq));
ax += dx * f;
ay += dy * f;
}
return { ax, ay };
}
// 반암시적 오일러: 속도를 먼저 바꾸고, 바뀐 속도로 위치를 옮김
function step(ship, bodies, dt) {
const { ax, ay } = gravityAt(ship, bodies);
ship.vx += ax * dt;
ship.vy += ay * dt;
ship.x += ship.vx * dt;
ship.y += ship.vy * dt;
}
~~~
여기에 재밌는 함정이 하나 숨어 있어요. 컴퓨터는 연속된 시간을 그대로 다룰 수 없어서, 시간을 잘게 쪼갠 다음 계산을 조금씩 쌓아 가요. 이걸 수치 적분이라고 불러요.
가장 단순한 방식은 명시적 오일러예요. 이전 속도로 위치를 먼저 옮기고, 속도는 나중에 갱신하는 방식이에요. 이렇게 하면 원 궤도를 돌던 우주선이 한 바퀴 돌 때마다 조금씩 바깥으로 밀려나요. 계산 오차가 에너지를 슬금슬금 불리기 때문이에요. 위 코드처럼 속도를 먼저 갱신하고, 그 새 속도로 위치를 옮기도록 순서만 바꿔도 궤도가 훨씬 안정적으로 유지돼요. 더 정확하게 하고 싶다면 Velocity Verlet 같은 방법도 있어요.
고정 타임스텝도 꼭 챙겨야 해요. requestAnimationFrame이 호출되는 간격은 기기마다 달라요. 이 값을 그대로 dt로 쓰면 120Hz 모니터와 60Hz 모니터에서 궤도가 서로 달라져요. 그래서 물리는 1/120초처럼 정해진 간격으로 돌리고, 화면 그리기는 따로 하는 게 정석이에요.
이렇게 해 두면 같은 입력에는 늘 같은 궤적이 나와요. 그러면 조준선 미리보기도 쉽게 만들 수 있어요. 지금 발사하면 어디로 날아갈지 점선으로 보여주는 기능인데, 같은 step 함수를 몇백 번 미리 돌리기만 하면 돼요.
비슷한 게임들과 비교해 보면
중력을 다루는 게임 하면 커벌 스페이스 프로그램(KSP)이 먼저 떠올라요. 재밌게도 KSP는 모든 천체의 중력을 동시에 계산하지 않아요. 우주선이 어떤 천체의 ‘영향권’ 안에 들어가 있으면 그 천체 하나만 고려하는 패치드 코닉(patched conics) 근사를 써요. 궤도를 예측하기 쉽고 안정적으로 만들려는 선택이에요.
반대로 여러 천체의 중력을 전부 더하는 방식을 N체 시뮬레이션이라고 해요. 더 사실적이지만, 아주 작은 차이가 결과를 크게 바꾸는 카오스적인 성질이 있어요. 퍼즐 게임에서는 오히려 이 예측하기 어려운 성질이 재미가 되기도 해요. 앵그리버드 스페이스나 Osmos도 중력이 만드는 비슷한 재미를 각자의 방식으로 풀어낸 게임이에요.
한국 개발자에게 주는 시사점
이런 프로젝트는 사이드 프로젝트나 게임잼 주제로 딱 좋아요. 캔버스 API와 벡터 계산 정도만 알면 하루 이틀 안에 프로토타입이 나오거든요. 그 과정에서 고정 타임스텝, 수치 오차, 결정론적 시뮬레이션 같은 개념을 직접 손으로 익히게 돼요. 이런 개념은 게임뿐 아니라 애니메이션 라이브러리, 물리 기반 UI, 시뮬레이션 백엔드에서도 그대로 쓰여요. 라이브러리 없이 물리를 한 번 직접 짜보면 그 경험이 생각보다 오래 남아요.
마무리
> 중력 새총 게임의 재미는 ‘정확한 물리’와 ‘예측 가능한 손맛’ 사이의 균형에서 나와요.
여러분이 이런 게임을 만든다면 사실적인 N체 시뮬레이션과 예측하기 쉬운 근사 방식 중 어느 쪽을 고르시겠어요? 직접 물리 엔진을 짜다가 겪은 재밌는 버그가 있다면 같이 나눠 주세요!
🔗 출처: Hacker News