2026-06-01·Sanchez

토끼굴을 갈아엎기 전, 구버전 진단

리빌드를 결심한 건 구버전을 한 번 더 플레이해보고 나서였다.
화면은 굴러가는데, 손에는 아무것도 안 남았다.

물리가 없었다

가장 큰 문제는 물리가 없다는 것이었다.
토끼는 Catmull-Rom 곡선으로 깐 경로 위 고정점에 부착돼 있었고, 카메라가 일정 속도로 그 점을 따라갈 뿐이었다.
중력도 점프도 관성도 가감속도 없었다.
A/D 키를 누르면 토끼가 붙어 있는 벽면 각도만 즉발로 휙 바뀌었다.
무게감이라는 게 들어설 자리가 없었다.

즉발 입력이 왜 문제인지는 나중에야 정리가 됐다.
입력에 반응이 즉시 100% 따라오면 조작하는 쪽에서는 아무런 저항을 못 느낀다.
저항이 없으면 잘 다뤘다는 감각도 생기지 않는다.
손맛이란 결국 "내 뜻대로 되지만 완전히 즉시는 아닌" 그 틈에서 나오는데, 구버전엔 그 틈이 0이었다.

비주얼이 의도와 정반대였다

비주얼은 의도와 정반대였다.
스탠포드 버니 스캔 모델(bunny.obj)에 흰색 단색, 애니메이션 0개.
배경은 거의 검정이었고 벽에는 "dirty wall" PBR 텍스처를 발랐다.
포근한 동화를 만들려던 게임이 호러 톤이 돼 있었다.

이건 실력 문제가 아니라 순서 문제였다.
무드를 정하기 전에 "일단 굴러가게" 하려고 손에 잡히는 에셋부터 붙였고, 그 에셋들이 각자 자기 톤을 끌고 들어왔다.
사실적인 스캔 모델과 사실적인 벽 텍스처와 어두운 배경은 하나하나는 멀쩡한데, 합치면 자동으로 어두운 사실주의가 된다.
아무도 호러를 의도하지 않았는데 결과는 호러였다.

실패가 없었다

결정타는 게임 구조였다.
장애물도 없고 실패 조건도 없었다.
그냥 500m에 도달하면 끝.
떨어지는 당근을 줍긴 하는데 못 주워도 아무 일도 안 일어났다.
한마디로 "당근 줍기 시뮬레이터"였다.

실패가 없으면 성공도 없다.
잃을 게 없는 선택은 선택이 아니라서, 플레이어가 무엇을 해도 이야기가 만들어지지 않는다.
당근을 열 개 연속으로 주워도, 하나도 못 주워도 화면은 똑같이 굴러간다.
이 구조에서는 조작을 아무리 다듬어도 재미가 생길 수 없었다.

구버전 화면

코드가 정직하지 않았다

코드 쪽도 정직하지 못했다. tunnelGenerator.ts, collision.ts는 만들어만 두고 안 썼고, constants.ts 값과 컴포넌트 하드코딩 값이 어긋나 있었다.
rapier 의존성도 선언만 돼 있고 실제로는 안 굴러갔다.

지금 보면 이 셋은 전부 같은 증상의 다른 얼굴이다. 선언과 실제가 어긋나 있는 것. 파일 이름은 충돌 판정이라고 말하는데 아무도 부르지 않고, 상수 파일은 이 값이 진실이라고 말하는데 컴포넌트는 다른 숫자를 쓰고, package.json은 물리 엔진을 쓴다고 말하는데 실행 경로엔 없다.

이런 코드베이스는 읽는 사람을 계속 속인다.
어디를 고쳐야 할지 알아내는 데만 시간이 들고, 고쳤다고 생각한 게 실은 죽은 코드였던 경험이 반복된다.
진짜 비용은 코드 줄 수가 아니라 이 불신이었다.

고칠 것인가, 다시 만들 것인가

증상을 늘어놓고 보니 결론은 하나였다.
부분 수리로 될 일이 아니다.

판단 기준은 단순했다.
문제가 표면에 있으면 고치고, 바닥에 있으면 다시 만든다.
여기서 나온 항목들은 전부 바닥이었다.
물리를 넣으려면 이동 방식을 전부 갈아야 하고, 실패를 넣으려면 충돌·체력·게임오버 흐름이 새로 필요하고, 톤을 바꾸려면 모델과 머티리얼과 조명을 다 버려야 한다.
하나씩 고쳐도 결국 전부 다시 쓰게 되는데, 그럴 거면 남은 구조에 끌려다니지 않는 편이 낫다.

반대로 리빌드를 말렸을 조건도 분명하다.
살릴 자산이 많거나, 이미 쓰는 사람이 있어서 멈출 수 없거나, 문제가 몇 개의 지역적 결함이었다면 고쳐 썼을 것이다.
구버전은 셋 다 아니었다.
남길 만한 건 "토끼가 굴을 달린다"는 발상 하나뿐이었고, 그건 코드가 아니라 문장이라 옮기는 데 비용이 들지 않았다.

다음 편에서는 무엇을 기준으로 다시 그릴지 정한 이야기를 쓴다.

다음 편: 재설계 방향 정하기 →