FINGER OF DOOM

개선 결과 · REPORT 003

이제 이길 수 있는
게임이 되었다

진단설계를 실제 코드로 옮긴 결과입니다. 승리 조건이 생겼고, 선택이 선택이 되었고, 무엇보다 검증할 수 있는 프로젝트가 되었습니다.

DATE 2026-08-23 VERSION 0.1.1 → 0.2.0 커밋 11 에이전트 29
01

한 장 요약

무엇이 달라졌는가

v0.1.1 v0.2.0 승리 조건 없음 20층 보스 = 클리어 20층 보스 승률 0% 53% 한 런 클리어율 불가능 46% 도달 중앙값 10층 20층 보상 선택지 4~5장 중 절반이 무효 3택 전부 유효 선택 있는 이벤트 9종 중 2종 9종 전부 손가락 슬롯 도달 불가 레벨에 잠김 시작 2 + 보스 보상 거부된 탭 피드백 0채널 3채널 모든 수치는 런타임 공식을 복제한 시뮬레이터와 헤드리스 스모크 테스트로 측정
CORE 진단이 지적한 가장 큰 문제는 “실기기에서 한 런을 끝까지 완주시켜 본 적이 없다”였습니다. 이번에 고친 것의 절반은 기능 추가가 아니라 완주를 막던 것들의 제거입니다.
02

가장 먼저 만든 것

고치기 전에, 고쳤는지 아는 방법부터

6시간 자율 작업에서 가장 위험한 상태는 무엇이 깨졌는지 모르는 것입니다. 그래서 첫 커밋은 게임 코드가 아니라 검증기였습니다.

validate.gd 무결성 7종 리소스 로드 · 매니페스트 동기화 · 번역 키 · 저장/로드 키 정합 · GameConfig 배선 · 개발 스위치 · shake 인자 순서 sim_runtime.py 밸런스 곡선 런타임 공식(.tres/.gd)을 직접 파싱해 재현. 기존 시뮬레이터는 멀티터치 무시 · 크리 1.5 vs 실제 2.0 으로 다른 게임을 계산했다 smoke_battle.gd 런타임 오류 실제로 새 게임을 시작해 전투를 굴린다. 부팅 검사는 파싱만 보므로 없는 노드 접근 · 시그니처 불일치를 잡지 못한다 셋 다 통과해야 커밋한다 — 6시간 자율 작업에서 무엇이 깨졌는지 모르는 상태가 가장 위험하다
효과 validate.gd는 만들자마자 진단 보고서의 발견 4건 (매니페스트 누락 보스 4종 · pet_data 미저장 · 치트 스위치 · shake 인자 역전)을 독립적으로 재현했습니다. 사람이 찾아야 했던 것이 이제 자동으로 걸립니다.
03

P0 · 출혈 정지

플레이어의 진행을 파괴하던 것들

소프트락

최상위 보상을 얻으면 게임이 멈췄다

GameManager.gd · damage_player()

불사조 펫의 부활 판정이 사망 확정 뒤에 있었습니다. 세이브 삭제 → 소울 선지급 → GAMEOVER 전환이 모두 끝난 다음에 부활이 일어나니, 부활에 성공한 플레이어는 앱 강제종료 외에 탈출구가 없었습니다.

수정 — 부활 판정을 요정 포션과 같은 위치(사망 확정 이전)로 올렸습니다. 세이브 삭제·소울 이중 지급·상태 고착 세 증상이 한 번에 사라졌습니다. _on_player_died()는 연출 전용으로 축소했습니다.

데이터 유실

펫 레벨이 재시작마다 초기화됐다

SaveManager.gd · save_meta_data()

19개 필드를 직렬화하면서 pet_data 하나를 빠뜨렸습니다. 킬마다 발생하던 디스크 쓰기가 전부 헛수고였습니다.

유출

플레이어에게 진행도 초기화 버튼이 노출됐다

dev_mode_enabled = true가 커밋된 상태였습니다. 클래스 기본값은 false인데 .tres가 덮어쓰고 있었습니다.

전면 무효

보스 밸런싱이 통째로 죽어 있었다

MonsterData.gd · _get_game_config()

Godot 4에서 모든 Object는 get을 갖는데 has_method("get") == false를 조건으로 걸어 함수가 항상 null을 반환했습니다. 보스 튜닝 7종이 전부 무시되고 있었습니다.

반전 — 고치자 boss_hp_multiplier 0.7이 되살아나면서 20층 보스 승률이 100%가 되어버렸습니다. 고치는 것만으로는 부족했고 수치를 다시 잡아야 했습니다.

04

P1 · 밸런스와 진행

승리 조건이 없는 게임에 결승선을 그었다

코드 전체에 final_stagegame_clear도 없었습니다. 스테이지는 무한히 증가하고 모든 플레이는 패배로 끝났습니다.

0% 25% 50% 75% 100% 1 10 20 30 40 50 ST.20 → 0% 이 지점 이후 전 구간 생존 0 스테이지 · 프로젝트 자체 시뮬레이터(마법사 기준)
v0.1.1 기준. 1~19층은 이미 정상 범위였고 무너지는 지점은 20층 하나였습니다 — 거기에 엔딩을 놓았습니다.
86%10층 보스 승률
중간 관문
53%20층 보스 승률
최종 보스 (목표 50~60%)
46%한 런 클리어율
절반쯤 이기는 난이도
근본 레벨이 로그로 정체하던 원인은 XP가 골드와 같은 값을 쓰면서 스테이지 스케일이 0%였던 것입니다 (30층 고블린도 4XP). XP를 골드에서 분리하고, 죽은 데이터였던 등급별 exp_multiplier를 살렸습니다. 골드도 가격 곡선과 같은 기울기로 올렸습니다 — 수입이 상수인데 물가만 오르니 상점이 장식이었습니다.
05

P2 · 코어 루프

선택이 선택이 되었다

이 게임이 클리커가 아니라 로그라이크임을 주장하는 근거는 선택뿐인데, 그 선택 셋이 모두 형해화되어 있었습니다.

이전

보상 4~5장 중 절반이 무효

SKIP은 완전 no-op이었고, GOLD·XP 카드는 처치 시 이미 자동 지급되는 것의 중복이었습니다.

3택 고정. 즉시파워 / 장기파워 / 생존 세 축에서 하나씩. 만피에 회복 카드, 포션 만재에 포션 카드 같은 무효 선택지는 생성 단계에서 차단합니다.

이전

이벤트 9종 중 7종이 선택지 없음

제단은 최대HP를 강제 징수하고, 상인은 골드를 묻지도 않고 차감했습니다. 정작 설명문은 전부 “~할 수 있다”로 선택을 전제했습니다.

9종 전부 ‘제시 → 선택 → 결과’ 3단계. EventData.choices로 데이터 주도화해 이후 이벤트 추가 비용이 급감합니다.

이전

상자 선택이 가짜였다

카드를 펼쳐 고르게 해놓고, 선택 직후 나머지 보상 전부를 순회하며 지급했습니다. 무엇을 눌러도 결과가 같았습니다.

3장 중 1장만. 유물 확률의 등급 역전(전설 0.05 < 황금 0.10)도 바로잡았습니다.

시그니처 손가락 슬롯이 드디어 플레이어 앞에 나옵니다. 동시에 누를 수 있는 손가락 수가 늘어나는 성장 — 이 게임의 유일한 차별화 축인데 도달 불가능한 레벨 뒤에 잠겨 있어 한 번도 보인 적이 없었습니다. 시작 2슬롯으로 올리고 보스 보상으로 옮겼으며, HUD에 슬롯 게이지를 붙였습니다.
06

P3 · 화면과 조작

30분 런이 오조작 한 번에 날아가지 않는다

문제이전수정
안드로이드 뒤로가기 = 앱 즉시 종료
Godot 4의 quit_on_go_back 기본값이 true
핸들러 0개 UIStack 오토로드 + 일시정지 패널. 모달 스택을 한 곳에서 처리
일시정지 수단 없음 0개 전투 화면 일시정지 버튼 + 하드웨어 back 과 동일 경로
오탭이 되돌릴 수 없는 결과로
22×20 InfoButton 이 110×140 카드 선택 영역에 겹침
15/24 가 44px 미만 버튼 제거 후 카드 롱프레스로 전환. 나머지 8종은 48px 이상으로
20:9 단말의 미설계 세로 영역 약 140px Camera2D 좌상단 기준 + 앵커 기반 재배치
튜토리얼을 건너뛸 수 없음 킬 목표 30 스킵 버튼 + 킬 목표 14
설계 롱프레스로 바꾸려면 선택 확정을 “누를 때”에서 “뗄 때”로 옮겨야 했습니다. 안 그러면 길게 눌러도 이미 보상이 확정됩니다. 골드가 부족해 선택이 막힌 카드도 상세는 볼 수 있게 차단 조건을 분리했습니다 — 살 수 없는 물건이야말로 내용을 확인하고 싶은 순간입니다.
07

P4 · 연출

누른 탭이 사라지지 않는다

1 TAP = 11 POSSIBLE CHANNELS 몬스터 스쿼시 히트 플래시 데미지 숫자 tap.wav 햅틱 강약 구분 화면 흔들림 히트스톱 파티클 화면 플래시 탭 잔상/링 BGM 레이어 쿨다운으로 거부된 탭(초당 2회 초과분)은 11채널 전부 무반응 — 눌러도 아무 일이 없다
v0.1.1 기준. 쿨다운으로 거부된 탭은 이 11개 채널 전부가 무반응이었습니다.

거부된 탭에 반응을 줬다

초당 2회를 넘는 탭이 아무 반응 없이 사라지고 있었습니다 — 클리커에서 가장 나쁜 감각입니다. 다만 거부는 성공보다 약해야 하고 연타 중 계속 발생하므로, 화면 흔들림은 쓰지 않고 자체 throttle 을 걸었습니다. HUD 슬롯 게이지가 이유를 함께 보여줍니다.

파티클을 풀링했다

기존 _spawn_explosion()은 사망마다 ColorRect 6~12개와 Tween 6~12개를 상한 없이 만들고 0.4초 뒤 버렸습니다. 동시 사망 3마리면 순간 36노드입니다. 이제 CPUParticles2D 12개 고정 풀로 런타임 노드 증감이 0입니다.

함정 히트스톱과 슬로모션이 서로를 취소하는 문제를 단순 참조 카운팅으로 고치면 더 나쁜 버그가 됩니다. 슬로모션 중 히트스톱이 겹치면 time_scale이 0으로 눌린 뒤 복원되지 않아 게임이 멈춥니다. ‘만료 시각을 갖는 항목 중 최저 배율’ 방식으로 해결했고, time_scale이 0이면 delta도 0이 되므로 만료 판정은 실시간 기준으로 했습니다.
08

P5 · 아트

픽셀아트 게임인데 텍스트만 벡터 폰트였다

가장 큰 발견은 픽셀 폰트가 거의 쓰이지 않고 있었다는 것입니다. Galmuri9 참조처가 데미지 숫자 한 곳뿐이었고, 나머지 게임 전체는 엔진 내장 폰트로 렌더됐습니다.

정정

Galmuri9의 선명한 크기는 9의 배수가 아니라 10의 배수다

진단 보고서는 “9의 배수(9·18·36)”라고 적었지만 틀렸습니다. 폰트를 실제로 래스터라이즈해 측정한 결과, 부분 커버리지(회색) 픽셀 비율이 10·20·30·40px에서 정확히 0.00%, 9px에서 87.99%였습니다. unitsPerEm=1000이고 글리프가 100 유닛 그리드에 놓여 있기 때문입니다.

조치 — 전역 폰트 적용 + 안티에일리어싱 해제 + 메인 메뉴 폰트 크기를 10의 배수로. 메인 메뉴의 흐린 픽셀 비율이 45.89% → 0.00%가 됐습니다.

첫인상

배경은 3색 타일을 12.8배 늘린 것, 로고는 아이템 아이콘 팩의 파일 하나였다

정작 assets/sprites/backgrounds/에 360×640 네이티브 배경 5장이 쓰이지 않은 채 있었습니다. 네이티브 배경으로 교체하고, 앱 아이콘과 타이틀 엠블럼을 기존 보스 스프라이트에서 4배 정수 NEAREST로 새로 만들었습니다.

픽셀 그리드

몬스터 24/24종이 소수 배율로 렌더됐다

sprite_scale 0.8~0.9는 1px 아웃라인을 끊고 idle 부유 중 지글거리게 만듭니다. 전부 1.0으로 되돌리고 위치도 정수 픽셀로 스냅했습니다.

제약 PixelLab 크레딧이 0이라 새 이미지를 한 장도 생성하지 못했습니다. 그래서 이번 아트 작업은 전부 “있는 것을 제대로 쓰기”였습니다 — 미사용 배경 투입, 폰트 배선, 픽셀 그리드 복구, 캔버스 접지선 통일. 새 아트가 필요하면 크레딧 충전이 선행되어야 합니다.
09

방법론

21개 에이전트를 파일로 갈라 붙였다

여러 에이전트가 같은 파일을 동시에 고치면 서로의 작업을 덮어씁니다. 그래서 파일 소유권을 겹치지 않게 배정하고, 경계를 넘는 연결은 계약으로 넘겨받아 직접 처리했습니다.

잘 작동한 것

  • 파일 분리 배정 — 4명이 동시에 작업해도 충돌 0건
  • godot 실행 금지 — 동시 실행이 임포트 캐시를 깨뜨리는 것을 막았다
  • api_contract 보고 — 경계를 넘는 호출을 명시적으로 넘겨받아 통합했다
  • 검증 게이트 — 세 검사를 통과해야만 커밋

사고 — 기록해 둘 것

작업 중 세션 한도에 걸려 검토자와 다음 단계 전원이 실패했습니다. 그 결과 검증되지 않은 변경 45개 파일이 작업 트리에 그대로 남았습니다 — 가장 위험한 상태입니다.

직접 검증해 복구했고 다행히 통과했지만, 교훈은 분명합니다: 병렬 워크플로우는 검증 단계까지 끝나야 안전합니다. 구현만 끝난 상태로 두면 안 됩니다.

검증이 잡은 것 검토자들이 설계안의 실제 결함을 잡아냈습니다 — 제안된 셰이더는 피벗이 중앙이라 동작하지 않았고, 제안된 시간배율 토큰화는 원래 버그보다 나쁜 버그를 만들었으며, 텔레메트리 설계는 permissions/internet=false를 한 번도 언급하지 않았습니다. 설계는 진단보다 틀리기 쉽습니다 — 진단은 있는 것을 읽지만 설계는 없는 것을 상상하기 때문입니다.
10

P7 · P8

시간이 남아 두 라운드를 더 돌렸다

P7 마감

인스펙터가 실제로 게임을 바꾼다

GameConfig 배선율 36% → 62%. 무결성 검사의 경고가 사라졌습니다. 소비처가 아예 없는 유령 필드는 지웠습니다 — “설정처럼 보이는 죽은 값”은 없는 게 낫습니다.

폰트 크기 65곳을 10의 배수로 스냅하되, 실제 문자열 131개를 실측해 컨테이너에 들어가는 것만 키웠습니다. 넘치는 것은 키우지 않고 내렸습니다.

P8 심화

세이브가 더는 파괴되지 않는다

진단에서 critical 이었는데 미착수였던 항목입니다. 파싱 실패 시 세이브를 즉시 영구 삭제하고 있었습니다 — 앱이 강제 종료돼 파일이 잘렸을 뿐인데 진행도가 사라졌습니다.

원자적 쓰기(.tmp → 검증 → .bak → rename), 손상 시 격리 후 백업 복구, 마이그레이션 체인, 타입 검증을 넣었습니다.

자기 회귀

이번 버전이 스스로 만든 문제를 잡았다

최종 스테이지를 20층으로 정하는 순간, 해금 조건이 도적 50층 · 마법사 100층 · 요정 30층인 것들이 영구 도달 불가가 되었습니다. 고치는 과정에서 새 결함을 만든 것입니다.

수정 — 클리어 횟수라는 새 축(total_clears)을 만들어 도적=첫 클리어 / 마법사=3회 / 불사조=5회로 재설계했습니다. 20층 상한에서 스테이지 축은 해상도가 없지만 클리어 횟수는 상한이 없고 이번 버전의 목표와 직결됩니다. 기존 유저의 해금 상태는 마이그레이션으로 승계합니다.

콘텐츠 아이템 11 → 24, 유물 12 → 24로 늘렸습니다. assets/items 에 아이콘이 677장 있는데 42장만 쓰이고 있어서 아트 비용 없이 가능했습니다. 기존 희귀도 분포와 수치 범위 안에서만 추가해 밸런스는 그대로입니다(보스10 90% / 보스20 54% / 클리어 49%).
11

정직한 현재 위치

아직 하지 않은 것

이번 작업으로 게임은 완주 가능해졌지만 재미있어졌는지는 아직 모릅니다. 그 질문에는 사람이 플레이해야만 답할 수 있습니다.

확인되지 않은 것

  • 실기기 플레이가 없다. 모든 수치는 시뮬레이터와 헤드리스 스모크 테스트 기반입니다. 손가락으로 눌러본 사람이 아직 없습니다.
  • 전역 폰트 교체의 레이아웃 영향을 전 화면에서 눈으로 확인하지 못했습니다. 텍스트 폭이 달라지면 컨테이너가 넘칠 수 있습니다.
  • 웹 캔버스 정렬. 헤드리스 브라우저 캡처에서 캔버스가 오른쪽으로 밀려 상단 버튼이 잘리는 것처럼 보였습니다. 셸의 CSS 에는 오프셋이 없어 소프트웨어 렌더링 중 캡처된 아티팩트로 보이지만 확증하지 못했습니다. 실제 브라우저에서 한 번 확인이 필요합니다.
  • 보상 카드 구조가 바뀌어 스테이지당 수입이 달라졌는데, 시뮬레이터는 보상 카드를 모델링하지 않습니다.

다음에 할 것

  • BGM. 배관은 깔았지만 곡이 0개입니다. 파일만 넣으면 즉시 동작합니다.
  • 몬스터 애니메이션이 여전히 1프레임입니다. 개체별 idle 움직임은 넣었지만 스프라이트 프레임은 생성 도구 없이는 못 만듭니다.
  • 실기기 성능 확인. 파티클과 전역 폰트가 저사양 안드로이드에서 어떤지 모릅니다.
  • 외부 플레이테스트. 모르는 사람에게 폰을 건네고 10분을 관찰하는 것 — 이것이 다음 보고서의 데이터 소스가 되어야 합니다.
지금 바뀐 게임을 이 사이트에서 바로 플레이할 수 있습니다. 전송량 15.6MB, 설치 없이 브라우저에서 실행됩니다.