2026-06-10

프레임 사이로 빠져나간 뿌리 — 충돌과 터널링

충돌 판정은 두 종류로 나뉜다.
당근이나 바위 같은 점 모양 엔티티는 구로 근사해서, 플레이어와의 3D 거리가 반경 합보다 작으면 닿은 것으로 본다.
단순하지만 충분했다.

부딪히면 하트가 깎이고, 다 닳으면 게임 오버

까다로운 건 뿌리였다.
뿌리는 점이 아니라 터널 둘레를 가로지르는 호 모양 장애물이다.
그래서 세 조건을 모두 만족할 때만 충돌이다 — 깊이가 가깝고, 토끼 각도가 뿌리의 호 범위 안에 있고, 점프 높이가 뿌리를 못 넘었을 때.
충분히 높이 뛰면 뿌리 위로 통과한다.

여기서 진짜 버그가 나왔다.
빠른 속도에서 뿌리를 그냥 뚫고 지나가버렸다.
원인은 프레임당 이동량이었다.
최고속도 30m/s에서 화면이 20fps로 떨어지면 한 프레임에 1.5m를 건너뛴다.
그런데 뿌리의 깊이 판정 창은 그보다 좁았다(1.46m).
토끼가 한 프레임 전엔 뿌리 앞, 다음 프레임엔 뿌리 뒤 — 정작 겹치는 순간은 두 프레임 사이로 빠져버린다.
고전적인 터널링이다.

해결은 판정 창에 이번 프레임 이동량(sweep)을 더해주는 것이었다.

export function hitsRoot(playerDepth, p, rootDepth, root, sweep = 0): boolean {
  if (Math.abs(playerDepth - rootDepth) > SPAWN.ROOT_TUBE + PLAYER.RADIUS + sweep)
    return false;
  if (p.height >= SPAWN.ROOT_CLEARANCE) return false;
  return angularDist(p.angle, root.angle) < root.arc / 2 + PLAYER.RADIUS / TUNNEL.RADIUS;
}

이러면 빨리 지나갈수록 판정 창이 그만큼 넓어져, 점프하지 않은 한 뿌리를 놓치지 않는다.

니어미스(+50점)에도 요령이 있었다.
다가올 때가 아니라 "지나간 뒤" 단면 거리로 판정한다.
부딪히진 않았는데 아슬아슬하게 스친 순간을, 통과 직후에 재서 보너스를 준다.
이 한 끗이 회피의 쾌감을 점수로 보상한다.

다음 편은 마지막, 효과음을 코드로 합성한 이야기다.

다음 편: Web Audio 효과음 합성 →