$

케이스 11 · 호러

Keep Watching — 규칙서 전체를 손전등 안에 넣은 호러 게임

웹 포털용 1인칭 호러. 규칙이 하나 남을 때까지 계속 잘라냈다 — 조각상을 시야에 두면 멈추고, 눈을 떼면 움직인다. 잠든 사람을 터는 코미디 스텔스로 시작해 절도 경제를 지우고, 위협을 룩어웨이 개체로 바꾸고, 조명을 다 껐다. 그러자 개체 자체가 문제가 됐다 — 걷고 손을 뻗어야 하는 생성 실사 인간은 언캐니 밸리로 떨어진다. 그래서 몸을 '원래 얼어붙은 얼굴이어야 하는 것' — 조각상으로 갈아치웠다. 우는 천사 한 방 가득, 그중 일부만 살아있고 장식과 똑같이 생겼다. 거기서부터 작업은 규칙이 놓인 그릇 전체로 옮겨갔다 — 조명만 바꾼 같은 침실 대신 다섯 개의 다른 공간, 추측이 아니라 실측한 난이도 곡선, 엔딩, 그리고 침실 문이 왜 예배당으로 열리는지 설명하는 세계관. 지금은 Keep Watching: Part I으로 배포돼 있다.

  • Three.js
  • Game Design
  • AI 3D Pipeline
  • Procedural Animation
  • Web Portal

$

문제의식 — 호러는 클립 장르다

웹 포털에서 호러는 클립으로 퍼진다. 누군가의 불빛이 그것에 닿는 15초가 곧 마케팅이다. 그래서 설계 기준을 여기에 맞췄다 — 60~90초 런 안에서, 언어 없이, 입력 하나로 그 순간을 안정적으로 만들어내야 한다. 15초 클립에서 살아남지 못하는 건 전부 잘랐다.

컨셉 — 빔이 곧 시선

규칙은 어디에도 적혀 있지 않고 물리로만 작동한다. 개체는 손전등 빔이 닿는 동안만 멈춘다. 불을 끄면 정면으로 노려보고 있어도 다가온다. 그래서 빛이 진행과 방어로 쪼개 쓰는 단일 자원이 된다 — 카펫을 뒤지면 방어가 반대편으로 돌아가고, 빔으로 붙들고 있으면 진행이 없다. 관찰 판정 콘을 실제 빔 폭에 맞춰 조여(60°→38°) 빔 밖인데 멈추는 경우를 없앴다. 조준과 방어가 같은 동작이다.

세 번 지워 한 문장으로

  1. 1 · 코미디 스텔스 → 전제가 무너지다

    V1은 '잠든 사람을 매트리스까지 턴다'였다. 웃겼지만 집이 무서워지는 순간 전제가 깨졌다 — 도둑이 왜 흉가를 계속 탐험하겠는가? 깨어나면 즉사하는 규칙도 긴장이 아니라 불공정으로 읽혔다. 돈·상점·가격으로 짠 절도 경제를 통째로 지웠다.

  2. 2 · 눈을 떼면 움직인다 → 규칙이 몸을 얻다

    피벗 — 방 하나, 개체 하나, 규칙 하나. 안 보면 움직인다. 생성한 GLB에 애니메이션 클립이 없어서, 조각상처럼 미끄러지던 걸 절차 본 애니메이션으로 바꿨다. 고관절과 무릎이 접히고 팔을 뻗으며 걷다가, 시선에 걸리면 그 자세 그대로 멈춘다. 접근 벡터를 셋으로 뒀다 — 바닥 직립·벽 거꾸로·천장 크롤 — 그래야 시선 스캔이 위·옆까지 정직해진다.

  3. 3 · 완전 암전 → 규칙이 물리가 되다

    여기서 조명을 다 끄고 오로지 손전등으로만 플레이하도록 정했다. 이 결정으로 시선과 빛이 하나의 물체, 빔으로 합쳐졌다. 손전등은 찾는 아이템에서 시작 장비가 됐고(암흑에선 손전등을 찾을 수 없다), 달빛과 램프를 껐고, 그것은 이제 내 빔 안에서 제 그림자를 드리운다. 클립 순간은 저절로 생겼다 — 불을 끄고, 잠깐 기다리고, 다시 켜면 더 가까이 와 있다.

지워진 캐스팅 — 다섯 거주자

하나의 개체로 통합하기 전에는 스테이지마다 고유한 거주자와 고유한 '텔'(안전하게 움직일 타이밍을 읽는 신호)이 있었다. 다섯 전부 컨셉→3D 생성→리깅까지 직접 만들었고, 지금도 디버그 플래그 뒤에 코드로 남아 있다.

  • 그것 (The Thing) — 텔: 한쪽 눈

    그것 (The Thing) — 텔: 한쪽 눈

    1스테이지. 평범한 몸처럼 누워 있지만 한쪽 눈이 떠 있다. 훔칠수록 사람이 아니게 변형됐다. 유일하게 계속 변이하던 거주자여서, 이걸 최종 개체의 씨앗으로 삼았다.

  • 수집가 노파 (The Hoarder) — 텔: 코골이

    수집가 노파 (The Hoarder) — 텔: 코골이

    2스테이지. 유일하게 코를 고는 거주자 — 날숨에 맞춰 움직였다. 흰 머리와 잠옷 그대로 초기 룩어웨이 빌드까지 올라왔다가, 검은 머리가 더 무섭다고 판단해 교체했다.

  • 키 큰 것 (The Tall One) — 텔: 순찰

    키 큰 것 (The Tall One) — 텔: 순찰

    3스테이지. 제자리에서 자지 않고 순찰한다. 숨소리 대신 동선을 읽어야 했다. 이 캐릭터 때문에 넣은 순찰 로직이 나중에 추격 시스템이 됐다.

  • 아이 (The Child) — 텔: 야등

    아이 (The Child) — 텔: 야등

    4스테이지. 야등이 꺼진 동안만 움직인다 — 빛이 곧 타이밍 신호다. 돌아보면 최종 게임이 딛고 선 '빛이 규칙이다'를 처음 스케치한 캐릭터였다.

  • 불면증 여자 (The Insomniac) — 텔: 정지 응시

    불면증 여자 (The Insomniac) — 텔: 정지 응시

    5스테이지, 최고 난도. 거의 잠들지 않는다. 가만히 앉아 응시하다가, 돌아설 때만 움직일 수 있다. '정지가 곧 위협'이라는 감각이 룩어웨이 개체의 프리즈로 그대로 이어졌다.

AI 에셋 파이프라인 — 모델이 호러를 거부할 때

실사 호러 인물은 주류 이미지 모델이 정확히 거부하는 대상이다. 통한 파이프라인은 이랬다 — 인물 특화 모델(soul_2)로 검은 머리 잠옷의 수척한 형상을 뽑고 → 리깅 켠 image-to-3D → Mixamo 규격 골격이라 기존 포즈 로직에 방향 보정 없이 그대로 물렸고 → WebP 압축 GLB(8.6MB→2.1MB). 인테리어 표면(벗겨진 벽·얼룩진 천장·삭은 문)은 별도 텍스처 패스로 만들었다. 머리를 검은색으로 하려는데 텍스처가 이미 한 장으로 구워져 있어서, 색만 덧입히지 않고 인물을 다시 생성했다 — 룩을 깨끗하게 유지하는 유일한 방법이었다.

이미지 한 장이 걷기까지 — 3D 작업

  • 본 축 실측 — 단일 본 6방향 스윕

    본 축 실측 — 단일 본 6방향 스윕

    생성 리그는 축을 문서화해 주지 않는다. 그래서 실측한다 — 본 하나에 여섯 방향 회전을 걸고 컷마다 확인. 이 리그에선 Z축이 팔을 든다는 걸 이 프레임에서 찾았다. 걸음은 추측이 아니라 실측한 축으로 조립했다.

  • 절차 걸음 + 리치 — 클립 없는 뼈들이 걷는다

    절차 걸음 + 리치 — 클립 없는 뼈들이 걷는다

    GLB엔 애니메이션 클립이 없다. 걸음은 순수 코드다 — 고관절과 무릎이 교대로 접히고, 팔이 움켜쥐려 올라오고, 머리가 갸웃한다. 그리고 빔이 닿는 순간 그 자세 그대로 얼어붙는다.

어둠 속에서

  • 눈을 뜬 순간 — 빔 서클 밖은 온통 어둠

    눈을 뜬 순간 — 빔 서클 밖은 온통 어둠

    완전 암전에 17° 원뿔 하나. 문틈만 희미하게 빛난다 — 눈에 보이는 유일한 출구다.

  • 빔에 걸린 그것 — 멈춘다

    빔에 걸린 그것 — 멈춘다

    빔이 닿으면 정지(실측 표류 0.000m). 정지가 절대적이라, 시선을 떼는 값이 비싸게 느껴진다.

  • 다시 켜면 — 코앞

    다시 켜면 — 코앞

    다시 켜면 — 침대 위, 팔을 뻗은 채. 이 소등/재점등 비트가 설계 전체가 복무하는 15초 클립이다.

공정성 — 디자인으로 가르치고 하네스로 검증한다

첫 플레이테스트에서 개체가 신규 플레이어를 약 1.5초 만에 잡았다 — 튜토리얼 문장이 뜨기도 전에. 텍스트 경고가 아니라 구조로 고쳤다. 처음 눈이 마주치기 전까지 미동도 없게 해서 눈맞춤 자체가 규칙을 가르치게 하고, 아이템을 확보할수록 속도가 오르게 했다. 루프에 대한 주장은 전부 실제 빌드를 상대로 한 Playwright 하네스에서 측정한다 — 정지 표류 0.000m, 암흑 접근 거리, 검거, 리트라이 상태, 탈출 — 그리고 수정은 당일 커밋한다.

플레이로 다듬다 — 잡힘을 빚은 루프

룩어웨이 코어를 내보낸 뒤 직접 반복해서 플레이했고, 매 세션마다 구조적인 걸 하나씩 바꿨다. 풀스크린 사진 점프스케어는 과하게 느껴져서 더 조용하고 더 무서운 잡힘으로 바꿨다 — 개체가 다가와 팔을 벌리고 뻗고, 그 손이 실제로 몸에 닿을 때만 게임이 끝난다. 고어 없이 급암전으로. 플레이해 보니 안 비추면 가만히 있어서 활성화가 너무 소심했는데, 이제 어둠이 몇 초 안에 그것을 깨운다. 뻣뻣한 걷기 사이클은 소리 없는 활공으로 교체했다. 그리고 매 패스마다 방을 키웠다 — 작은 침실 하나로 시작한 공간이 지금은 약 세 배다. 그것이 도착하기 전에 탐색이 숨 쉴 여유를 갖도록. 이 중 기획서에서 나온 건 없다. 직접 빌드를 플레이하면서 무엇이 공포를 깨뜨리는지 물어서 나온 것들이다.

피벗 — 애니메이션을 입혀야 하는 얼굴에서, 원래 얼어붙어 있어야 하는 얼굴로

실사 형상은 처음부터 끝까지 나와 싸웠다. '안 보면 움직인다'를 팔려면 그것이 걷고, 돌고, 손을 뻗어야 했는데 — 생성된 인간이 그러면 언캐니 밸리에 빠지거나, 더 나쁘게는 우스꽝스러워졌다. 룩어웨이 규칙에 필요한 건 사실적인 몸이 아니라, 완벽히 정지해 있을 때 섬뜩한 몸이다. 조각상이 정확히 그렇다 — 원래 얼어붙어 있어야 하는 형상, 움직이면 안 되기에 오히려 불안한 것. 닥터후의 우는 천사가 이걸 내가 이미 만들어 둔 규칙으로 정리해 줬다: 보는 순간 멈추고 안 보면 다가오는 석상. 유령을 조각상으로 갈아치우자 모든 문제가 한 번에 사라졌다 — 입힐 표정도, 팔아야 할 걷기 사이클도, 기어 나와야 할 밸리도 없다. 정지는 더 이상 기술적 실패가 아니라 캐릭터 그 자체가 됐다.

규칙은 두고, 몸을 바꾼다

  1. 1 · 유령 → 우는 천사 — 코드는 거의 그대로

    '관찰됨'은 이미 '손전등 시야 안'을 뜻했다. 같은 정지-전진 로직을 조각상에 겨눴다 — 빔 안에선 돌이고, 눈을 떼는 순간 다가온다. 코어는 거의 안 바뀌었다. 바뀐 건, 이제 정지가 말이 된다는 것. 먼저 조잡한 프리미티브 조각상으로(크레딧 0) 메커닉을 증명하고, 그다음 실사 우는 천사를 생성해 갈아끼웠다.

  2. 2 · 거리별 얼굴 전환 — 공포가 근접에 비례해 커진다

    사진 점프스케어 하나는 단발 비트다. 대신 천사는 멀리선 얌전한 얼굴을, 손이 닿는 거리에 들면 달려드는 일그러진 얼굴로 전환한다 — 다가올수록 공포가 커지고, 그 전환 자체가 스케어가 된다. 메시 둘을 거리로 스위칭.

  3. 3 · 한 방 가득 — 의태(擬態)

    진짜 도약은 이거였다: 방을 조각상으로 채우고 그중 일부만 살아있게 한다. 장식과 똑같이 생겨서 위협과 가구를 구분할 수 없다 — 유일한 단서는 '방금 저게 더 멀리 있지 않았나?'. 등장 수·살아있는 것·위치가 매판 랜덤이라 같은 판이 없고, 외운 길이 안전하지 않다.

리네임 — 이름이 게임을 따라가야 했다

「그 집(The Wrong House)」은 절도라는 전제 — 침입한 집 — 에서 나온 이름이었다. 하지만 그 전제는 첫 컷에서 이미 지웠고, 이제 위협은 집 안의 유령이 아니라 한시도 눈을 뗄 수 없는 조각상이었다. 이름이 게임을 따라가야 했다. 「Keep Watching — don't look away」는 규칙 전체를 두 단어로 말한다. 커밋 전에 CrazyGames에 동일 타이틀이 없는지 확인하고, 전부 리네임했다 — 레포·빌드·배포·이 케이스 스터디까지.

지금의 게임 — 마주 보는 조각상들

룩어웨이 규칙을, 조각상과 믿을 수 없는 방을 중심으로 다시 세웠다. keep-watching.vercel.app에 배포된 실제 게임 — 이 사이트에서 바로 플레이할 수 있다.

  • 프로토타입 — 크레딧 0의 프리미티브 조각상

    프로토타입 — 크레딧 0의 프리미티브 조각상

    생성에 한 푼 쓰기 전에, 메커닉부터 증명하려고 조잡한 프리미티브로 조각상을 세웠다 — '보면 멈추고 안 보면 다가온다'가 석상에서도 무서운가? 무서웠다. 그제서야 실사 천사를 생성했다.

  • 군중 + 의태 — 어느 게 살아있나

    군중 + 의태 — 어느 게 살아있나

    똑같이 생긴 우는 천사 한 방. 일부는 살아있고 일부는 장식이며, 하나가 움직이기 전까지 구분할 수 없다. 피벗 전체가 이 긴장을 위한 것이었다.

  • 덮침 — 얌전한 얼굴에서 달려드는 얼굴로

    덮침 — 얌전한 얼굴에서 달려드는 얼굴로

    손이 닿는 거리에 들면 얌전한 얼굴이 이걸로 바뀐다. 두 번이나 지나쳤던 그 조각상이, 지금 당신을 잡으려는 놈이다.

  • 매판 랜덤 — 같은 방, 다른 판

    매판 랜덤 — 같은 방, 다른 판

    천사 수·살아있는 것·위치가 매판 랜덤이다. 방을 외워도 소용없다 — 위협은 매번 다른 곳에 있다.

방 하나로는 부족했다 — 그리고 나는 스스로를 속이고 있었다

몇 주 동안 '맵 변주'를 내보내고 있었다 — 조명 테마 5종, 장애물 배치 5종, 라운드마다 움직이는 가구. 오너가 맵의 컨셉 자체가 바뀐 게 맞냐고 물었을 때 직접 확인했고, 정직한 답은 아니오였다. 벽·바닥·천장·소품 구성·문 위치가 1방부터 5방까지 같은 침실이었다. 다른 건 빛뿐이었다. 변경 로그를 자기가 쓰면 스스로를 설득하기 쉬운 종류의 착각이다 — 변주는 분명 있었지만, 정작 중요한 축에서는 한 번도 없었다. 그래서 제대로 기획했다 — 다섯 장소의 컨셉아트, 설계안, 승인, 그다음 구현.

다섯 개의 장소, 하나의 규칙

평면·출구 문·루프는 다섯 곳 모두 동일하다 — 규칙을 다시 배울 일이 없다. 나머지는 전부 교체된다: 텍스처, 천장 높이(2.45m~4.6m), 소품, 뒤질 수 있는 다섯 곳, 그리고 석상이 서는 자리.

  • 01 침실 — 석상이 있으면 안 되는 곳

    01 침실 — 석상이 있으면 안 되는 곳

    온보딩 방 — 살아있는 석상 1개, 장식 0. 여기선 석상이 명백히 이질적이고, 그래서 규칙을 가장 빨리 가르친다.

  • 02 폐예배당 — 석상이 원래 있어야 할 곳

    02 폐예배당 — 석상이 원래 있어야 할 곳

    문을 넘는 순간 천장이 2.6m에서 4.2m로 열린다. 의태가 작동하기 시작하는 곳 — 예배당에 석상은 원래 있는 것이라 '장식인가 위협인가'가 진짜 질문이 된다.

  • 03 폐병동 — 커튼이 시야를 끊는다

    03 폐병동 — 커튼이 시야를 끊는다

    01의 침대가 여섯 번 반복되고 전부 비어 있다. 기믹은 커튼 — 시야를 물리적으로 끊어서, '시야에 두라'는 규칙을 처음으로 진짜 압박한다.

  • 04 공장 — 석상을 만들던 곳

    04 공장 — 석상을 만들던 곳

    스토리의 열쇠를 대사 한 줄 없이 말한다 — 완성 단계가 다른 석재 4개를 실은 컨베이어(왼쪽은 그냥 돌, 오른쪽은 이미 얼굴을 가리고 있다)와 사람 모양 거푸집 3개.

  • 05 납골당 — 배경과 위협이 같아진다

    05 납골당 — 배경과 위협이 같아진다

    가장 높은 두 곳 뒤에 오는 가장 낮은 천장(2.45m). 벽 전체가 아치 벽감이고 벽감마다 석상이 서 있어서, 위협과 건축이 말 그대로 같은 물건이 된다. 메커닉의 종점이다.

난이도를 추측하지 않고 실측했다

곡선이 맞느냐는 물음에 감으로 답하고 싶지 않아서, 숫자 하나를 재는 하네스를 만들었다 — 모든 석상을 활성화한 뒤 등을 돌리고 가만히 서 있을 때 몇 초를 버티는가. 1방: 26초 동안 안 잡힘. 3방: 3.3초. 5방: 7.1초. 8방: 2.1초. 코드를 확인하니 숫자가 시사한 대로였다 — 살아있는 수와 장식 수가 둘 다 5방에서 상한이고, 속도 항은 탐색 3곳만 해도 1방부터 이미 상한이다. 6방·7방·20방이 수학적으로 동일했다. 리더보드는 실력이 아니라 인내심을 재고 있었다. 속도도 해법이 못 된다 — 석상은 플레이어의 74%로 움직이고, 더 올리면 탈출이 불가능해진다(이미 한 번 고친 데스 스파이럴). 그래서 상승은 개수와 작업량에서 와야 했고, 방어할 수 없는 곡선을 내보내는 대신 그렇게 말했다.

침실 문이 왜 예배당으로 열리는가

다섯 개의 뚜렷한 장소는 새 문제를 만들었다 — 아무것도 그걸 설명하지 않았다. 빌드에서 실제로 발화되는 서사 문장을 전수 조사하니 일곱 개였다. 깨어남 1, 장소 부제 5, 결과화면 1. 그중 어느 것도 전환을 다루지 않았다. 더 나쁜 건, 상시 떠 있는 목표바가 납골당에서도 '카펫·옷장·액자를 뒤져라'라고 했고, 잠긴 문을 두드리면 몇 달 전에 지운 게임의 현관 매트를 들춰 보라고 했다. 세계관은 첫 문제를 풀었고, 저 두 줄은 그냥 버그였다. 돈이 들지 않는 답은 이랬다 — 다섯 장소는 다섯 장소가 아니다. 한 시설의 다섯 공정 단계다(수거·보관·준비·경화·적치). 그리고 문은 출구가 아니라 다음 공정으로 넘기는 인계다. 당신은 탈출한 적이 없다. 처리되고 있었다. 순서를 하나도 바꿀 필요가 없었고, 4방의 컨베이어가 픽션의 심장이 됐으며, 엔지니어링 제약(동일한 평면)이 전제가 됐다 — 같은 시설, 같은 규격 모듈.

여섯 단어가 세계를 세운다

설명은 없다. 모든 스테이지가 출구 문 옆 같은 자리에 같은 규격의 명패를 달고 있고, 나가는 길에 반드시 지나친다. 01 INTAKE는 그 자체로는 아무 뜻이 없다 — 4방에서 컨베이어 옆의 SETTING을 읽을 때 도착한다. 명패가 다섯 곳에 똑같이 있다는 사실 자체가 여기가 한 건물이라는 증거다.

  • 02 HOLDING — 보관

    02 HOLDING — 보관

    예배당 출구 옆. 석상이 제자리에 있는 것처럼 보이는 건 실제로 그렇기 때문이다 — 수거한 것을 두는 칸이다.

  • 04 SETTING — 경화

    04 SETTING — 경화

    나머지 다섯 단어를 도착시키는 단어. 반쯤 만들어진 사람을 실은 컨베이어에서 3미터 떨어진 곳에서 읽는다.

  • 접수된 예배당 — 아치 천장을 가로지르는 작업등

    접수된 예배당 — 아치 천장을 가로지르는 작업등

    예배당과 납골당이 둘 다 돌·아치라 같은 방으로 읽힐 위험이 있었다. 해법 — 시설이 이 예배당을 접수해 창고로 쓴다. 아치 천장을 가로지르는 작업등, 석벽에 클램프로 물린 배선관, 긴 의자마다 붙은 재고 번호표. 성스러운 공간에 박힌 산업 하드웨어는 그 자체로 서늘하고, 두 방을 깨끗하게 갈라 놓는다.

  • 독백 — 화자 없는 말풍선을 지우다

    독백 — 화자 없는 말풍선을 지우다

    메시지가 꼬리 달린 말풍선으로 떠서, 방 안의 누군가가 말하는 것처럼 보였다. 꼬리만 떼면 게임 피드백과 이야기가 같은 무게가 되므로 둘을 갈랐다 — 피드백은 작은 토스트로, 서사는 이렇게. 세계가 뒤로 물러나고 생각만 남는다. 새 장소 직전엔 한 줄이 먼저 뜬다: STILL INSIDE.

Part I에는 끝이 있다

다섯 곳은 게임 하나로는 짧게 느껴졌고, 정직한 선택지는 열 개를 만들거나 다섯을 1부로 규정하는 것이었다. 장소 다섯 개를 더 만드는 건 비용이 가장 크고 얻는 게 가장 적다 — 메커닉은 납골당에서 종점에 닿으므로 6~10장은 배경만 다른 같은 난이도가 된다. 그 예산을 엔딩과, 엔들리스가 실제로 어려워지게 만드는 데 썼다.

  • 컷 A — 나선 순간

    컷 A — 나선 순간

    이 엔딩을 처음엔 3D로 만들었는데 끝내 플레이 화면으로 보였다. 생성 스틸이 맞는 판단이었다 — 마지막 그림이 내가 박스로 짠 방이어서는 안 된다. 등 뒤 문에서 아직 새는 따뜻한 빛, 화면 아래 자기 발끝, 그리고 네 번의 STILL INSIDE가 준비해 온 그 단어.

  • 컷 B — 무덤이 아니라 야적장

    컷 B — 무덤이 아니라 야적장

    폭로는 석상이 많다는 게 아니다. 규칙적인 열로 지평선까지 늘어서 있다는 것 — 무덤이 아니라 재고다. 여기까지 오며 01 INTAKE부터 05 STORAGE까지 읽은 사람에게, 그 규칙성이 문 옆 마지막 명패의 뜻을 확정한다. OVERFLOW.

탐색에 추론이 없었다

탈출은 열쇠가 나올 때까지 다섯 곳을 뒤지는 것이었다. 그건 방탈출이 아니라 정지 조건이 무작위인 루프고, 공간을 이해하든 말든 똑같이 흘러간다. 그래서 열쇠를 체인 뒤로 넣었다 — 다섯 곳 중 한 곳에 두 자리 번호가 적힌 전표가 있고, 방에는 번호가 찍힌 잠금상자가 셋, 맞는 상자는 다이얼이 2.2초 돈다. 그 2.2초가 핵심이다. 상자를 마주 본다는 건 방에 등을 돌린다는 뜻이고, 그것들은 아무도 안 볼 때만 움직인다. 번호는 방마다 새로 굴려 외울 수 없다.

  • 잠금상자 — 게임 조명(손전등만)

    잠금상자 — 게임 조명(손전등만)

    번호판에는 emissiveMap을 얹었다. 같은 함정에 이미 한 번 당했다 — 만족스러웠던 공정 명패가 가장 어두운 스테이지에서는 읽히지 않았다. 어두운 방은 표면에 반사할 빛 자체를 거의 안 준다. 그래서 이번엔 메커닉을 다 짜기 전에 납골당 조명으로 먼저 찍어 봤다.

  • 예배당 측랑 — 비우고 나서

    예배당 측랑 — 비우고 나서

    테스트 실패 하나를 고치다 여기까지 왔다. 석상 자리는 손으로 찍는데, 그중 상당수가 그 공간의 붙박이 안에 들어가 있었다 — 에러도 경고도 없이 조용히 제외됐다. 실측하니 병동은 살아있는 석상이 최대 3개, 예배당은 5개. 곡선이 요구하는 건 8이다. 엔들리스는 어려워지지 않고 있었고, 예배당은 스토리 마지막 방보다 오히려 쉬웠다. 코드 주석은 측랑이 있다고 말하는데 의자는 벽까지 닿아 있었다. 주석이 말하던 측랑이 실제로 생기도록 의자를 줄였다.

나는 없는 메커닉을 문서에 적고 있었다

위 챕터는 체인을 만든 날 쓴 것이고, 다이얼 2.2초가 핵심인 이유는 그동안 방에 등을 돌리기 때문이라고 적혀 있다. 나는 그걸 커밋 메시지에도, 프로젝트 위키에도, 보고에도 썼다. 그러다 더 보완할 게 없냐는 질문을 받았고, 답하는 대신 한 번도 실제로 확인하지 않은 것부터 뒤졌다. 다이얼은 한 줄이었다. dialT += dt. 알아서 흘러갔다. 상자를 탭하고 뒤돌아 방을 감시해도 공짜로 열렸다. 대가가 없었다. 나는 의도를 적은 주석 — “이 게임 최고의 취약 구간” — 을 써 놓고, 그 주석을 메커닉이 존재한다는 근거로 인용하고 있었다.

  • 다이얼은 이제 상자를 보고 있을 때만 감긴다

    다이얼은 이제 상자를 보고 있을 때만 감긴다

    다이얼은 이제 상자가 카메라 프러스텀 안에 있는 동안에만 감긴다 — 석상이 감시당하고 있는지 판정하는 바로 그 검사다. 규칙 하나를 두 곳에 쓴다. 상자를 시야에 두는 것과 그것들을 시야에 두는 것은 동시에 안 된다. 눈을 떼도 즉시 실패시키진 않고 0.6배로 되감는다 — 석상이 다가와 어쩔 수 없이 돌아봐야 하는 순간에 하드 페일이면 교착이 되기 때문이다. 문구보다 먼저 알아채는 건 다이얼 딸깍 소리가 끊기는 것이다.

  • 전표 — 토스트가 아니라 읽는 종이

    전표 — 토스트가 아니라 읽는 종이

    두 번째 격차는 작지만 종류가 같았다. 요청은 방탈출이 쪽지를 건네듯 종이에 적힌 힌트였는데, 나는 스쳐 지나가는 토스트를 내놨다. 쪽지 UI는 이미 코드에 있었다 — 지금은 없어진 규칙 괴담 컨셉용으로 만들어져 쓰이지 않고 있었다. 그걸 되살리니 번호가 종이에 남고, 머리에도 남는다. 이 종이는 부르지 않아도 뜨므로 떠 있는 동안 시뮬레이션을 멈춘다. 팝업이 시야를 가려서 죽는 건 이 게임이 다루는 위험이 아니다.

마지막 1마일에서 실패는 조용해진다

출시 직전에 세 가지가 잘못됐고, 셋 다 스스로를 알리지 않았다. 타이틀이 뜨는 데 5.9초가 걸렸고 나는 처음에 6.9MB 페이로드 탓이라고 봤다 — 아니었다. DOM은 386ms에 준비됐고, 4686ms짜리 단일 메인스레드 블록이 페인트를 붙들고 있었다. 씬 생성이 타이틀 뒤로 미뤄진 적이 한 번도 없어서, 브라우저는 그릴 수 있는 게 없었다. 게임을 import하기 전에 두 프레임을 양보하니 첫 페인트가 68ms가 됐다. 그리고 그 수정이 자기 버그를 낳았다 — 게임이 로드되기 전에 도착한 PLAY 탭을 받아 두는 코드를 모듈 중간에 뒀는데, 그 아래 const 선언들이 아직 TDZ라 부팅이 통째로 죽었다. 타이틀은 68ms에 뜨고 게임은 시작되지 않는다. 스크린샷으로는 성공처럼 보이는 실패다. 그 기능을 위해 쓴 테스트가 그걸 잡았다.

  • 내비게이션 +300ms — 같은 자리에 전에는 검은 화면이 5.9초 있었다

    내비게이션 +300ms — 같은 자리에 전에는 검은 화면이 5.9초 있었다

    나머지 둘은 플레이어에게 도달하지 않았고, 그게 이걸 적어 두는 유일한 이유다. 제출 zip을 만들면 dist/.env.local이 포털로 넘어갈 뻔했다 — Vercel 토큰이고, zip -r가 숨김 파일을 담기 때문에 딸려 들어간다. 패키징 스크립트는 이제 아카이브 안에 .env나 .vercel이 있으면 실패한다. 그리고 배포는 성공했다고 출력했는데 라이브는 옛 빌드 그대로였다. 번들러가 매 빌드마다 dist를 비우면서 Vercel 프로젝트 링크까지 지웠고, CLI는 조용히 디렉터리 이름으로 새 프로젝트를 만들어 거기에 배포하고 있었다. 이제 배포는 먼저 다시 링크하고, 라이브 번들 해시를 로컬과 대조한 뒤에야 끝났다고 말한다.

$

근거 · 깃·위키 인용

  • 소스 (game-lab 모노레포) · github:sh-binx/game-lab
  • 재설계 아크 R25–R69 — 조각상 피벗·다섯 스테이지·Part I 엔딩 (game-lab 위키) · game-lab/wiki/games/keep-watching.md
  • 스테이지 시스템 — 장소 하나당 StageDef 하나 · git:edb2988
  • 난이도 실측 하네스 · keep-watching/tools/_difficulty.mjs
  • 세계관 층 검수 (실빌드 대상 7항목) · keep-watching/tools/_lore.mjs
  • 완전 암전 커밋 · git:0c32355
  • 플레이테스트 QA 수정 · git:8e9b7c9
  • 탈출 체인 — 전표 → 번호 상자 → 다이얼 → 열쇠 · git:8fda054
  • 엔들리스 곡선 6~26방 전 라운드 실측 · keep-watching/tools/_live16.mjs
  • 주장을 테스트로 — 등 돌린 채 6초는 열리면 안 된다 · keep-watching/tools/_dialwatch.mjs
  • 폰 가로 750×342 실제 터치로 체인 완주 · keep-watching/tools/_mobilechain.mjs
  • 배포 스크립트 — 다시 링크하고 라이브 번들 해시 대조 · keep-watching/tools/deploy.sh