제기 띄우기
제기가 떨어지기 전에 눌러 계속 띄운다 — 바람이 술을 흔들어 방향이 틀어진다.
프롬프트를 복사해 claude.ai 에 붙여 넣으면 바로 실행됩니다 · Opus 5.5 · Effort High 기준
- 권장 모델
- Claude Opus 5.5 · Effort High
- 화면비
- 9:16 (세로)
- 조작
- 제기 아래쪽 누르기 · 누른 자리에 따라 튀는 각도가 달라진다
- 결과물
- HTML 파일 하나 · 따로 설치할 것 없음
프롬프트
Claude 가 읽는 제작 지시문입니다. 맨 위 [바꿀 곳]의 값을 바꿔 붙여 넣어도 됩니다.
세로 9:16 브라우저 미니게임 「제기 띄우기」를 HTML 파일 하나로 만들어 줘.
핵심 아이디어: 늦은 오후 골목, 토담 앞에서 엽전에 한지 술을 단 제기 하나가 떨어진다. 사람은 그리지 않는다 — 누르는 순간 붓으로 그은 발 궤적, 엽전의 찌그러짐, 흙먼지, 「탁」 소리가 「찼다」는 느낌을 대신한다. 제기가 땅 가까이(발 자리)에 왔을 때 차면 올라가고, 낮게 받아 찰수록·연달아 찰수록 더 높이 뜬다. 높이 뜨면 화면이 따라 올라가 담장 기와 너머의 마을 지붕, 감나무, 먹으로 번진 세 겹 산, 노을 해가 드러난다. 한지 술 14가닥은 저마다 점 8개짜리 베를레 줄이라 바람과 차기에 따라 나풀거리고, 바람은 시간이 갈수록 세져 제기를 옆으로 흘린다.
시그니처 순간: 구경 모드 시작 4.9초 무렵(두 번째 차기의 꼭대기), 제기가 높이 4.0S 에 닿는다. 화면이 2.95S 만큼 올라가 담장 기와 끝은 화면 아래로 사라지고, 화면 아래 22% 에 기와지붕들, 가운데에 먹 산 세 겹, 오른쪽에 감나무, 위쪽에 노을 해가 펼쳐진 가운데 제기(술 포함 높이 약 0.3S)가 화면 위에서 34% 지점에 0.3초쯤 멈춘 듯 떠서, 위로 부채처럼 펴진 술이 바람에 한쪽으로 쓸린다. 플레이에서도 연달아 10번을 낮게 받아 차면 같은 장면이 나온다.
[바꿀 곳]
CONFIG 맨 위에 아래 값을 이름 붙여 둔다. 이 값만 바꿔도 작품이 망가지지 않게 짠다.
- TITLE: '제기 띄우기', START_TEXT: '눌러서 시작 · Space / 탭', RETRY_TEXT: '눌러서 다시'
- GRAVITY: 3.2 (S/초²), FALL_SPEED_MAX: 1.6 (S/초, 떨어질 때 최대 속도 — 술이 공기를 잡는 효과)
- BASE_APEX: 1.2 (S), STREAK_STEP: 0.12 (S), STREAK_CAP: 10, DEPTH_BONUS: 1.6 (S)
- ZONE_TOP: 0.30, ZONE_BOTTOM: 0.02 (S, 발 자리 높이), FOOT_MIN_X: 0.10, FOOT_MAX_X: 0.90 (판 너비 비율), GRACE: 0.12 (초)
- SIDE_KICK_VX: 0.45 (S/초), WIND_START: 0.3, WIND_MAX: 1.8 (S/초)
- STRANDS: 14, STRAND_POINTS: 8, STRAND_SEG: 0.03 (S)
- 색: SKY_TOP '#DCD9CF', SKY_LOW '#EBD9C0', WALL '#C2A985', STONE_A '#A39784', STONE_B '#8F8574', TILE '#55534F', TILE_DARK '#3E3C39', GROUND '#B5A283', GROUND_WORN '#9E8B6D', INK '#2B2926', HANJI '#EFE6D2', BRASS '#A88A4E', SUN '#E39B5B', PERSIMMON '#D9772F', MT_FAR '#A9A79F', MT_MID '#8E8C85', MT_NEAR '#6F6D68', ROOF '#5E5B55'
- 술 오방색: OBANG = { 백 '#F2EDE2', 청 '#3F6474', 황 '#D4A63A', 적 '#B8412E', 흑 '#2E2C2A' }
[크기]
- 게임판은 9:16 세로. 판 너비 S = min(창 너비, 창 높이 × 9/16), 판 높이 = S × 16/9 ≈ 1.78S. 판은 창 가운데. 이 글의 모든 길이는 S 의 배수다.
- 판 바깥(데스크톱의 좌우, 납작한 창의 위아래)에도 같은 장면을 이어 그린다: 담장·땅·하늘은 옆으로 계속되고, 판 좌우 끝에는 골목 옆 담의 안쪽 면(폭 0.06S, 담 색보다 18% 어두움)이 세로로 서 있어 제기가 튕기는 벽이 눈에 보인다. 빈 띠·검은 띠를 두지 않는다.
- 세계 좌표: x 는 판 왼쪽 0 ~ 오른쪽 1 (S 단위), y 는 땅 0 에서 위로 (S 단위). 땅선은 판 바닥에서 0.12S 위. 화면의 높이(판 바닥에서 위로) = 0.12S + (y − p × camY) × S. p 는 겹의 원근 계수(가까운 것 1, 먼 것일수록 작다), camY 는 화면이 올라간 양.
- camY 목표 = clamp(제기 y − 0.85, 0, 2.95). camY 는 목표로 지수적으로 다가간다(초당 비율 5). 처음 화면(camY ≈ 0.25)에서 판 꼭대기는 y ≈ 1.91 이다.
- 제기: 엽전 몸통 너비 0.10S · 두께 0.035S, 술 길이 7마디 × 0.03S = 0.21S, 술이 펴진 제기 전체 높이 약 0.3S. 판 너비의 30% — 작게 그리지 않는다.
- 점수 숫자: 판 위에서 9% 지점, 글자 높이 0.16S. 소리 단추: 판 오른쪽 위, 여백 0.03S, 한 변 0.085S.
- 붓 발 궤적: 반지름 0.16S, 굵기 0.035S. 흙먼지 한 덩이 반지름 0.02~0.04S.
[장면]
늦은 오후, 해가 낮게 깔린 골목. 바탕은 모두 채도 낮은 흙·한지·먹 색이고, 강조색은 노을 해·감의 주황(SUN·PERSIMMON)과 술의 오방색 둘뿐이다. 아래에서 위로 겹을 쌓는다. 정적인 겹은 창 크기가 바뀔 때 화면 밖 캔버스에 미리 그려 두고, 매 프레임은 위치만 옮겨 붙인다.
1. 하늘(p = 0): 위 SKY_TOP → 아래 SKY_LOW 세로 그라데이션. 한지 섬유 결 — 길이 0.02~0.06S, 굵기 1px, 알파 0.05 의 짧은 곡선 400개를 시드 난수로 흩어 미리 그려 둔다.
2. 노을 해(p = 0.05): 중심 x 0.28, y 1.55, 반지름 0.07S, SUN 색 원. 가장자리는 먹 번짐처럼 — 반지름 1.0·1.15·1.3 배 원을 알파 0.9·0.15·0.06 으로 겹친다(발광 효과 아님, 단순 겹침). 처음 화면부터 담 위 하늘에 보인다.
3. 먹 산 세 겹: 먼 산 p 0.15 · 능선 꼭대기 y 1.55 · MT_FAR, 가운데 p 0.22 · 1.45 · MT_MID, 가까운 산 p 0.30 · 1.35 · MT_NEAR. 셋 다 밑동 y 1.0, 판 너비의 1.6배 폭(판 밖까지 이어짐). 능선은 시드 난수 꼭짓점 9~12개를 부드러운 곡선으로 잇고, 위 가장자리는 진하고 아래로 갈수록 알파가 1 → 0.55 로 옅어지는 먹 농담 그라데이션. 산마다 아래쪽에 SKY_LOW 알파 0.35 의 안개 띠(높이 0.12S)를 깐다.
4. 마을 기와지붕(p 0.5): 밑동 y 1.2 ~ 용마루 y 1.75. 지붕 6~7채가 겹치며 높이가 조금씩 다르다. 지붕마다 처마가 양 끝에서 살짝 들린 곡선(비대칭 — 왼쪽 끝이 오른쪽보다 0.01S 더 들림), ROOF 색, 기와 골 세로줄을 0.012S 간격 TILE_DARK 알파 0.4 로. 처음 화면에선 용마루 끝만 담 위로 삐죽 보인다.
5. 감나무(p 0.7): x 0.80 에 줄기 밑 y 1.2, 수관 y 1.7~2.9. 먹 붓질 같은 가지 — 굵기 0.02S 에서 끝 0.004S 로 가늘어지는 곡선 가지 14개, 잎은 거의 다 떨어지고 PERSIMMON 감 11개(반지름 0.018S, 꼭지는 INK 점). 높이 떴을 때 화면 오른쪽 아래 절반을 차지한다.
6. 담장(p 1): y 0 ~ 1.30 토담(WALL). 박힌 둥근 돌 — STONE_A·STONE_B 타원(긴 지름 0.05~0.09S)을 가로 줄 6줄로 엇갈려 박고, 돌 위쪽 가장자리는 1px 밝게, 아래쪽 그림자는 알파 0.2. 위 y 1.30 ~ 1.45 는 기와 지붕 덮개(TILE): 수키와 줄을 0.035S 간격으로, 덮개 아래 그림자 띠 높이 0.03S 알파 0.3. 덮개 위 마른 풀 다섯 포기(높이 0.04S)가 바람 세기만큼 눕는다. 담 면에 감나무 가지 그림자를 TILE_DARK 알파 0.08 로 비스듬히(해가 왼쪽 위).
7. 땅(p 1): y < 0 은 다진 흙(GROUND), 담 밑 0.04S 는 조금 어둡게. x 0.10~0.90 사이에 발에 닳은 자리(GROUND_WORN, 가장자리가 흐린 넓은 타원, 알파 0.5) — 발이 닿는 폭을 말없이 보여 준다. 잔돌 30개.
8. 제기: 엽전(BRASS 타원, 너비 0.10S·두께 0.035S, 윗면 가운데 네모 구멍 INK)을 한지(HANJI)로 감싼 몸통. 한지는 엽전 위로 오므려 묶인 주름 다섯 줄. 기울기는 수평 속도에 따라 ±18°.
술 14가닥은 몸통 윗부분 반지름 0.025S 고리에서 나온다. 오방 배치 규칙으로 색을 정한다 — 화면 오른쪽(동) 3가닥 청, 왼쪽(서) 3가닥 백, 앞쪽(남) 3가닥 적, 뒤쪽(북) 3가닥 흑(뒤쪽은 몸통보다 먼저 그려 뒤에 깔린다), 가운데 2가닥 황. 가닥마다 뿌리 굵기 0.018S → 끝 0.008S 로 가늘어지는 띠(점들을 잇는 사다리꼴 띠), 가닥 가운데에 한지 섬유 한 줄(알파 0.25, 1px). 끝 0.01S 는 찢긴 종이처럼 작은 톱니.
9. 제기 그림자: 땅선 위 제기 x 에 타원, 너비 0.12S × (1 − min(y/2, 0.7)), 알파 0.30. 제기가 발 자리(y ≤ ZONE_TOP)에 들면 알파 0.55 로 짙어지고 INK 붓 고리(반지름 0.07S, 굵기 0.006S)가 둘린다 — 「지금 차라」는 신호. 예약(아래 조작 설명)이 걸려 있으면 그림자 옆에 짧은 붓 점 하나.
10. 차는 느낌(사람 없이): 차는 순간
- 히트스톱 0.045초(모든 움직임 정지),
- 엽전 찌그러짐: 가로 1.2배·세로 0.7배 → 0.12초에 easeOutBack 으로 복귀,
- 붓 발 궤적: 제기 아래, 차는 방향의 반대편에서 시작해 제기 밑을 쓸고 올라가는 초승달 붓획. 중심은 제기에서 (−방향 × 0.06S, −0.12S), 반지름 0.16S, 쓸어 올리는 각도 110°, 굵기 0.035S 에서 끝으로 가늘어짐, INK 알파 0.55. 번짐은 0.004S 씩 어긋난 획 세 겹(알파 0.55·0.2·0.08). 0.22초에 사라진다. 낮게(깊게) 찰수록 반지름 1.25배·알파 0.75 까지.
- 흙먼지: 땅선에서 4~6덩이, 0.05S 떠오르며 0.4초에 사라짐(동시에 최대 12덩이).
- 판 전체가 0.008S(깊은 차기는 0.015S) 아래로 밀렸다 0.08초에 돌아옴.
- 점수 숫자가 1.25배 → 1배로 0.15초.
헛발(찰 수 없을 때 누름)은 같은 자리에 회색 얇은 궤적(알파 0.25, 굵기 절반)만 그리고 제기는 그대로다.
11. 「담 너머」: 제기가 y 1.6 을 넘으면 제기 옆에 「+2 담 너머」를 INK 글자(높이 0.045S)로 0.7초 띄우고 사라지게 한다.
[조작]
누르는 것 하나가 「찬다」이다. 키보드와 터치는 같은 함수 tryKick(방향) 을 부른다.
- 키보드: Space · ArrowUp · KeyW · Enter = 곧게 차기. ArrowLeft · KeyA = 왼쪽으로 차기, ArrowRight · KeyD = 오른쪽으로 차기. M = 소리 끄기/켜기(M 은 판을 시작하지 않는다). e.repeat 인 keydown 은 무시한다.
- 터치·마우스: pointerdown 한 번 = 차기. 화면 어디를 눌러도 되지만, 친 위치가 제기 x 에 대해 어디냐로 각도가 정해진다 — 「제기 아래쪽을 친다」고 생각하면 된다. dx = 탭 x − 제기 x (S 단위). |dx| ≤ 0.12 이면 곧게 차기. 그보다 바깥이면 반대쪽으로 튄다(제기 왼쪽 아래를 치면 오른쪽 위로): 수평 속도 = −sign(dx) × SIDE_KICK_VX × min(1, (|dx| − 0.12) / 0.2). 소리 단추 영역의 탭은 소리만 바꾸고 차지 않는다.
- 폰 세로 화면에서 엄지 하나로 된다 — 제기가 찰 수 있을 때는 늘 화면 아래쪽(땅선 근처)에 있으니 엄지가 닿는 곳을 치게 된다.
tryKick 판정(입력이 들어온 그 프레임에 바로 처리하고 화면에 반영한다):
0. state 가 'demo' 나 'over' 면 → 새 판 'play' 를 열고 끝(그 누름은 차기가 아니다). 끝나는 연출 0.7초 동안의 누름은 무시.
1. 제기가 발 자리(ZONE_BOTTOM ≤ y ≤ ZONE_TOP)에 있거나, 땅에 닿은 뒤 GRACE(0.12초) 안이고, x 가 FOOT_MIN_X~FOOT_MAX_X 안이면 → 바로 찬다. 깊이 depth = clamp((ZONE_TOP − y) / (ZONE_TOP − ZONE_BOTTOM), 0, 1). 땅에 닿은 뒤 살린 차기는 depth 1 이고 「아슬!」 글자를 0.5초.
2. 제기가 발 자리보다 위에서 떨어지는 중(vy < 0)이면 → 예약. 제기가 ZONE_TOP 을 지나는 순간 depth 0 으로 저절로 찬다. 예약 중 다시 누르면 방향만 바꾼다.
3. 올라가는 중이면 → 헛발. 0.3초 동안 누름을 받지 않는다(헛발 궤적·바람 소리만).
4. 발 자리에 있는데 x 가 발 폭 밖이면 → 헛발(궤적이 제기에 못 미친다).
차기의 결과:
- 목표 꼭대기 apex = BASE_APEX + min(streak, STREAK_CAP) × STREAK_STEP + depth × DEPTH_BONUS. streak 는 이번 차기 전까지 연달아 찬 횟수(첫 차기는 0).
- 위 속도 vy = sqrt(2 × GRAVITY × (apex − y)). 올라갈 때는 공기 저항 없이 GRAVITY 만 받으므로 꼭대기가 정확히 apex 가 된다.
- 수평 속도 vx: 곧게 차기 = clamp((0.5 − x) × 0.8, −0.4, 0.4)(가운데로 살짝 돌아옴), 옆 차기 = ±SIDE_KICK_VX(터치는 위 공식). 여기에 지금 바람 w × 0.3 을 더한다 — 술이 바람을 받아 방향이 틀어진다.
- 검산: depth 0·streak 0 이면 apex 1.2, vy 2.4, 꼭대기까지 0.75초. depth 1·streak 10 이면 apex 1.2 + 1.2 + 1.6 = 4.0.
[규칙]
- 움직임: 제기는 늘 GRAVITY 로 떨어지고, 떨어지는 속도는 FALL_SPEED_MAX(1.6 S/초)에서 멈춘다(술이 공기를 잡아 천천히 떨어짐). 수평으로는 술이 공기를 잡아 바람을 따라간다: ax = 1.2 × (0.3 × w′ − vx), w′ 는 지금 바람 w(담 높이 1.45 아래에서는 담이 막아 0.6 × w). 그래서 차기의 수평 속도는 초당 비율 1.2 로 줄고, 제기는 0.3 × w′ 속도로 흘러간다 — 높이 띄울수록 바람을 오래 받는다(높이와 위험의 맞바꿈). 판 좌우 x 0.06·0.94 에서 수평 속도 −0.5배로 튕긴다.
- 검산: 기본 차기(ZONE_TOP 에서 depth 0) 체공 = 올라감 0.75초 + 떨어짐(0.5초에 0.4S 를 떨어져 최대 속도에 닿고, 남은 0.5S 를 1.6 으로 0.3125초) = 약 1.56초. 최대 속도일 때 발 자리(0.28S)를 지나는 시간 0.175초 + 땅 유예 0.12초.
- 점수: 한 번 찰 때마다 +1. 제기가 y 1.6(담 기와 위)을 넘으면 그 차기에 +2 「담 너머」. streak 는 차기마다 +1, 판이 끝나면 0.
- 판이 끝남: 제기가 땅(y 0)에 닿으면 그 자리에 멈추고(vy 0) GRACE 를 센다. 그 안에 차지 못하면 끝. 엽전이 0.03S 한 번 튀고, 흙먼지 8덩이, 술이 땅에 눕는다. 이 연출 0.7초 동안 state 는 'play', 끝나면 'over'. 끝난 이유를 over 화면에 쓴다 — 기본은 「땅에 떨어졌다」, 마지막 순간 x 가 발 폭 밖이었다면 「너무 옆으로 흘렀다」.
- 아무것도 누르지 않으면 판 시작 후 약 0.94초에 제기가 땅에 닿아 약 1.8초에 'over' 가 된다 — 40초보다 훨씬 짧다.
- 새 판 시작: 제기는 x 0.5, y 1.1 에서 vy 0 으로 떨어지기 시작하고(땅 닿기까지 약 0.94초), 시작한 누름은 차기로 치지 않는다. 술은 위로 부채꼴(펼침 70°)로 편 자세에서 시작.
- 난이도(판 시작부터 잰 시간 t):
- 바람 w(t) = amp × (0.6 sin(2πt/7.3 + φ1) + 0.4 sin(2πt/3.1 + φ2)) + 돌풍. φ 는 판마다 시드 난수.
- amp: 처음 20초는 WIND_START 0.3 S/초(담 아래에서 제기가 흐르는 속도 0.3 × 0.6 × 0.3 ≈ 0.05 S/초 — 1.5초 체공에 0.1S 쯤 흐를 뿐, 곧게 차기의 가운데 돌림이 이긴다 → 처음 20초는 거의 지지 않는다). 20초부터 초당 0.024 씩 고르게 늘어(10초에 +0.24) 82.5초에 WIND_MAX 1.8 에서 멈춘다: amp = min(1.8, 0.3 + 0.024 × max(0, t − 20)).
- 30초부터 돌풍: 6~9초마다(시드 난수) 1.2초 동안 지금 바람 방향으로 +0.6.
- 40초부터 떨어지는 최대 속도가 초당 0.01 씩(10초에 +0.1) 늘어 100초에 2.2 에서 멈춘다: min(2.2, 1.6 + 0.01 × max(0, t − 40))(발 자리를 지나는 시간이 0.175 → 0.127초로 줄어든다).
- 기대: 처음 해 보는 사람의 첫 판은 30~90초, 점수 20~60.
- HUD: 'play' 에선 판 위에서 9% 에 점수(INK, 0.16S, 굵게), 그 아래 0.05S 글자로 「연속 N」. 'over' 에선 판 가운데 너비 0.8S 의 한지 패널(HANJI, 알파 0.92, 섬유 결, 모서리 둥글기 0.02S, 테두리 INK 알파 0.3 1px)에 큰 점수(0.22S) · 「N번 찼다」 · 「최고 M」 · 「담 너머 K번」 · 끝난 이유 · RETRY_TEXT(알파가 sin 으로 0.5~1 사이를 오감). 최고 점수는 메모리에만.
[구경 모드]
열리자마자 state 'demo'. 컴퓨터가 같은 물리·같은 doKick 으로 차는데, 차는 높이를 정해 둔 순서로 고른다. 이것이 미리보기의 첫인상이다.
- 시작: 제기 x 0.5, y 1.1, vy 0, 술은 위로 부채꼴. camY 는 처음부터 목표값(0.25)에 둔다. 첫 프레임에 담장·기와·담 위 지붕 끝·먹 산·노을 해·떨어지는 제기가 모두 보인다.
- 컴퓨터는 제기가 떨어지다 y ≤ 0.16(depth 0.5) 이 된 첫 스텝에 찬다. 목표 꼭대기를 순서 [2.0, 4.0, 1.4, 1.7, 1.3] 에서 차례로 골라 되풀이하고(이때 apex 공식 대신 이 값을 쓴다), 방향은 x < 0.35 면 오른쪽, x > 0.65 면 왼쪽, 그 밖엔 곧게. 컴퓨터의 차기는 발 폭을 따지지 않고 늘 성공하며, 붓 궤적·흙먼지·찌그러짐·히트스톱도 플레이와 똑같이 나온다.
- 바람 amp 는 0.6 고정(술이 눈에 띄게 쓸리도록), 돌풍 없음.
- 검산된 시간표: 첫 차기 0.84초(y 1.1 에서 0.5초에 0.4S, 남은 0.54S 를 1.6 으로 0.34초) → 꼭대기 2.0, 체공 2.47초 → 두 번째 차기 3.31초 → 꼭대기 4.0 에 4.86초(올라감 sqrt(2 × 3.84 / 3.2) = 1.55초) → 세 번째 차기 7.51초. 차기마다 히트스톱 0.045초가 더해지므로 실제 꼭대기 시각은 약 4.95초. 첫 1초 안에 첫 차기(붓 궤적·흙먼지)가, 5초 안에 시그니처 순간이 나온다. 이 순서는 약 12.6초마다 되풀이된다.
- 판 위 14% 에 TITLE(INK, 글자 높이 0.09S, 글자 간격 넓게), 판 위 88% 에 START_TEXT(INK, 0.045S, 알파가 sin 으로 0.45~1). 구경 모드에선 점수를 띄우지 않는다.
- 구경 모드는 끝나지 않는다. 첫 입력(M 과 소리 단추 제외)이면 새 판 'play' 를 연다 — 그 입력은 차기로 치지 않는다.
[소리]
WebAudio 합성음만. AudioContext 는 첫 입력 때 만들거나 resume 한다(첫 입력 전엔 소리 없음). 주 음량 0.5. 소리 단추와 M 키로 끄고 켠다(끄면 주 음량 0, 단추에 빗금).
- 차기 「탁」: 흰 소음 30ms 를 bandpass 1800Hz(Q 1.2)로, 동시에 사인 220 → 110Hz 60ms. 깊은 차기일수록 사인 시작을 180Hz 까지 낮추고 음량 1.3배.
- 엽전 「짤랑」: 차기 0.01초 뒤 삼각파 2400Hz·3100Hz 두 음, 80ms 감쇠, 음량 0.12.
- 헛발: 소음을 bandpass 600 → 1400Hz 로 150ms 쓸어 올리는 「휙」, 음량 0.15.
- 담 너머: 평조 음계(라·도·레를 고른 사인, 예: 440·523·587Hz) 세 음을 0.07초 간격, 각 0.25초 감쇠, 음량 0.1.
- 바람: 늘 깔리는 흰 소음 lowpass 400Hz, 음량 = 0.02 + 0.05 × (|w| / WIND_MAX).
- 떨어짐: 사인 90Hz 120ms 「툭」 + 소음 lowpass 900Hz 200ms 「사르륵」.
[구현]
- HTML 파일 하나로 만든다. 바깥 파일·글꼴·라이브러리·네트워크 요청을 쓰지 않는다(미리보기 칸에서 막힌다).
- 첫 <script> 맨 앞에 CONFIG 객체를 두고 [바꿀 곳]의 값과 이 글에 나온 숫자(속도·크기·색·시간)를 이름 붙여 모은다.
- 게임 진행은 requestAnimationFrame 의 타임스탬프로 고정 시간 간격(1/120초) 갱신을 돌린다. 한 프레임에 밀린 시간은 0.1초까지만 따라잡는다. setTimeout·setInterval·CSS 애니메이션으로 움직이지 않는다.
- 난수는 시드 고정 난수(mulberry32 등)만 쓰고, 판마다 시드를 하나씩 늘린다. Math.random 은 쓰지 않는다.
- 캔버스는 늘 창 전체 크기다. 창 크기가 바뀌면 다시 맞추고, 게임판 비율이 정해진 게임은 판을 가운데 두되 남는 곳에도 배경을 그린다(빈 띠를 두지 않는다). 스크롤이 생기지 않게 한다. devicePixelRatio 는 2 까지만 선명도에 반영한다.
- 키보드와 터치를 둘 다 받는다. 키 입력은 window 의 keydown·keyup 으로 받아, 화면을 먼저 누르지 않아도 첫 키가 먹게 한다. 짧게 눌렀다 뗀 키도 놓치지 않게 keydown 때 처리하거나 기록해 둔다. 터치는 페이지 스크롤·확대가 일어나지 않게 막는다(touch-action: none).
- 점수·상태를 window.__game = { state, score } 에 두고 매 프레임 갱신한다. state 는 'demo'(구경 모드)·'play'·'over' 셋 중 하나, score 는 숫자다.
- 열리자마자 'demo' — 컴퓨터가 스스로 플레이하는 구경 모드와 「눌러서 시작」 안내. 첫 키 입력이나 탭이면 새 판 'play'. 판이 끝나면 'over' 화면, 거기서 키 입력이나 탭이면 새 판. 'over' 화면은 뜨자마자 키 입력·탭을 받는다(입력을 막는 대기 시간을 두지 않는다). 끝나는 연출이 있으면 그동안은 state 를 'play' 로 두고, 연출이 끝나 입력을 받을 수 있게 된 때 'over' 로 바꾼다.
- 플레이 중 아무 입력이 없으면 늦어도 40초 안에 판이 끝난다.
- 브라우저 저장소(localStorage·sessionStorage·IndexedDB·쿠키)를 쓰지 않는다 — 미리보기 칸에서 막혀 오류가 난다. 최고 점수는 메모리에만 둔다.
- 소리는 WebAudio 로 합성한 소리만, 첫 입력 뒤에만 낸다. 화면 구석에 소리 끄기 단추(M 키도)를 둔다.
- 탭이 숨으면 멈추고 돌아오면 이어서 한다.
- 글자는 시스템 글꼴만 쓴다: 'Pretendard', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', sans-serif.
- 모든 함수·상수를 먼저 정의하고, 시작(초기화와 첫 requestAnimationFrame)은 스크립트 맨 끝에서 한 번만 부른다.
- 콘솔에 오류가 나지 않게 한다. 스스로 대조하는 표는 console.table, 어긋남은 console.warn 으로만 낸다.
이 작품만의 구현:
- 구조: CONFIG → 난수(mulberry32) → 상태(game, jegi, strands, camera, fx) → 물리 step(dt) → tryKick(dir, source, tapX) / doKick(depth, vx, apexOverride) → 구경 모드 AI → 그리기(drawSky, drawLayers, drawWall, drawGround, drawShadow, drawStrands, drawJegi, drawFx, drawHud) → 소리 → 입력 → 자기 대조 selfCheck() → 맨 끝에서 init() 과 첫 requestAnimationFrame.
- 입력은 keydown·pointerdown 에서 tryKick 을 바로 부른다(그 순간의 제기 상태로 판정). 같은 프레임의 그리기에 결과가 보인다. pointerdown 에서 preventDefault, canvas 에 touch-action: none, body 에 overflow: hidden · margin 0.
- 히트스톱은 고정 간격 step 안에서 남은 정지 시간을 줄이며 물리를 건너뛰는 식으로 만든다(시간 누적은 그대로).
- 술 베를레(가볍게 유지 — 점 14 × 8 = 112개, 이 수를 늘리지 않는다):
- 뿌리 점(0번)은 매 step 제기 몸통 위 고리 자리에 고정(기울기 반영). 나머지 7점은 위치·이전 위치만 가진다.
- 갱신: 속도 v = (p − prev), 새 p = p + v × 0.995 + a × dt². a = (0, −GRAVITY) + 7 × (공기 속도 − v/dt). 공기 속도 = (w × (1 + 0.3 sin(7t + i)), 0) — i 는 가닥 번호, 가닥마다 조금씩 다르게 나풀거린다.
- 길이 맞추기: 마디 길이 STRAND_SEG 로 3번 반복(뿌리 쪽부터). 반복은 3번을 넘기지 않는다.
- 이 저항 계수면 술 혼자 떨어지는 속도는 GRAVITY / 7 ≈ 0.46 S/초라, 1.6 으로 떨어지는 제기 위로 술이 펴져 날리고, 차서 솟구칠 때는 아래로 처졌다가 꼭대기에서 부채처럼 퍼진다. 술은 제기의 움직임에 힘을 되돌려 주지 않는다(한 방향).
- 그리기: 가닥마다 점들을 잇는 사다리꼴 띠 한 번의 fill(경로 하나) + 섬유 한 줄 stroke. 가닥당 그리기 호출 2번 이내.
- 정적 겹(하늘·산 세 겹·지붕·감나무·담장·땅)은 resize 때 화면 밖 캔버스 각각에 미리 그리고, 매 프레임 겹마다 drawImage 한 번으로 y 만 옮긴다. 담장 캔버스는 판 바깥 좌우까지 포함해 창 너비 전체.
- 카메라: camY 는 step 마다 camY += (목표 − camY) × (1 − exp(−5 × dt)). 시그니처 순간에 camY 가 2.6 이상이어야 한다.
- 프레임 시간 목표: 폰에서 프레임당 그리기 4ms 이내. shadowBlur 는 쓰지 않는다.
- 결과 HTML 은 60KB 안쪽으로 짧게 — 같은 그리기를 함수로 묶는다.
합격 기준(기본값에서) — init 때 selfCheck() 가 화면과 따로 물리 step 만 돌려 재고, console.table 로 기대·잰 값·맞음 여부를 보이며, 어긋나면 console.warn:
| 항목 | 기대 | 허용 |
|---|---|---|
| 기본 차기(y 0.30, depth 0, streak 0)의 꼭대기 | 1.20 S | ±0.03 |
| depth 1 · streak 10 차기의 꼭대기 | 4.00 S | ±0.05 |
| 기본 차기 체공(0.30 → 0.30) | 1.56 초 | ±0.05 |
| 최대 속도로 발 자리를 지나는 시간 | 0.175 초 | ±0.02 |
| 구경 모드 두 번째 차기 꼭대기 시각(히트스톱 포함) | 4.95 초 | ±0.15 |
| 그때의 camY | 2.6 이상 | — |
| 무입력 판이 'over' 가 되는 시각 | 약 1.8 초 | 40 초 미만 |
| 술 점 수 | 112 | 같아야 함 |
| t = 90초의 바람 amp | 1.8 | ±0.01 |
| 판 너비 대비 제기(술 포함) 높이 | 약 0.3 | 0.25 이상 |
selfCheck 는 게임 상태를 건드리지 않게 복사본으로 돌린다. 어긋나도 게임은 그대로 돌아가야 한다(경고만).
[하지 말 것]
- 사람·발·다리·버선·캐릭터를 그리지 않는다 — 플랫 캐릭터는 장면을 싸구려로 만들고 발 동작을 맞추느라 손맛이 늦어진다. 차는 느낌은 붓 궤적·찌그러짐·흙먼지·흔들림·소리로만 낸다.
- 술을 직선 막대·고정 그림·스프라이트로 그리지 않는다 — 이 작품의 볼거리가 사라진다. 반대로 점 수·반복 수를 늘리지 않는다 — 폰에서 느려진다.
- 장식 입자를 남발하지 않는다(꽃잎·낙엽 비·반짝이·불티 없음, 흙먼지는 동시에 12덩이까지) — 제기와 술이 묻힌다.
- 발광·네온·shadowBlur·렌즈 플레어·햇살 광선을 넣지 않는다 — 먹과 한지 화면이 탁해지고 그리기가 무거워진다.
- 화면 모서리에 라벨·조작 설명·FPS·디버그 글자를 두지 않는다(소리 단추만) — 장면을 가린다. 조작 안내는 구경 모드의 START_TEXT 한 줄이면 된다.
- 넓은 바탕에 남청색·고채도 색을 쓰지 않는다 — 노을 골목의 흙빛이 깨진다. 강조색은 해·감의 주황과 술의 오방색뿐.
- 태극 문양·한복·탈·부채춤 같은 상투 그림을 넣지 않는다 — 한국적인 것은 토담·기와·한지·먹 농담·오방 배치로 이미 있다. 새 떼·구름 떼도 넣지 않는다.
- 탭한 곳으로 제기를 옮기거나 끌어 올리지 않는다 — 차기만 제기를 움직인다.
- 시작 단추·메뉴·설명 화면을 따로 두지 않는다 — 첫 화면은 구경 모드다. 'over' 뒤에 입력을 막는 대기를 두지 않는다.
- 바깥 글꼴·이미지·소리 파일을 쓰지 않는다 — 미리보기 칸에서 막힌다.
- 이름 있는 상업 게임의 이름·캐릭터·그림을 쓰지 않는다.
- 공식과 다른 값을 「느낌상」 고치지 않는다 — 숫자를 바꾸려면 CONFIG 에서 바꾸고, 합격 기준이 어긋나면 console.warn 으로 알린다.
사용 방법
필요한 것: claude.ai 계정 — 무료 계정에서도 Claude Opus 5.5 를 고를 수 있습니다
- claude.ai 에서 새 대화를 엽니다.
- 모델 메뉴에서 Claude Opus 5.5 를 고르고, 같은 메뉴의 Effort(답변 깊이)를 High 로 둡니다.
- 복사한 프롬프트를 붙여 넣고 전송합니다. 결과로 나온 HTML 파일을 브라우저로 열면 바로 해 볼 수 있습니다.
추가 요청 예시
결과가 나온 뒤 같은 대화에서 이어서 요청하면 해당 부분만 수정됩니다.
- 몇 번 찼는지 화면 위에 크게 세어 줘
- 골목을 눈 내리는 설날 풍경으로 바꿔 줘
- 가끔 바람이 세게 부는 구간을 넣어 줘
미니게임