"엘리트와 잡몹이 몰리면 버벅인다"는 제보를 받았습니다. 성능 문제로 읽고 성능을 고쳤습니다. 프레임마다 힙 할당을 두 군데서 하고 있었고, 마법사 스킬 하나가 적을 셀 때 스웜 전체를 다시 훑는 O(n²)를 돌고 있었습니다. 셋 다 진짜 문제였고 고쳤더니 시드 고정 런에서 12% 빨라졌습니다.

그런데 체감이 없었습니다. 여전히 버벅인다는 답이 돌아왔습니다.

"화면이 흔들린다"가 단서였다

두 번째 제보에 이런 말이 있었습니다. 뭉친 곳으로 돌파하면 화면이 마구 흔들린다고. 프레임이 떨어지는 것과 화면이 흔들리는 것은 다른 증상입니다. 프레임률이 문제라면 화면은 그냥 뚝뚝 끊기지, 흔들리지 않습니다.

그래서 재봤습니다. 임시 카운터를 심고 600초를 돌렸습니다.

frames=63695  frozen=27688 (43.5%)  shakeHi=46975 (73.7%)  crits=43510

게임 프레임의 43.5%가 dt=0으로 지나가고 있었습니다. 시간이 0이라는 건 그 프레임에서 세계가 한 발짝도 안 움직였다는 뜻입니다. 숫자가 정확히 맞아떨어집니다. 600 게임초를 만들려면 60분의 1씩 36,000프레임이면 되는데 63,695프레임이 걸렸으니, 게임이 0.565배속으로 돌고 있었습니다. 렌더러는 60fps를 그대로 유지하고 있었고요. 그게 "버벅임"의 정체였습니다.

원인은 며칠 전에 내가 되살린 두 줄

히트스톱이라는 게 있습니다. 타격 순간 게임 시간을 몇 프레임 멈춰서 손맛을 만드는 기법입니다. 크리티컬이 터질 때 살짝 멈추고 화면이 흔들리게 해 놨습니다.

문제는 크리티컬 판정이 적중마다 돈다는 것입니다. 스웜에서는 초당 72번이었습니다. 프레임당 한 번을 넘습니다. 히트스톱이 빠지는 속도보다 다시 걸리는 속도가 빨라서, 한 번 걸린 뒤로는 영영 안 풀렸습니다. 화면 흔들림도 같은 모양이었습니다. 흔들림이 초당 190씩 잦아드는데 프레임마다 10 이상 들어오니 상한에 붙어서 내려오질 않았습니다.

여기서 배운 게 둘 있습니다.

프레임당 한 번 넘게 나는 이벤트에 안전한 히트스톱 값은 없습니다. 1프레임으로 낮춰도 50% 정지입니다. 화면 전체를 멈추거나 흔드는 연출은 드문 이벤트 전용이어야 하고, 적중마다 나오는 피드백은 그 자리에서만 일어나야 합니다.

그리고 이 값은 정상 동작이 안 보이고 폭주만 보이는 구조였습니다. 흔들림 세기가 제곱으로 계산돼서 한 번 들어오는 10은 0.05픽셀입니다. 즉 "살렸더니 드디어 보이네"라고 생각한 그 순간이 이미 버그였습니다. 죽어 있던 값을 살릴 때는 그게 초당 몇 번 불리는지부터 세야 합니다.

고친 뒤 다시 재니 43.5%가 1.9%로, 73.7%가 1.5%로 내려갔습니다. 남은 건 원래 의도했던 스킬과 보스 처치의 연출입니다. 폰에서 확인받았습니다. 버벅이던 건 없어졌다고요.

상인이 안 눌린다는 제보에 판정 사각형만 세 번 고쳤다

같은 기간에 더 부끄러운 게 있었습니다. 런 중에 나타나는 떠돌이 상인을 눌러도 반응이 없다는 제보였습니다.

첫 번째로 탭 판정 박스를 키웠습니다. 30픽셀인데 그려지는 상인이 34픽셀이었고, 도망 중이라 조준하는 사이에 움직였으니까요. 두 번째로는 판매대가 열리자마자 닫히는 걸 찾았습니다. 나가는 판정이 화면 전체 폭의 보이지 않는 띠 두 개였는데, 그중 아래쪽이 하필 폰에서 엄지가 놓이는 자리였습니다. 상인을 쫓느라 짚고 있던 엄지를 고쳐 짚는 순간 그게 나가기 버튼이 됐습니다.

세 번째로 같은 제보가 왔습니다. 그제서야 산수를 해봤습니다. 거래 사거리가 26픽셀인데 상인은 초당 38로 도망가고 가장 느린 클래스는 46으로 달립니다. 접근 속도가 초당 8입니다. 78픽셀을 26픽셀로 좁히려면 6.5초 동안 한 치의 오차 없이 직선으로 쫓아야 하고, 그러고 나서 움직이는 34픽셀짜리 표적을 정확히 눌러야 했습니다. 폰에서 스웜에 밀리면서 될 일이 아닙니다.

판정 사각형 문제가 아니었습니다. 거기까지 갈 수가 없었던 겁니다. 히트박스를 아무리 키워도 못 고쳤을 문제였습니다. 상인 속도를 낮추고, 사거리를 넓히고, 사거리 안에 들어오면 멈춰 서게 하고, 무엇보다 화면 상단에 고정된 거래 버튼을 만들었습니다. 고정된 사각형은 도망가지도, 손가락에 가려지지도 않습니다.

다음부터는 "누를 수 있나" 전에 "거기까지 갈 수 있나"를 먼저 계산하기로 했습니다.

곱셈은 끝까지 따라간다

오늘 폰에서 온 마지막 제보는 45분 엔딩 보스가 너무 튼튼하다는 것이었습니다. 결국 잡기는 하는데 너무 오래 걸려서 그 사이 다른 보스들이 겹겹이 쌓인다고요.

원인은 이틀 전에 제가 한 조정이었습니다. 그때는 반대 제보를 받았습니다. 보스가 스킬 한 번 써보지도 못하고 죽는다고요. 실제로 재보니 첫 보스가 0.9초 만에 죽고 있었습니다. 그래서 보스 기본 체력을 크게 올렸습니다.

그런데 기본값과 시간 배율이 곱해집니다. 초반만 올리려던 조정이 곱셈을 타고 45분까지 그대로 올라갔습니다. 엔딩 보스가 플레이어들이 이미 깨는 걸 증명한 값의 1.49배가 돼 있었습니다.

고친 방법은 시간 배율에 상한을 두는 것입니다. 상한을 걸면 엔딩 지점이 예전에 검증된 값과 2% 안쪽으로 돌아옵니다. 상한은 22분쯤부터 걸리니까 정작 문제였던 초반은 그대로 2.71배입니다. 실측으로 후반 보스전이 943초에서 579초로 줄었습니다.

이 게임에서 45분 엔딩은 다음 스테이지를 여는 유일한 조건이라, 여기 손대면 반드시 45분을 다시 돌려보고 넘어갑니다.

남은 것

이 글을 쓸 때 심사를 기다리던 v1.2.46은 통과해서 스토어에 올라갔습니다. 이번 판에는 위의 수정들과 함께 타이틀 화면 재설계, 그리고 이 게임 최초로 강해지지 않는 보상인 칭호 9종이 들어갔습니다.

이 게임의 보상은 그동안 전부 힘으로 바뀌었습니다. 업적은 무기를 열고, 스테이지는 캐릭터를 열고, 주간 과제는 장비를 줍니다. 그래서 "가장 어려운 걸 깬 사람에게 뭘 줄까"에 답이 없었습니다. 또 힘을 주면 이 게임이 벗어나려던 그 반복이 되고, 안 주면 목표가 안 됩니다. 칭호가 그 출구입니다. 자랑거리는 되는데 강해지지는 않습니다.