기본 모드 실험 이틀: 모바일 생존 이탈의 원인은 난이도가 아니라 첫 판 멈춤이었다
Salmonholic 새 플레이어를 생존 모드로 먼저 보내는 A/B 실험을 이틀 돌렸다. 모바일만 무너진 이유를 난이도에서 찾다가, 이탈 기록을 다시 보고 첫 판 5~22초 멈춤을 찾아 고친 기록.

생존 모드를 붙인 날의 글은 “실험 결과는 아직 없다”로 끝났다. 0.24.0(커밋 6401c6c)에서 처음 설치한 프로필을 반반 나눠, A군은 지금처럼 배틀로얄로, B군은 첫 실행을 생존 모드로 시작하게 했다. 그 뒤 이틀 동안 버전을 다섯 번 올렸고, 마지막 0.25.0(e570e3d)에서 생존을 모든 기기의 기본 모드로 정했다. 그 사이에 나는 모바일 문제의 원인을 한 번 잘못 짚었다. 이 글은 그 과정의 기록이다.
하루 만에 갈린 결과
하루치 집계에서 데스크톱과 모바일이 정반대로 나왔다(커밋 ec902e1).
| 기기 | 군 | 판당 평균 플레이 | 1분 넘긴 판 |
|---|---|---|---|
| 데스크톱 | A(배틀로얄 먼저) | 99초 | 75% |
| 데스크톱 | B(생존 먼저) | 285초 | 79% |
| 모바일 | B(생존 먼저) | 33초 | 20% |
데스크톱 B군은 한 판을 거의 세 배 오래 했다. 모바일 B군은 5판뿐이었지만 1분을 넘긴 판이 20%였다. 표본이 작아도 그대로 둘 수는 없었다. 0.24.1에서 터치 기기의 새 플레이어만 A군으로 고정하는 임시 조치를 넣고, 모바일 생존 첫 판을 따로 조사하기로 했다.
첫 번째 진단: 너무 빨리 둘러싸인다
조사 보고서에도 “모바일 B 표본은 5경기로 작으므로 단일 원인을 확정한 결과가 아니다”라고 적었다. 그래서 코드에서 위험 요인을 찾았다. 가장 눈에 띈 것은 워밍업이었다. 배틀로얄은 로비에서 기다리는 동안 조작을 익힐 시간이 있다. 생존은 로비 없이 바로 경기에 들어가고, 연어 14마리가 둥글게 둘러싼 채로 시작한다. 손가락 조작을 처음 하는 사람에게는 가혹해 보였다.
0.24.2(93f4411)에서 고친 것은 이렇다.
- 첫 판이고 터치 기기면 연어를 6마리만, 더 멀리 배치한다.
- 추가 출현량은 0초 35%, 15초 67.5%, 30초 100%로 천천히 올린다. 체력·피해·속도는 그대로다.
- 원래 20초가 지나면 사라지던 “드래그로 이동” 안내를 첫 드래그에 성공할 때까지 남긴다.
- 앱을 전환하면 생존 경기를 일시정지한다.
같은 날 브라우저 QA에서 15분 생존 성공 창의 “보상 받기” 버튼이 어두운 바탕에 어두운 글자라 거의 안 보인다는 결함도 나와서 함께 고쳤다.
0.24.2 브라우저 QA에서 찍힌 수정 전 화면. 두 번째 버튼에 “보상 받기”가 적혀 있지만 읽을 수 없다.
터치 기기도 다시 실험에 넣었다. 그런데 0.24.4(01cec23) 시점의 이틀치 집계는 기대와 달랐다. 데스크톱 생존 먼저 군은 19명이 평균 4.6판, 1분 넘긴 판 89.8%, 한 사람당 약 16분을 했다. 배틀로얄 먼저 군은 약 2.4분이었다. 반면 모바일 생존 먼저 군은 고친 뒤에도 1분 넘긴 판이 27%(11판)로, 배틀로얄 먼저 군의 56%에 한참 못 미쳤다. 나는 모바일 새 플레이어를 배틀로얄로 보내는 쪽으로 정했다.
숫자를 다시 열어 보니 아무도 죽지 않았다
요약 숫자만 보고 결정한 것이 걸려서, 9월 28~30일 웹 텔레메트리 원본 이벤트 544건을 받아 판 단위로 다시 나눴다. 표본은 전부 CrazyGames 트래픽이다. 0.21.5에서 넣은 이탈 문맥 이벤트에 그 판의 가장 긴 프레임 시간이 들어 있었던 것이 결정적이었다.
생존 판은 기기와 상관없이 대부분 2초 넘게 멈췄다. 배틀로얄 판은 19%였다.
모바일 생존 15판 중 죽어서 끝난 판은 0판이었다. 15판 중 13판에서 화면이 2초 넘게 멈췄고, 멈춘 화면에 5~18초 동안 아무 입력이 없다가 페이지를 닫았다. 가장 긴 멈춤은 21.9초였다. 연어에게 둘러싸여서 떠난 것이 아니라 게임이 멈춰서 떠난 것이다. 앞의 수정은 틀린 가설을 열심히 고친 셈이었다.
시점으로 나누면 더 분명했다. 부팅 후 120초 안에 시작한 생존 판은 데스크톱 28판 중 27판, 모바일 15판 중 13판이 멈췄다. 같은 사람의 첫 생존 판은 48판 중 41판이 멈췄고, 두 번째 판부터는 4판 중 1판이었다. 데스크톱 생존도 36판 중 28판이 멈췄다. 데스크톱 전환율이 좋았던 것을 보면 데스크톱 사람들은 참고 기다린 것으로 보이고, 휴대폰 사람들은 기다리지 않았다.
원인: 보이지 않는 고양이 9마리의 옷
생존 모드는 배틀로얄의 10인 경기 구조를 그대로 쓴다. 내 고양이만 살아 있고 나머지 9자리는 처음부터 죽은 상태로 화면에 나오지 않는다. 그런데 이 9자리가 배틀로얄용 무작위 품종·캐릭터·장신구를 그대로 갖고 있었다. 첫 프레임에서 외형을 준비하는 코드가 이 품종 팩(최대 12개)과 캐릭터 팩을 차례로 내려받았다. 그리고 팩이 하나 도착할 때마다 10마리 외형 아틀라스를 다시 합성하면서, 큰 품종 PNG를 메인 스레드에서 매번 다시 디코딩했다.
배틀로얄에서는 같은 작업이 로비의 참가자 미리보기에서 끝난다. 이탈 문맥은 경기 시작부터 재기 때문에 배틀로얄 측정에는 잡히지 않았다. 로비를 건너뛰는 생존에서만 경기 첫 수십 초에 그 비용이 몰렸다.
고친 코드는 몇 줄이다.
if not p.alive:
p.death_t = 0.0
# 보이지 않는 9자리의 품종·캐릭터 팩을 받지 않는다
g.set_skin(p, 0)
p.breed = 0
p.accessory = 0
내 고양이의 외형은 그대로 두었다. 이제 기본 프로필로 생존을 시작하면 외형 팩 요청이 0건이다.
0.25.0에서 바꾼 것
| 항목 | 0.24.x | 0.25.0 |
|---|---|---|
| 새 플레이어 첫 모드 | A/B 실험, 모바일은 배틀로얄 | 모든 기기 생존 |
| 생존 첫 판 외형 팩 요청 | 최대 13개 | 0개 |
| 첫 60초 최대 프레임(QA) | 실측 중앙값 6~7초 | 13~42ms |
| 첫 터치 판 첫 카드 | 초밥 6개 | 초밥 3개 |
| 터치 안내 | 드래그 안내 한 줄 | 건너뛸 수 있는 3단계 안내 |
QA는 iPhone 13 세로·가로, Pixel 7, 데스크톱 화면 조건에서 새 플레이어의 첫 60초를 봤다. 외형 팩 요청 0건, 최대 프레임 13~42ms, 오류 0건이었다. 메모리가 적은 Android는 낮은 그래픽 단계로 시작하게 했다. 데이터상 모바일 생존 사망이 0판이었으므로, 생존 초반에 무적 방패를 주자는 안은 넣지 않았다. 연어 한 방에 쓰러지는 규칙과 난이도도 건드리지 않았다.
이번에 배운 것은 단순하다. 작은 표본에서 그럴듯한 원인을 찾으면 그것을 고치고 싶어진다. 하지만 “죽었나, 떠났나”를 먼저 나눴다면 하루를 아꼈을 것이다. 이탈 문맥에 프레임 시간을 넣어 둔 덕분에 원인을 찾았다. 그 이벤트를 어떤 기준으로 모으는지는 쿠키 없는 텔레메트리 글에, 같은 방식으로 포털 크래시를 찾은 이야기는 크래시 1.42% 글에 있다. 생존 모드는 Salmonholic 게임 페이지에서 바로 해 볼 수 있다.


